/* DESIGN
   ------
   * This stylesheet is the single source of truth for a Refactoring UI case study
   * (Adam Wathan and Steve Schoger). Built as a design system reference: every value
   * is intentional, documented, and traceable back to a core principle from the book.
   * Full case study at simoneamico.com.
   *
   * Philosophy:
   * - Context-driven approach (Desktop-first): Back when I built the Telephone Number Validator, 
   *   I strictly followed the "mobile-first" dogma, but I wrote a note questioning it: shouldn't 
   *   we build for the primary device our users actually use? This project is the materialization 
   *   of that insight. Since this showcase is a UI study built as a functional developer tool 
   *   for side-by-side comparisons, the audience will view it on desktop screens. Therefore, 
   *   I consciously built it desktop-first. Mobile is handled gracefully as a fallback, proving 
   *   that "audience-first" takes precedence over rigid rules
   * - Token-driven architecture: I chose CSS custom properties for every value so the 
   *   system is trivially themeable. Changing one token propagates everywhere, eliminating
   *   the need to manually update hardcoded values across the file
   * - Tinted grays: I deliberately avoided pure, neutral grays (0% saturation).
   *   Instead, every "gray" carries a hue of 31° to harmonize with the warm background (30°).
   *   This prevents neutral elements from feeling disconnected, ensuring they blend naturally 
   *   into the surrounding color palette
   * - Light-based elevation: The shadow system uses two layers for each component.
   *   The first shadow is small and dark, like a direct light shining from above.
   *   The second shadow is large and blurred, like natural light spreading in a room.
   *   To create a realistic sense of height, the vertical shadow distance (y-offset) 
   *   doubles at each new elevation level
   * - Physical interactive states: UI components behave like real physical objects. 
   *   For example, buttons visibly press down into the page when clicked (:active state).
   *   Inputs look like holes cut into the page, and when you click them (focus), they 
   *   look even deeper inside, rather than just adding a colored ring around the border
   * - Asymmetric transitions: I used a timing trick to make clicking feel fast and 
   *   responsive. When you press a button, it goes down almost instantly (60ms).
   *   But when you release the mouse, it comes back up a bit slower (160ms).
   *   This difference in speed feels much more natural than a uniform animation
   *
   * Stylesheet order:
   *    1. UNIVERSAL (Design Tokens)    — the single source of truth for every value
   *    2. BASE & RESET                 — body, fieldset
   *    3. FLEX UTILITIES               — reusable layout helpers
   *    4. PAGE LAYOUT                  — main container
   *    5. TYPOGRAPHY                   — section titles, component labels
   *    6. STYLESHEET PREVIEW           — css preview block, description text
   *    7. COMPONENT: BUTTONS           — primary, secondary, tertiary, copy
   *    8. COMPONENT: INPUTS            — text input, checkbox, radio
   *    9. COMPONENT: ICON FRAME        — ghost container for size normalization
   *   10. COMPONENT: CARDS             — five shadow elevation levels
   *   11. COMPONENT: EMPTY STATE       — title, subtitle
   *   12. INTERACTIVE STATES           — hover, active, focus-visible
   *   13. RESPONSIVE                   — mobile breakpoint, landscape, carousel
   *   14. MOTION PREFERENCE            — respects prefers-reduced-motion
*/



/* UNIVERSAL (Design Tokens) */

