/* Design tokens for the Chess Books theme.
   Values are taken from the Figma reference (file LhHc6mzXOWQFWqkrLTZ88G,
   home node 3:3, category node 25:414). Every other stylesheet in this theme
   consumes these; no feature stylesheet may hard-code a size, space or color. */

:root {
  /* ---- Color ---- */
  --color-canvas: #ffffff;
  --color-surface: #f8f8f8;
  --color-brand-soft: #ffeacb;
  --color-action: #ffc46b;
  --color-ink: #111111;
  --color-muted: #707070;
  /* Figma uses a single light gray for de-emphasised text (author, placeholder,
     footer links). Kept distinct from --color-muted, which is body-copy gray.
     At 1.9:1 on the card surface it is for what may look inert (disabled
     options, placeholders, the header's icons), never for text a visitor must
     read: authors and old prices take --color-muted (4.7:1). */
  --color-subtle: #b7b7b7;
  --color-border: #dedede;
  --color-footer: #2a2b2d;
  --color-footer-ink: #ffffff;
  --color-footer-muted: #b7b7b7;
  --color-danger: #c62828;
  --color-dialog-backdrop: rgb(17 17 17 / 58%);
  /* Veils content that is being replaced (the catalog grid while it refreshes).
     Far lighter than the dialog backdrop: the page behind stays readable,
     because it is still the answer until the new one arrives. */
  --color-busy-veil: rgb(255 255 255 / 72%);
  /* The Figma badge colours (#6b7aff, #ff4040) darkened, at the same hue and
     saturation, to the lightest shade that carries white 16px bold text at
     4.5:1 (WCAG AA). The sale red is also the discounted price's colour. */
  --color-badge-new: #5364ff;
  --color-badge-sale: #ee0000;
  /* A favourite's heart. Deliberately softer than the sale badge's alarm red:
     a dusty rose that sits beside the amber action and the cream brand tint
     rather than competing with them. No Figma frame draws favourites (see
     CLAUDE.md, "Figma source-of-truth deviations"). */
  --color-favorite: #e0707a;
  /* An order's status badge, one tone per stage so a glance tells an open
     order from a shipped, finished or cancelled one. No Figma frame draws
     order states. Pending is the amber action (the shop owes the next step);
     processing takes the "new" badge's blue, shipped a violet, complete and
     delivered a green and cancelled the danger red, each as a pale fill with
     its ink at 4.5:1 or more (WCAG AA for the badge's 14px bold label). */
  --color-status-pending: var(--color-action);
  --color-status-pending-ink: var(--color-ink);
  --color-status-processing: #e6e9ff;
  --color-status-processing-ink: #3a47c9;
  --color-status-shipped: #f1e6fb;
  --color-status-shipped-ink: #7030a8;
  --color-status-complete: #e3f4e7;
  --color-status-complete-ink: #1e6e36;
  --color-status-cancelled: #fde8e8;
  --color-status-cancelled-ink: var(--color-danger);
  --color-focus: #1f5eff;

  /* ---- Focus ----
     One ring for every focusable element; a composite control (the header
     search field) draws it around the whole control instead. */
  --focus-ring: max(0.1875rem, 3px) solid var(--color-focus);
  --focus-ring-offset: max(0.125rem, 2px);

  /* ---- Interaction ----
     Hover shades are DERIVED, never hand-picked: one darkening amount applies
     to every colour, so a new colour token gets its hover variant by copying
     one line. The plain hex on the first declaration is the fallback for
     engines without color-mix(); the second line wins where it is supported. */
  --hover-darken: 12%;

  --color-action-hover: #e0ac5e;
  --color-action-hover: color-mix(in srgb, var(--color-action), #000 var(--hover-darken));

  --color-ink-hover: #000000;
  --color-ink-hover: color-mix(in srgb, var(--color-ink), #000 var(--hover-darken));

  --color-danger-hover: #ae2323;
  --color-danger-hover: color-mix(in srgb, var(--color-danger), #000 var(--hover-darken));

  --color-footer-ink-hover: #e0e0e0;
  --color-footer-ink-hover: color-mix(in srgb, var(--color-footer-ink), #000 var(--hover-darken));

  --color-border-hover: #c3c3c3;
  --color-border-hover: color-mix(in srgb, var(--color-border), #000 var(--hover-darken));

  --color-brand-soft-hover: #e0cdb3;
  --color-brand-soft-hover: color-mix(in srgb, var(--color-brand-soft), #000 var(--hover-darken));

  --color-muted-hover: #636363;
  --color-muted-hover: color-mix(in srgb, var(--color-muted), #000 var(--hover-darken));

  --color-favorite-hover: #c5636b;
  --color-favorite-hover: color-mix(in srgb, var(--color-favorite), #000 var(--hover-darken));

  /* ---- Root scale ----
     Every size in the theme is in rem, so the root font size is the one dial
     for the whole composition. 100% everywhere except a desktop with a mouse
     (see the media query at the end of this file). */
  --root-font-size: 100%;

  /* ---- Typography ----
     Figma sets body copy at 18px, not 16px; the scale is anchored there. */
  --font-sans: "Raleway", system-ui, sans-serif;

  /* Sizes audited against every TEXT node in the Figma file via the REST API.
     Each is fluid between a 390px and a 1440px anchor: the max is the Figma
     desktop value, the min is the phone value. Interpolating in tokens.css is
     what keeps feature stylesheets free of breakpoint-by-breakpoint overrides.
     Formula, for a -> b px across 390 -> 1440:
       slope(vw) = (b-a)/10.5 ; intercept(rem) = (a - (b-a)/1050*390)/16
     The px in each max() is the step's phone size, the floor the desktop
     density scale (end of file) may not take it below: text on a laptop is
     never smaller than the same text on a phone. */
  /* Text no rule sizes (it inherits from body): the browser's own default,
     floored like the steps below so the density scale cannot shrink it. */
  --font-size-base: max(1rem, 16px);
  --font-size-3xs: max(0.75rem, 12px);  /* 12 - copyright, count badges. Fixed: already minimal. */
  --font-size-xs: max(0.875rem, 14px);  /* 14 - labels, form fields. Fixed. */
  --font-size-field: var(--font-size-xs); /* text typed into a field; 16 on touch screens, see the end of file */
  --font-size-sm: max(clamp(0.9375rem, 0.914rem + 0.1vw, 1rem), 15px);    /* 15 -> 16 */
  --font-size-md: max(clamp(1rem, 0.954rem + 0.19vw, 1.125rem), 16px);    /* 16 -> 18 */
  --font-size-xl: max(clamp(1.1875rem, 1.071rem + 0.48vw, 1.5rem), 19px); /* 19 -> 24 */
  /* The header wordmark shares the 24px desktop size of the type scale's xl
     step (Figma node 4:19), but needs a lower mobile floor than xl's 19px:
     two stacked lines at that size crowd the burger toggle and header
     actions in a 390px row. Kept as its own token instead of lowering xl,
     which drives unrelated headings elsewhere. Unlike the type scale it has
     no floor: it is part of the logo, which scales as one picture. */
  --font-size-brand: clamp(0.875rem, 0.643rem + 0.95vw, 1.5rem); /* 14 -> 24 */
  --font-size-2xl: max(clamp(1.25rem, 1.111rem + 0.57vw, 1.625rem), 20px);  /* 20 -> 26 */
  --font-size-3xl: max(clamp(1.375rem, 1.189rem + 0.76vw, 1.875rem), 22px); /* 22 -> 30 */

  --font-weight-regular: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;
  --font-weight-black: 900;    /* wordmark */

  /* 1.17 is the file's dominant leading; 1.3 is used for body copy and the
     uppercase actions; 2.0 for form field text. */
  --line-height-tight: 1.17;
  --line-height-snug: 1.3;
  --line-height-normal: 1.5;
  --line-height-loose: 2;
  --line-height-solid: 1;

  --letter-spacing-caps: 0.04em;

  /* ---- Space ----
     Micro-spacing (<=24) is fixed: shrinking it makes tight clusters collide,
     so it is floored against the desktop density scale too. Macro-spacing is
     fluid, and is where most of the page height goes, on a phone and on a
     laptop alike. */
  --space-3xs: max(0.25rem, 4px);  /* 4 */
  --space-2xs: max(0.5rem, 8px);   /* 8 */
  --space-xs: max(0.75rem, 12px);  /* 12 */
  --space-sm: max(1rem, 16px);     /* 16 */
  --space-md: max(1.25rem, 20px);  /* 20 */
  --space-lg: max(1.5rem, 24px);   /* 24 */
  --space-xl: clamp(1.25rem, 0.971rem + 1.14vw, 2rem);   /* 20 -> 32 */
  --space-2xl: clamp(1.5rem, 1.129rem + 1.52vw, 2.5rem); /* 24 -> 40 */
  --space-3xl: clamp(1.75rem, 1.286rem + 1.9vw, 3rem);   /* 28 -> 48 */
  --space-4xl: clamp(2rem, 1.257rem + 3.05vw, 4rem);     /* 32 -> 64 */
  --space-5xl: clamp(2.5rem, 1.571rem + 3.81vw, 5rem);   /* 40 -> 80 */

  /* ---- Layout ---- */
  --container-max: 75rem; /* 1200 content inside the 1440 Figma composition */
  --gutter: clamp(1rem, 4vw, 2.5rem);
  --home-curation-card-width: min(82vw, 20.625rem); /* phone carousel card: 82vw, max 330 */
  --home-curation-tabs-width: 17.5rem;         /* 280: Figma discount/new switch */
  --home-curation-tabs-width-compact: 13.75rem; /* 220: phone */

  /* ---- Shape ---- */
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-pill: 999px;
  /* Strokes and functional icons are floored at their own size: under the
     desktop density scale a 1px line would land between device pixels and
     render thinner, and an icon is read like text. */
  --stroke-hairline: max(0.0625rem, 1px);
  --stroke-control: max(0.125rem, 2px);
  --status-icon-size: max(1rem, 16px);
  --sort-icon-size: max(1.25rem, 20px);
  --favorite-icon-size: max(1.5rem, 24px); /* 24: the heart inside its 44px toggle */
  --header-icon-size: max(1.125rem, 18px); /* 18: the header's cart and favourites glyphs */
  --stepper-icon-size: max(0.75rem, 12px); /* 12: the cart quantity stepper's plus and minus */
  --chevron-size: max(0.5rem, 8px); /* 8: every border-drawn chevron; the pager and the lightbox override it */
  --shadow-card: 0 0.5rem 1.5rem rgb(0 0 0 / 8%);
  --shadow-dialog: 0 1.5rem 4rem rgb(0 0 0 / 24%);

  /* ---- Motion ----
     Durations and easings are the file's own prototype interaction values,
     read from the Figma REST API (see the asset README for node ids). */
  --motion-card: 100ms;      /* product card hover: DISSOLVE / EASE_IN */
  --motion-tile: 300ms;      /* category tile hover: DISSOLVE / EASE_OUT */
  --motion-switch: 300ms;    /* offer switcher: SMART_ANIMATE / EASE_OUT */
  --motion-drawer: 200ms;    /* panels that take over the screen: mobile menu, filter drawer, phone search */
  /* The puzzle page's die turning while the next puzzle loads. No Figma frame
     draws it; long enough to read as a throw, short enough never to wait on.
     chess-puzzles.js reads it, so the script and the animation agree. */
  --motion-dice: 600ms;
  /* The header's die, turned once a week to draw the eye (die-hint.js): it
     waits for the page to settle, then turns twice at the die's own pace. */
  --die-hint-delay: 800ms;
  --die-hint-turns: 2;
  --ease-in: cubic-bezier(0.42, 0, 1, 1);
  --ease-out: cubic-bezier(0, 0, 0.58, 1);

  /* ---- Component metrics from the Figma reference ----
     Fluid where the metric drives page height or eats phone width; fixed where
     the value is an accessibility floor or a desktop-only container. */

  /* WCAG 2.5.8 target size. Never scales — it is a floor, not a preference,
     on a laptop's density scale too. */
  --tap-target: max(2.75rem, 44px);  /* 44 */
  --chip-height: max(2.25rem, 36px); /* 36: drawn size of a filter chip; its hit area stays --tap-target */
  --tap-target-narrow: max(2.25rem, 36px); /* 36: a pager link's side on a phone row; still clears WCAG 2.5.8's 24px */

  --header-height: clamp(3.5rem, 2.943rem + 2.29vw, 5rem);    /* 56 -> 80 */
  --action-height: clamp(2.75rem, 2.611rem + 0.57vw, 3.125rem); /* 44 -> 50 */
  --header-z: 50;               /* below the drawers, which cover the header */
  --drawer-z: 100;              /* panels that take over the screen: menu, filters, phone search */
  --die-fab-z: 45;              /* the home page's floating die: above the page, below the header's overlays and drawers */
  --catalog-toolbar-z: 40;      /* sticky filter/sort row: above cards, below the header */
  --action-bar-z: 40;           /* bottom action bar stuck over the page (the puzzle's die): above content, below the header */
  --puzzle-coach-z: 110;        /* the puzzle board's first-visit note: its backdrop over the header and the action bar, below toasts and dialogs */
  --catalog-toolbar-open-z: 60; /* row with its sort panel open: its backdrop covers the header too */
  --account-nav-open-z: 60;     /* stacked account menu while open: its backdrop covers the header too */

  --logo-tile-width: 8.5rem;    /* 136 */
  --logo-tile-width-compact: 6.5rem; /* 104: phone header, beside burger, cart and account */
  /* The bookmark hanging from the tile (logo-mark.svg). Sized in rem with the
     tile and the wordmark, so the logo scales as one picture. */
  --logo-glyph-width: 1.1875rem;  /* 19 */
  --logo-glyph-height: 1.875rem;  /* 30 */
  --search-field-halo: var(--space-xs); /* lit margin around the search field over the page backdrop */
  --search-suggest-cover-width: 2.5rem; /* 40: cover thumbnail in a suggestion row */
  --search-suggest-max-height: min(32rem, 70vh); /* the panel scrolls, the page does not */

  /* Breathing room between the last row and the top of the keyboard.

     This was 2.5rem, sized to hold the whole of the form accessory capsule
     (previous/next/Done) that iOS floats over the page above the keyboard, on
     the assumption that iOS leaves it out of the keyboard height it reports.
     On iOS 26 it does not: the visual viewport it reports already stops above
     the capsule, so the sheet was reserving that height a second time and the
     list stopped a capsule-and-a-half short of the keyboard. What is left is
     a gap chosen to look like one, and it is the single value to turn if the
     list ever does end up under the capsule. */
  --keyboard-clearance: 0.5rem;

  /* What a phone's on-screen keyboard takes, near enough: a little over a
     third of the screen, rounded up so the reserve errs on the generous side.
     It is only the opening position - search-suggest.js measures the real
     keyboard within a frame or two and that measurement takes over - and the
     value a browser that never reports a keyboard is left with. */
  --keyboard-reserve: 45vh;

  /* How far above the foot of the screen a full-screen sheet has to stop so
     that iOS Safari, tinting its bottom bar, reads the backdrop behind it
     rather than the sheet (docs/style-guide.md, "Safari colours its bottom bar
     from the page"). It is WebKit's geometry, not a design value, and stays in
     px: the sample point sits 4px above the foot and the pixels read are the 2px
     above that. It is out of sight behind the browser's bar. */
  --edge-sample-inset: 6px;

  /* The height of the field's own row, derived from what the field is made of
     rather than measured: the phone header holds this much space open while
     the field is away being the search drawer, so the page behind does not
     jump. */
  --search-field-height: calc(var(--font-size-md) * var(--line-height-normal) + 2 * var(--space-2xs) + var(--stroke-hairline));

  /* A dropdown that ends flush on its last visible option looks like the whole
     list, so a long one is never scrolled. The cap is therefore a HALF option:
     the cut row is the affordance. Derived from the option's own metrics rather
     than measured, so the two cannot drift apart. */
  --listbox-option-height: calc(var(--font-size-xs) * var(--line-height-normal) + 2 * var(--space-2xs)); /* 37 */
  --listbox-visible-options: 6.5;
  --card-home-width: 24.1875rem;   /* 387 */
  --card-catalog-width: 17.8125rem; /* 285 */
  --filter-column-width: 17.8125rem; /* 285 */
  --tile-height: 16.125rem;     /* 258 */
  --tile-height-compact: 10rem; /* 160: phone, two tiles per row */
  --account-panel-max: 64rem;   /* 1024 */
  --technical-form-max: 52rem;  /* 832 */
  --checkout-sidebar-width: 22rem; /* 352 */
  --order-thumbnail-width: 5rem; /* 80 */
  --admin-bar-height: 2.75rem;  /* 44 */
  --account-action-width: 11.25rem; /* 180 */

  /* Book covers: width is fluid, height is derived from the aspect ratio, so a
     cover can never again be 253px wide inside a 390px viewport. The ratios are
     the Figma boxes (home 253x344, category 205x260). */
  --card-cover-width: clamp(9.375rem, 6.984rem + 9.81vw, 15.8125rem);  /* 150 -> 253 */
  --card-cover-aspect: 253 / 344;
  --card-cover-catalog-width: clamp(8.125rem, 6.384rem + 7.14vw, 12.8125rem); /* 130 -> 205 */
  --card-cover-catalog-aspect: 205 / 260;
  /* The badge's top edge sits on the home cover's middle line, clear of the
     title printed at the top of a real cover. That is where it showed on a
     laptop when this was 31.7% (Figma's 109/344, meant for the cover) of the
     whole card; derived from the cover's own size now, so it no longer drifts
     down the cover when a row's titles make the card taller. */
  --card-badge-offset: calc(var(--card-cover-width) * 344 / 253 / 2);
  /* The same line on the catalog's cover, for a compact card that keeps the
     home card's overhang (the desktop home offers). */
  --card-badge-offset-catalog: calc(var(--card-cover-catalog-width) * 260 / 205 / 2);
  /* A phone's cards stand in one sideways row stretched to a common height,
     so there the original share of the card cannot drift and is kept. */
  --card-badge-offset-compact: 31.7%;

  /* Product gallery: capped against the viewport so the cover can never own the
     whole fold. 55vh leaves room for title, price and the buy action. */
  --gallery-max-height: min(38.75rem, 55vh);
  --gallery-cover-height-compact: min(11.25rem, 28vh); /* 180: phone cover small enough that price and add to cart stay above the fold */
  --gallery-thumb-height: 4rem; /* 64: a photo switcher readable as a cover, well under the main one */

  /* Cart line-item cover: small enough that a row is a row, not a card. */
  --cart-cover-width: clamp(4.5rem, 3.943rem + 2.29vw, 6rem); /* 72 -> 96 */
  --cart-cover-aspect: 3 / 4;
  /* Two digits beside the stepper buttons, in the field's own type (ch resolves
     where the token is used), so the width follows --font-size-field. */
  --cart-quantity-input-width: calc(2ch + 2 * var(--space-xs) + 2 * var(--stroke-hairline));
  --cart-quantity-width: 9.5rem;
  --cart-price-width: 7.5rem;
  --dialog-max-width: 30rem;
  --dialog-wide-max-width: 48rem;
  --shipping-options-min-width: 38rem;
  --toast-max-width: 28rem;
  --info-note-max-width: 20rem; /* an info bubble's note (approximate price, puzzle notation): a short paragraph, never the row's width */
  --toast-z: 120;
  --dialog-z: 130;   /* above the toast: a jQuery UI dialog is a foreground interruption */

  /* Home hero and tiles (Figma node 3:3). The hero is a band lower than the
     category tile under it. Its two covers overlap, the back one higher, and
     stand on its bottom edge, which cuts a quarter of the front one off as the
     tiles' edges cut theirs; small ornaments frame the pair (see "Figma
     source-of-truth deviations" in CLAUDE.md). */
  --hero-height: 11.5rem;              /* 184: about seven tenths of --tile-height */
  --hero-height-medium: 11rem;         /* 176: tablet */
  --hero-cover-width: 8.5rem;          /* 136: a 185px cover, larger than a tile's */
  --hero-cover-width-medium: 7rem;     /* 112 */
  --hero-cover-width-compact: 4rem;    /* 64: phone, the pair in a row under the copy */
  --hero-cover-aspect: 203 / 276;      /* the Figma cover box */
  /* The pair's geometry, in cover widths. hero-cover-ornaments.svg is drawn
     around exactly this pair: its box starts 60/136 of a cover left of the
     front cover and spans 318/136 of one. A phone's frame
     (hero-cover-ornaments-compact.svg) is only a strip on the pair's right,
     as high as the pair, spanning 123/64 of a cover from the front one's left
     edge; the rest of its ornaments are scattered beside the action
     (hero-ornaments-scattered.svg). */
  --hero-cover-overlap: 0.3;
  --hero-cover-step: 0.18;
  --hero-ornaments-start: 0.441;
  --hero-ornaments-span: 2.338;
  --hero-ornaments-start-compact: 0;
  --hero-ornaments-span-compact: 1.9219;
  --hero-copy-inset: 4.375rem;         /* 70: Figma x=190, the copy's inset past the arrow */
  --hero-art-inset: 10rem;             /* 160: centres the pair and its ornaments in the band's right half */
  --hero-copy-max-width: 26rem;        /* 416: the description's measure */
  --hero-action-mark-size: max(1.75rem, 28px); /* 28: the amber circle behind the action's arrow */
  --hero-action-arrow-size: max(1rem, 16px);
  --hero-action-nudge: 0.25rem;        /* 4: the arrow's move on hover */
  --hero-nav-size: var(--tap-target);
  --hero-nav-visual-size: max(2.25rem, 36px); /* 36 inside the 44px accessible target */
  --hero-nav-offset: var(--space-3xs);
  --hero-dot-size: max(0.625rem, 10px);
  --hero-layer-content: 1;
  --home-tile-cover-width-max: 5.9375rem; /* 95: the Figma cover */
  --home-tile-cover-width-min: 4rem;      /* 64: phone, two tiles per row */
  --home-tile-cover-width: clamp(var(--home-tile-cover-width-min), 3.1rem + 3.69vw, var(--home-tile-cover-width-max)); /* 64 -> 95 */
  --home-tile-cover-height: 9.375rem;     /* 150: the Figma cover, at every width */
  --home-link-tile-min-width: 10rem;      /* 160: "All categories" / "All books" */
  --cover-hover-lift: 0.75rem;            /* 12: covers rise out of a category tile, or the hero, on hover */

  /* Chess puzzles page. The diagram column is as wide as the space allows,
     capped at the max, and shrinks with a short viewport; it never drops
     below the min, where the pieces would stop being legible. The chrome is
     what the viewport keeps around the board: the header, the source, the
     caption and the page padding, plus room to spare, so on a laptop the
     board stays in proportion with the column beside it, whose book must
     reach the first screen too (the product owner preferred it at this size
     to one filling the height). A phone's board simply takes the full width
     (puzzles.css). */
  --puzzle-board-max: 22rem; /* 352: 44px squares */
  --puzzle-board-min: 15rem; /* 240: 30px squares */
  --puzzle-board-phone: 18.5rem; /* 296: 37px squares, a phone's board */
  --puzzle-page-chrome: 30rem; /* 480 */
  --puzzle-side-width: 26rem; /* 416: the game's column beside the board on a desktop */
  /* The solution opens inside the bottom action bar below the desktop
     breakpoint, so a long one scrolls there instead of covering the board. */
  --puzzle-solution-max-height: 30svh;
  /* The playable board (board/puzzle-board.js) marks squares over their own
     colour, so the square still reads dark or light: the picked-up piece
     tinted in the action amber and the last move framed in it (its tint would
     turn a light square into a dark one's colour), a legal
     destination as a dot and a capture as a ring in soft ink, a king in check
     in the danger red, and a square under the pointer a shade darker. */
  --color-board-last-move: var(--color-action);
  --board-last-move-frame: max(0.1875rem, 3px); /* 3: the last move's frame, inside its squares */
  --color-board-selected: rgb(255 196 107 / 80%);
  --color-board-selected: color-mix(in srgb, var(--color-action) 80%, transparent);
  --color-board-target: rgb(17 17 17 / 22%);
  --color-board-target: color-mix(in srgb, var(--color-ink) 22%, transparent);
  --color-board-check: rgb(198 40 40 / 55%);
  --color-board-check: color-mix(in srgb, var(--color-danger) 55%, transparent);
  --color-board-hover: rgb(0 0 0 / 12%);
  --color-board-hover: color-mix(in srgb, #000 var(--hover-darken), transparent);
  --board-target-dot: 16%;    /* a destination dot's radius, of the half-diagonal (radial-gradient's own unit) */
  --board-capture-ring: 64%;  /* a capture ring's inner radius, of the half-diagonal: a thin ring just inside the edge */
  --puzzle-promotion-choice-size: 4rem; /* 64: a piece a pawn can become, large enough to tell apart at a glance */
  /* A control with nothing to do (the board's back at the diagram, forward
     at the last move) stays in place, faded, so the row does not jump. */
  --disabled-opacity: 0.4;
  /* The die glyph sits in running text (the header link) and in buttons, so it
     is sized from the type it stands beside. */
  --die-icon-size: 1.25em;
  /* The header's die: the glyph in an amber circle, the theme's icon action. */
  --die-mark-size: 1.625em;
}


/* Desktop density. The Figma desktop frame is 1440 wide and the fluid scale
   above reaches its full values there, so a 13-14" laptop (1440-1512 CSS px
   wide, about 780 tall in a browser) drew the frame 1:1 - an 80px header
   row, a 340px hero, 18px body copy - and the header and hero alone filled
   the first screen.

   A desktop with a mouse therefore draws the composition denser, through one
   factor on the root font size. Two kinds of size answer to it differently:

   - Composition - the container, columns, cards, tiles, covers, the hero,
     the header row, the logo, macro-spacing, headings - scales with it, so
     the page keeps the Figma proportions and reads the same, only denser.
   - Legibility - text, functional icons, micro-spacing, strokes, tap
     targets - is floored (the max() in each token above) at its phone size,
     or its own size where it is fixed: nothing a visitor reads or hits is
     smaller on a laptop than on a phone, and a 1px line stays one pixel.

   The factor is anchored on body copy (--font-size-md), held at the web's
   16px baseline across laptop widths:

     992-1440  root = 16px / body, body stays 16px (92% -> 89%, i.e. 8/9);
     1440-1920 grows back to the Figma size (body 16 -> 18px), where a
               monitor has the room for it;
     1920+     the Figma frame itself.

   Phones and touch tablets are untouched, and media queries are unaffected:
   their rem is the browser's initial size.
   992 -> 1440: 14.93 -> 14.22px, slope -0.158vw, intercept 16.497px (1.0311rem).
   1440 -> 1920: 14.22 -> 16px, slope 0.3704vw, intercept 8.889px (0.5556rem). */
@media (min-width: 62rem) and (hover: hover) and (pointer: fine) {
  :root {
    --root-font-size: clamp(0.8889rem, 1.0311rem - 0.158vw, 0.9331rem);
  }
}

@media (min-width: 90rem) and (hover: hover) and (pointer: fine) {
  :root {
    --root-font-size: clamp(0.8889rem, 0.5556rem + 0.3704vw, 1rem);
  }
}

/* iOS Safari zooms the page into any field that takes focus with text under
   16px, and leaves it zoomed after the keyboard goes. Touch screens therefore
   type at 16px; a mouse-driven desktop keeps the drawn 14. Not done through
   `maximum-scale` in the viewport meta: Android honours that and would take
   pinch-zoom away from visitors who need it. */
@media (pointer: coarse) {
  :root {
    --font-size-field: var(--font-size-base);
  }
}

/* The die's turn is the one motion a script waits on, so it is switched off
   here, in the token, not only by the blanket rule in base.css. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-dice: 0ms;
  }
}