:root {

  /* COLORS - BACKGROUND */
  --bg-main:            hsl(30, 26%, 84%);          /* Warm sand — the page canvas */

  /* COLORS - TEXT */
  --text-title:         hsla(31, 12%, 14%, 0.60);   /* Section headings — warm hue, 60% opacity */
  --text-label:         hsla(31, 12%, 14%, 0.40);   /* Component labels */

  /* COLORS - PRIMARY */
  --primary-bg:         hsl(32, 25%, 29%);          /* Dark warm brown — main action color */
  --primary-text:       hsl(0, 0%, 100%);           /* White text on primary */

  /* COLORS - SECONDARY */
  --secondary-border:   hsla(31, 26%, 46%, 0.70);   /* Warm mid-brown border at 70% */
  --secondary-text:     hsl(31, 25%, 30%);          /* Secondary labels and strokes */

  /* COLORS - TERTIARY */
  --tertiary-text:      hsl(30, 26%, 40%);          /* Subtle text button color */

  /* COLORS - INPUTS */
  --input-bg:           hsl(30, 26%, 78%);          /* Cavity effect background */
  --input-shadow-color: hsl(30, 75%, 91%);          /* Bottom-edge highlight (simulates upward light reflection) */
  --input-placeholder:  hsla(31, 25%, 30%, 0.55);   /* Muted placeholder — 55% opacity of secondary-text */

  /* COLORS - FORM LABELS */
  --form-label:         hsl(31, 22%, 24%);          /* Unified token: input text, checkbox label, radio label */
  --radio-stroke:       var(--secondary-text);        /* Single source of truth — references secondary-text */

  /* COLORS - CARDS */
  --card-bg:            hsl(36, 31%, 97%);          /* Near-white warm card surface */

  /* COLORS - EMPTY STATE */
  --empty-title:        hsl(30, 15%, 10%);          /* Warm near-black (avoids cold neutral black) */
  --empty-subtitle:     hsl(31, 17%, 39%);          /* Muted warm subtitle */

  /* COLORS - COPY ICON */
  --copy-icon-stroke:   hsl(29, 13%, 71%);          /* Deliberately subtle — copy is secondary UI */

  /* LAYOUT */
  --container-width:    736px;                        /* Max width of the showcase columns */

  /* 
   * SPACING SCALE (4px base unit) 
   * Following the Refactoring UI methodology, the naming convention uses 
   * a multiplier index (e.g. space-3 = 3 × 4px = 12px). This creates a 
   * predictable rhythm and discourages arbitrary pixel values
   */
  --space-3:   12px;  /* [xs]  — Tight grouping   (radio items, label + subtitle) */
  --space-7:   28px;  /* [sm]  — Element grouping (component + copy button) */
  --space-8:   32px;  /* [md]  — Inner section    (card column gap) */
  --space-10:  40px;  /* [lg]  — Outer section    (card grid outer gap) */
  --space-11:  44px;  /* [xl]  — Loose grouping   (empty state internal gap) */
  --space-15:  60px;  /* [2xl] — Content block    (section items without title) */
  --space-20:  80px;  /* [3xl] — Major block      (section title + content) */
  --space-26: 104px;  /* [4xl] — Page layout      (gap between main sections) */
}


/* BASE & RESET */

/*
 * Universal reset
 * - box-sizing: border-box ensures padding and borders are included in an 
 *    element's total width/height, preventing layout math headaches
 * - -webkit-tap-highlight-color removes the default translucent square 
 *    browsers (especially iOS Safari) draw over tapped elements.
 *    We rely on our custom :active and :focus-visible states for feedback instead
 */
*,
*::before,
*::after {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

body {
  font-family: 'Ubuntu', sans-serif;
  background-color: var(--bg-main);
  margin: 0;
  padding: 64px 32px;
  display: flex;
  flex-direction: column;
  align-items: center;
}


/*
 * Browser default fix for fieldset
 * I wrap the radio group in a <fieldset> and <legend> to comply with accessibility 
 * standards (WCAG 1.3.1), ensuring screen readers understand they are related options.
 * However, browsers apply thick borders and padding to fieldsets by default.
 * Resetting them here keeps the A11y semantics intact without breaking the visual layout
 */
fieldset {
  border: none;
  padding: 0;
  margin: 0;
}


/* FLEX UTILITIES */

.flex-col {
  display: flex;
  flex-direction: column;
}

.flex-row {
  display: flex;
  flex-direction: row;
}

.items-center { 
  align-items: center; 
}

.items-start { 
  align-items: flex-start; 
}

.justify-center { 
  justify-content: center; 
}

.justify-between { 
  justify-content: space-between; 
}


/* PAGE LAYOUT */

.main-container {
  display: flex;
  flex-direction: column;
  gap: var(--space-26);
  width: max-content;
  max-width: var(--container-width);
  align-items: flex-start;
}


/* TYPOGRAPHY */

.section-title {
  color: var(--text-title);
  font-size: 24px;
  font-weight: 700;
  line-height: normal;
  margin: 0;
}

.component-label {
  color: var(--text-label);
  font-size: 24px;
  font-weight: 700;
  line-height: normal;
  margin: 0;
}


/* STYLESHEET PREVIEW */

.stylesheet-desc {
  color: var(--tertiary-text);
  font-size: 18px;
  font-weight: 400;
  line-height: 1.5;
  margin: 0;
}

/*
 * Code Preview Window
 * Instead of showing a massive block of code, I set a fixed height to give users 
 * a quick look at the file structure. I added position relative to create a 
 * boundary for its children. This tells the absolute-positioned fade overlay 
 * to stay locked exactly at the bottom of this box, rather than escaping 
 * out into the main page
 */
.css-preview {
  position: relative;
  width: 100%;
  height: 260px;
  overflow: hidden;
  background: var(--card-bg);
  border-radius: 16px;
  box-shadow:
    0 1px 3px 0 rgba(0, 0, 0, 0.10),
    0 1px 2px 0 rgba(0, 0, 0, 0.06);
}

.css-preview-header {
  padding: 14px 20px;
  border-bottom: 1px solid hsla(31, 26%, 46%, 0.12);
}

.css-preview-filename {
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: 13px;
  color: var(--form-label);
}

.css-preview-code {
  margin: 0;
  padding: 16px 20px 0;
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: 13px;
  line-height: 1.65;
  color: var(--secondary-text);
  white-space: pre; /* Preserves spaces and line breaks just like an IDE */
}

/* 
 * Smooth fade effect
 * This creates a gradient at the bottom so the code fades out gently, instead 
 * of ending with a sharp cut. Since this gradient sits on top of the text, I 
 * added pointer-events none to make it "click-through". Without this property, 
 * the gradient would block the mouse, and users wouldn't be able to select or 
 * copy the code below it
 */
.css-preview-fade {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 80px;
  background: linear-gradient(to bottom, transparent, var(--card-bg));
  pointer-events: none;
}

/*
 * Syntax highlighting colors
 * I pulled these colors directly from the Docusaurus light theme of my site 
 * (simoneamico.com) because this project will be hosted there. This keeps the 
 * design perfectly consistent. The keywords use a warm brown that matches the 
 * primary "brand" color. The code tokens (like property names and numbers) use 
 * a dark petrol blue for strong contrast. Finally, the comments are styled 
 * with a warm muted gray and italics, ensuring they stay visually in the 
 * background without competing with the actual code
 */
.syntax-keyword { 
  color: #614a33; 
}
.syntax-token { 
  color: #005a75; 
}
.syntax-comment { 
  color: hsl(30, 18%, 58%); 
  font-style: italic; 
}


/* COMPONENT: BUTTONS */

/*
 * Why explicit font-family on buttons:
 * Unlike most HTML elements, the <button> tag does not automatically inherit 
 * the font family from the body. Browsers apply their own ugly default styles 
 * to forms and buttons. I declare the font explicitly here on the shared rule 
 * to guarantee that 'Ubuntu' renders everywhere, preventing broken UI
 *
 * Why min-width instead of fixed width:
 * I use min-width paired with horizontal padding to create a flexible system. 
 * This ensures the button respects its minimum visual size for short labels 
 * (like "Save"), but automatically expands horizontally if a longer label 
 * (like "Confirm changes") is used. A fixed width would eventually cause text 
 * to overflow or clip in real-world scenarios
 *
 * Native feels on mobile:
 * I added touch-action: manipulation to disable the default "double-tap to zoom" 
 * behavior on mobile browsers. This makes clicks register instantly without any 
 * 300ms delay. I also used user-select: none (and its -webkit- prefix for Safari) 
 * to prevent the text from being accidentally highlighted or copied if the user 
 * long-presses the button. These two rules combined make the web button feel 
 * like a real, physical app control
 */
.btn-primary,
.btn-secondary,
.btn-tertiary {
  font-family: 'Ubuntu', sans-serif;
  font-size: 20px;
  font-weight: 500;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  box-sizing: border-box;
  padding: 0;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
}

/*
 * Primary Button
 * To make the button look like a physical object protruding from the page, I 
 * used two subtle drop shadows for depth, plus a pure white inset shadow pushed 
 * 1px from the top. This acts as an "inner highlight", simulating an overhead 
 * light source hitting the top edge of the button
 */
.btn-primary {
  min-width: 140px;
  height: 52px;
  padding: 0 28px;
  border-radius: 16px;
  background: var(--primary-bg);
  box-shadow:
    0 1px 2px 0 rgba(0, 0, 0, 0.20),
    0 0.5px 1px 0 rgba(0, 0, 0, 0.10),
    0 1px 0 0 rgba(255, 255, 255, 0.30) inset;
  color: var(--primary-text);
  border: none;
  transition: filter 160ms ease, box-shadow 160ms ease, transform 160ms ease;
}

/* 
 * Secondary Button
 * Why a smaller min-width for the secondary button?
 * This is a deliberate choice for visual hierarchy. The primary button represents 
 * the main call-to-action and must carry the most visual weight. By giving the 
 * secondary button a slightly smaller minimum width (128px vs 140px), it naturally 
 * feels subordinate when placed side-by-side with a primary button (e.g., "Cancel" 
 * next to "Confirm"). This subtly guides the user's eye toward the primary action
 */
.btn-secondary {
  min-width: 128px;
  height: 52px;
  padding: 0 28px;
  border-radius: 16px;
  border: 3px solid var(--secondary-border);
  background: transparent;
  color: var(--secondary-text);
  transition: background-color 160ms ease, transform 160ms ease;
}

/* 
 * Tertiary Button
 * Unlike the others, this button is sized strictly by its content (width: auto). 
 * The padding provides just enough invisible hit area for the user's mouse
 */
.btn-tertiary {
  width: auto;
  height: auto;
  padding: 8px 12px;
  color: var(--tertiary-text);
  background: transparent;
  border: none;
  transition: filter 160ms ease;
}

/* Copy Button */

/*
 * UX Note on hit area:
 * The copy icon is visually 16x16px, which is far below the WCAG 2.5.5
 * minimum touch target of 44x44px. I expanded the hit area to 44x44px via
 * padding: 14px, then applied margin-top: -14px to compensate. This trick 
 * ensures the icon sits at the exact same visual distance from the component 
 * above it, while providing a massive invisible hit area for mobile users.
 * The flex properties ensure the SVG remains perfectly centered within 
 * this new enlarged hit area
 */
.btn-copy {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 14px;
  margin-top: -14px; /* Compensates top padding -> preserves visual spacing */
  background: transparent;
  border: none;
  cursor: pointer;
}

.btn-copy svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0; /* Safeguard: keeps exact 16x16px proportions, preventing flex distortion */
  transition: opacity 120ms ease;
}


/* COMPONENT: INPUTS */

/*
 * Cavity effect:
 * Following Refactoring UI principles, I use two shadow layers to simulate a 
 * physical inset. The bottom shadow acts as a highlight (light hitting the lower
 * edge), while the top inset shadow represents the area blocked from overhead light.
 * This makes the input feel recessed into the page, signaling "place content here" 
 * as opposed to a button which protrudes and signals "press me"
 *
 * I removed the browser's default outline because the custom focus ring will be 
 * handled intentionally via dedicated pseudo-classes, keeping the "brand" consistent
 */
.input-alone {
  width: 352px;
  height: 56px;
  border-radius: 16px;
  background: var(--input-bg);
  box-shadow:
    0 1px 0 0 var(--input-shadow-color),
    0 1px 1px 0 rgba(0, 0, 0, 0.20) inset;
  border: none;
  outline: none; /* Suppresses browser default custom focus ring handled below */
  padding: 0 20px;
  box-sizing: border-box;
  color: var(--form-label);
  font-size: 18px;
  font-weight: 400;
  transition: box-shadow 160ms ease;
}

.input-alone::placeholder {
  color: var(--input-placeholder);
}

/* Checkbox */

/*
 * I sized the checkbox at 20×20px with border-radius: 6px. The ratio (6/20 = 30%)
 * mirrors the input's ratio (16/56 = ~29%), keeping the two controls visually
 * "from the same family". They feel concentric at their respective scales
 */
.cbx-box {
  width: 20px;
  height: 20px;
  border-radius: 6px;
  background: var(--input-bg);
  box-shadow:
    0 1px 0 0 var(--input-shadow-color),
    0 1px 1px 0 rgba(0, 0, 0, 0.20) inset;
  flex-shrink: 0;
  transition: background-color 120ms ease, box-shadow 120ms ease;
}

.cbx-label {
  color: var(--form-label);
  font-size: 18px;
  font-weight: 400;
}

/*
 * Screen-reader only utility (visually hidden, still accessible):
 * - Purpose: Hide text visually without removing it from the accessibility tree
 *   (Unlike display:none/visibility:hidden, which screen readers typically ignore)
 * - Technique: 
 *   1. Collapse the element to a 1x1px box and take it out of normal layout flow.
 *   2. Use a negative margin (-1px) to pull that single remaining pixel back 
 *      onto itself, completely neutralizing its physical footprint so it won't 
 *      disturb the layout of surrounding elements
 *   3. Apply a clip/clip-path to act as a 0px mask. This ensures that any long
 *       text inside the 1px box is strictly contained and cannot spill out to 
 *       become visually rendered on the screen
 * - Compatibility: clip is a legacy fallback; clip-path is the modern equivalent, 
 *   guaranteeing this trick works on both ancient and modern browsers
 */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
 * We target the custom box (.cbx-box) only when the hidden native checkbox 
 * before it is selected. To keep the HTML clean and avoid extra DOM elements, 
 * the checkmark is injected directly via CSS as a URL-encoded SVG
 * 
 * The %23 URL encoding trick
 * In a standard SVG, the stroke color would be written as "#D5C7B8". However, 
 * inside a Data URI, the hashtag symbol (#) breaks the code because browsers 
 * interpret it as a jump link (anchor). To safely pass the hex color, we must 
 * mask the hashtag using its URL-encoded percentage equivalent: "%23"
 * 
 * The color itself (#D5C7B8) perfectly matches the input's default background
 * When the checkbox turns solid, this specific color creates a negative space 
 * effect, making the checkmark look like it was physically cut out of the
 * colored background
 */
.cbx-custom:checked + .cbx-box {
  background-color: var(--primary-bg);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 8' fill='none'%3E%3Cpath d='M1 4L4 7L9 1' stroke='%23D5C7B8' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center 54%; /* I noticed true center (50%) looked too high, so I iterated down to 54% for a perfect optical balance */
  background-size: 10px 8px;
  box-shadow: none;
}

/* Radio */

.radio-label-text {
  color: var(--form-label);
  font-size: 18px;
  font-weight: 400;
}

/*
 * Contextual scaling and Separation of Concerns:
 * The source SVG in the HTML retains its native 24x24px dimensions to match 
 * its original viewBox grid. This acts as a standard, reusable baseline across 
 * the project. I handle the actual sizing strictly via CSS because an icon's 
 * scale depends on its context, not its markup. This prevents giant icons if 
 * the stylesheet fails to load, while keeping the HTML modular
 *
 * Typography matching (Cap-height alignment):
 * In this specific context, I override the size to 20x20px to deliberately 
 * match the typography. I found that alongside an 18px font, a 20px control 
 * sits perfectly flush with the text's cap-height (the height of capital letters), 
 * ensuring a crisp, professional horizontal alignment
 */
.custom-radio-svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  transition: filter 120ms ease;
}

.radio-dot { display: none; } /* Hidden by default — shown via :checked below */

.native-radio:checked + .custom-radio-svg .radio-dot {
  display: block;
}


/* COMPONENT: ICON FRAME */

/*
 * Ghost container for icon size normalization:
 * The three icon variants have different sizes (128px vs 16px). Without a fixed 
 * container, the 16px variant would collapse its column and break the grid layout.
 * I wrapped all icons in this invisible 128x128px frame so every column maintains 
 * the exact same footprint (the physical space occupied on the screen), regardless
 * of the actual icon size inside it
 */
.icon-frame {
  width: 128px;
  height: 128px;
  display: flex;
  align-items: flex-end; /* Keeps smaller icons anchored to the bottom baseline */
  justify-content: center;
}


/* COMPONENT: CARDS */

.cards-row-center {
  display: flex;
  flex-direction: row;
  justify-content: center;
  width: 100%;
}

.card {
  width: 224px;
  height: 224px;
  border-radius: 12px;
  background: var(--card-bg);
}

/*
 * Five-level shadow system (Two-layer model):
 * I built this elevation system based on Refactoring UI principles.
 * Realistic shadows require two distinct layers to feel natural:
 * 1. A sharp layer (low blur, higher opacity) to simulate direct overhead light
 * 2. A soft layer (high blur, lower opacity) to simulate ambient, diffuse light
 * 
 * The mathematical scale (Soft Layer):
 * I double the y-offset at each step to create a predictable sense of depth.
 * For the blur radius, I use a sliding multiplier applied to the y-offset.
 * The multiplier smoothly tightens from 3.0x down to 2.0x as elevation increases, 
 * preventing high shadows from blowing out too widely:
 * 
 * Level | y-offset | Multiplier | Blur Result
 * ------|----------|------------|------------
 *   1   |   1px    |    3.0x    |    3px
 *   2   |   2px    |    3.0x    |    6px
 *   3   |   4px    |    2.5x    |   10px
 *   4   |   8px    |    2.25x   |   18px
 *   5   |  16px    |    2.0x    |   32px
 */
.card-1 {
  box-shadow:
    0 1px 3px 0 rgba(0, 0, 0, 0.10), /* Soft layer */
    0 1px 2px 0 rgba(0, 0, 0, 0.06); /* Sharp layer */
}

.card-2 {
  box-shadow:
    0 2px 6px 0 rgba(0, 0, 0, 0.10),
    0 1px 3px 0 rgba(0, 0, 0, 0.06);
}

.card-3 {
  box-shadow:
    0 4px 10px 0 rgba(0, 0, 0, 0.10),
    0 2px 6px 0 rgba(0, 0, 0, 0.07);
}

.card-4 {
  box-shadow:
    0 8px 18px 0 rgba(0, 0, 0, 0.11),
    0 4px 10px 0 rgba(0, 0, 0, 0.07);
}

.card-5 {
  box-shadow:
    0 16px 32px 0 rgba(0, 0, 0, 0.12),
    0 8px 16px 0 rgba(0, 0, 0, 0.08);
}


/* COMPONENT: EMPTY STATE */

.title-empty {
  color: var(--empty-title);
  font-size: 40px;
  font-weight: 500;
  margin: 0;
}

.subtitle-empty {
  width: 372px;
  color: var(--empty-subtitle);
  font-size: 24px;
  font-weight: 400;
  line-height: 40px;
  margin: 0;
}


/* INTERACTIVE STATES */

/*
 * Asymmetric timing for physical feel:
 * CSS transition durations usually apply symmetrically (same speed in and out).
 * However, physical buttons don't work like that. They press down instantly but 
 * spring back up slightly slower. I recreated this by setting a fast 60ms 
 * transition on the :active state (for a snappy press) and relying on the 
 * slower 160ms transition declared on the base rule for a satisfying release
 * 
 * On mobile touch devices, applying standard :hover and :active pseudo-classes 
 * creates a notorious bug where the button stays highlighted even after the finger 
 * is lifted. To prevent this, I wrapped all mouse-specific states inside a 
 * @media (hover: hover) query. For touch devices, a small JS script manually 
 * adds/removes an .is-pressed class, giving us exact control over the touch duration
 */

/* Primary button */
@media (hover: hover) {
  .btn-primary:hover { filter: brightness(0.90); }

  .btn-primary:active {
    filter: brightness(0.82); /* Progressive pressure model: darker than hover */
    box-shadow: none;
    transform: translateY(1px);
    transition: filter 60ms ease, box-shadow 60ms ease, transform 60ms ease;
  }
}

.btn-primary.is-pressed {
  filter: brightness(0.82);
  box-shadow: none;
  transform: translateY(1px);
  transition: filter 60ms ease, box-shadow 60ms ease, transform 60ms ease;
}

.btn-primary:focus-visible {
  outline: 3px solid hsla(31, 26%, 46%, 0.55);
  outline-offset: 3px;
}

/* Secondary button */
@media (hover: hover) {
  .btn-secondary:hover { background-color: hsla(31, 26%, 46%, 0.10); }

  .btn-secondary:active {
    background-color: hsla(31, 26%, 46%, 0.18);
    transform: translateY(1px);
    transition: background-color 60ms ease, transform 60ms ease;
  }
}

.btn-secondary.is-pressed {
  background-color: hsla(31, 26%, 46%, 0.18);
  transform: translateY(1px);
  transition: background-color 60ms ease, transform 60ms ease;
}

.btn-secondary:focus-visible {
  outline: 3px solid hsla(31, 26%, 46%, 0.55);
  outline-offset: 3px;
}

/* Tertiary button */
@media (hover: hover) {
  .btn-tertiary:hover { filter: brightness(0.87); }

  .btn-tertiary:active {
    filter: brightness(0.65);
    transition: filter 60ms ease;
  }
}

.btn-tertiary.is-pressed {
  filter: brightness(0.65);
  transition: filter 60ms ease;
}

.btn-tertiary:focus-visible {
  outline: 3px solid hsla(31, 26%, 46%, 0.55);
  outline-offset: 3px;
  border-radius: 8px;
}

/* Copy button */
@media (hover: hover) {
  .btn-copy:hover svg  { opacity: 0.65; }
  .btn-copy:active svg { opacity: 0.40; }
}

.btn-copy:focus-visible {
  outline: 3px solid hsla(31, 26%, 46%, 0.55);
  outline-offset: 2px;
  border-radius: 6px;
}

/* Input */

/*
 * Preserving the inset cavity effect on focus
 * A standard solid outline would destroy the input's physical inset appearance
 * Instead, I kept the two original shadow layers (the bottom edge and top inset) 
 * and injected a 2px primary-color ring as a third composite layer. This clearly 
 * signals focus to the user without breaking the illusion of the carved-in field
 */
.input-alone:focus {
  outline: none;
  box-shadow:
    0 1px 0 0 var(--input-shadow-color),
    0 1px 1px 0 rgba(0, 0, 0, 0.20) inset,
    0 0 0 2px var(--primary-bg);
}

/* Checkbox */
.cbx-custom:focus-visible + .cbx-box {
  box-shadow:
    0 1px 0 0 var(--input-shadow-color),
    0 1px 1px 0 rgba(0, 0, 0, 0.20) inset,
    0 0 0 2px var(--primary-bg);
}

.cbx-custom:checked:focus-visible + .cbx-box {
  box-shadow: 0 0 0 2px var(--primary-bg); /* Overrides default focus to prevent inset shadows on solid background */
}

/* Radio */

/* 
 * Full-row hover targeting
 * Applying the hover effect strictly to the SVG makes it hard to trigger 
 * By targeting the parent <label:hover>, the brightness effect activates even 
 * if the user hovers over the text label, making the whole row highly responsive
 */
@media (hover: hover) {
  label:hover > .custom-radio-svg { filter: brightness(0.82); }
}

.native-radio:focus-visible + .custom-radio-svg {
  outline: 2px solid var(--primary-bg);
  outline-offset: 2px;
  border-radius: 50%; /* Forces the square outline to become a circle, simulating a native radio focus ring (since the custom element is an SVG) */
}


/* RESPONSIVE */

/*
 * Mobile Breakpoint Strategy (760px)
 * This single breakpoint triggers just above our 736px container width.
 * Below this threshold, multi-column rows would break the layout.
 * The mobile strategy focuses on 5 core adjustments:
 * - Fluid sizing: Redefining --container-width to 100% and using min() for inputs
 * - Vertical stacking: Switching flex rows (.components-row) to columns
 * - Card Carousel: Converting the static card grid into a horizontal scrolling container
 * - Proportional scaling: Reducing the size of cards and icons by exactly 0.625x
 * - Hardware safety: Respecting iOS notch safe areas via env() variables
 */
@media (max-width: 760px) {

  :root {
    --container-width: 100%;
    --space-7:   20px;  /* component to copy gap: 28px -> 20px */
    --space-10:  24px;  /* inner section gap:     40px -> 24px */
    --space-11:  32px;  /* empty state inner gap: 44px -> 32px */
    --space-15:  36px;  /* section content gap:   60px -> 36px */
    --space-20:  48px;  /* section title to row:  80px -> 48px */
    --space-26:  52px;  /* main section gap:     104px -> 52px */
  }

  body {
    padding: 32px max(16px, env(safe-area-inset-right)) 32px max(16px, env(safe-area-inset-left));
    overscroll-behavior-x: none;
    touch-action: manipulation;
  }

  html {
    overflow-x: clip;
  }

  .main-container {
    width: 100%;
  }

  .section-content {
    width: 100%; /* Sections that wrap content on desktop must fill the screen on mobile */
  }

  /* 
   * Component groups (e.g. Primary, Secondary, Tertiary buttons) 
   * Switch from a side-by-side horizontal layout to a vertical stack 
   * to prevent elements from overflowing the narrow mobile screen
   */
  .components-row {
    flex-direction: column;
    align-items: center;
    gap: var(--space-10);
  }

  /*
   * Card Carousel DESIGN:
   * 1. Flattening the DOM: Applying `display: contents` to the desktop row wrappers 
   *    (.cards-row-1, .cards-row-center) dissolves their physical boxes from the layout
   *    This allows their inner cards to bypass their direct parents and become direct 
   *    flex children of this horizontal carousel container
   * 2. Scroll and Snap: `overflow-x: auto` establishes a horizontal scroll container
   *    Applying `scroll-snap-type: x mandatory` intercepts the user's swipe momentum, 
   *    forcing the viewport to cleanly dock at the start of each card rather than 
   *    stopping mid-scroll
   * 3. Shadow Protection: Elements with `overflow` create a strict clipping mask 
   *    that truncates overflowing visuals like `box-shadow`. Injecting `padding-bottom` 
   *    artificially expands the container's internal rendering space, giving shadows 
   *    the exact pixel room they need to paint
   * 4. Full-bleed Layout: Negative margins pull the container outside the body's padding, 
   *    allowing cards to swipe from edge to edge of the screen. Inner padding pushes 
   *    the cards back into place, while `scroll-padding-left` makes sure the first card 
   *    snaps perfectly in line with the rest of the page layout
   */
  .cards-outer {
    flex-direction: row;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch; /* Smooth native momentum scrolling on iOS */
    scrollbar-width: none;             /* Hides the visual scrollbar but keeps swipe active */
    align-items: flex-start;
    gap: 16px !important;
    
    /* 2. Scroll and Snap */
    scroll-snap-type: x mandatory;

    /* 3. Shadow Protection */
    padding-bottom: 8px; 
    
    /* 4. Full-bleed Overflow */
    box-sizing: border-box;
    width: calc(100% + 32px);  /* 100% + 16px left + 16px right */
    margin-left: -16px;        /* Pulls container to the left glass edge */
    padding-left: 16px;        /* Pushes the first card back into visual alignment */
    padding-right: 16px;       /* Prevents the last card from sticking to the right edge */
    scroll-padding-left: 16px; /* Offsets the snap position by 16px so the first card aligns with the page layout */
  }

  .cards-outer::-webkit-scrollbar {
    display: none;
  }

  /* 1. Flattening the DOM: Removes the wrapper's box model, making the cards direct flex items of the carousel */
  .cards-row-1,
  .cards-row-center {
    display: contents;
  }

  /* 2. Scroll and Snap: Forces the scroll position to stop exactly at the start edge of each card */
  .cards-row-1 > div,
  .cards-row-center > div {
    scroll-snap-align: start;
    flex-shrink: 0;
  }

  /* Cards scaled down to 0.625x (224px -> 140px) to fit mobile screens */
  .card {
    width: 140px;
    height: 140px;
  }

  /* Icons scaled down to match the cards' 0.625x ratio (128px -> 80px) */
  .components-row svg[width="128"] {
    width: 80px;
    height: 80px;
  }

  .icon-frame {
    width: 80px;
    height: 80px;
  }

  /* Restricts the code block height to 200px and hides long text lines to prevent the mobile layout from breaking horizontally */
  .css-preview {
    height: 200px;
    overflow-x: hidden; 
  }

  /* Input: Scales width to the viewport while setting a maximum limit of 352px (original desktop size) */
  .input-alone {
    width: min(352px, calc(100vw - 40px));
  }

  /* Empty state: Reduces title font size and constrains subtitle width to prevent text overflow */
  .title-empty {
    font-size: 32px;
  }

  .subtitle-empty {
    width: min(372px, calc(100vw - 40px));
    font-size: 20px;
    line-height: 32px;
  }
}

/*
 * Mobile Landscape
 * When a phone is horizontal, the camera notch can cover the content on the left or right.
 * To fix this, `env(safe-area-inset)` directly asks the device for the exact pixel size 
 * of that hardware notch. We then use `max(24px, env(...))` to tell the browser: 
 * "Always keep at least 24px of padding, but if the notch is bigger than 24px, 
 * use the notch size so nothing gets hidden"
 */
@media (orientation: landscape) and (max-height: 600px) {
  body {
    padding-top: 24px;
    padding-bottom: 24px;
    padding-left: max(24px, env(safe-area-inset-left));
    padding-right: max(24px, env(safe-area-inset-right));
  }

  /* Input: slightly narrower in landscape since the vertical height is restricted */
  .input-alone {
    width: min(310px, calc(100vw - 48px));
  }

  /* Restore horizontal layout: landscape is wide enough for side-by-side components */
  .components-row {
    flex-direction: row;
    align-items: flex-start;
  }

  /* 4. Full-bleed Overflow (Landscape)
   * Recalibrate to match the wider landscape body padding (16px -> 24px) 
   */
  .cards-outer {
    width: calc(100% + 48px);
    margin-left: -24px;
    padding-left: 24px;
    padding-right: 24px;
    scroll-padding-left: 24px;
  }
}


/* MOTION PREFERENCE */

/*
 * Accessibility fix for vestibular disorders:
 * I apply prefers-reduced-motion at the end so it overrides all transitions
 * defined above. I discovered that 0.01ms duration (instead of 0) avoids a 
 * browser behavior where 0ms transitions can skip JS transitionend event listeners
 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}