/* Layout tokens every stylesheet can use (#72, "Layout rules"): breakpoints, the text scale,
   safe areas and touch sizes. Loaded before app.css and shell.css. README "Layout tokens".

   BREAKPOINTS. CSS variables cannot go inside @media, so copy these queries as they are.
   js/layout.ts also writes the current one on the page root as data-bp="xs|s-land|m|l|tv"
   (use :root[data-bp='xs'] … when a rule must also follow TV mode, which no query can see).

     xs      @media (max-width: 599.98px) and (orientation: portrait)
     s-land  @media (max-height: 499.98px) and (orientation: landscape)
     m       @media (max-width: 1199.98px), (max-height: 799.98px)      (what xs / s-land leave)
     l       @media (min-width: 1200px) and (min-height: 800px)
     tv      @media (min-width: 1800px)   or   :root.tv   (TV mode: the setting, or a gamepad
                                                          was the last input and no mouse hovers)
     touch   @media (pointer: coarse) and (hover: none)   (also :root[data-touch])

   Order when several match: tv from 1800 px, then xs, then s-land, then TV mode, then l, else m:
   TV mode has no effect at a phone's size (no "tv" class there either).

   ROOM AT THIS TEXT SIZE. Breakpoints do not see the text-size setting, so js/layout.ts also
   writes data-fit on the page root: the width in lines of text (root ems), so text up to 200 %
   reflows instead of scrolling sideways. Use it for menu layout:
     :root[data-fit='narrow']   under 36 ems wide: one column, labels above their buttons
     :root[data-fit='medium']   36–50 ems: one column for two-column grids
     :root[data-fit='wide']     50 ems and more
   And size grid tracks with min(100%, …): repeat(auto-fill, minmax(min(100%, 13em), 1fr)).

   TEXT. The page's font size is var(--fs-base) × the text-size setting (--scale, 90–200 %).
     --fs-base   the breakpoint's base size (tv 28 px at 1080p, l 20, m 18, xs and s-land 17)
     --fs-min    the breakpoint's smallest label (tv 24, l 16, m 15, xs 14)
     --fs-small  --fs-min × the text-size setting: the floor for every small label. Write
                 font-size: max(var(--fs-small), 0.8em), never a bare 0.75em of a small base.
     --fs-hint   button hints (the smallest label size: 24 px on a TV at 100 %)
     --line      the page's line height: normal (the font's own) in English, 1.6 in Arabic, whose letters
                 reach higher and lower (#84; set on :root:lang(ar), so ?lang=qps-rtl has it too)
     --line-k    what a rule that sets its own line height multiplies it by: 1 in English, 1.2 in
                 Arabic (line-height: calc(1.15 * var(--line-k))), so a tight label stays tight and a
                 loose paragraph loose, each with room for Arabic letters (#84)
   TV text follows the screen height (2.593vh is 28 px at 1080p, 56 px on a 4K TV at 100 %
   zoom) so a 720p TV in TV mode shows the same layout, never below 18 px.

   SAFE AREAS. Keep HUD boxes and buttons inside these (env(safe-area-inset-*) on phones with a
   notch, rounded corners or a home bar; 5 % of the screen on a TV, against overscan).
     --safe-top, --safe-right, --safe-bottom, --safe-left
     --safe-start, --safe-end   the left and right ones where a line starts and ends (swapped
                 right to left, #84), for padding-inline and inset-inline

   TOUCH. --touch-min (56 px buttons), --touch-gap (12 px between them), --touch-menu (44 px: the
   smallest thing to tap in a menu on a touch screen, #271; shell.css applies it to all of them).

   COLOURS (#403; A · Console, docs/plans/console-redesign.md 3.1): dark, quiet, one yellow for
   "do this". Every colour the shell's stylesheets use is one of these (tools/css/css.test.ts fails
   on a colour written anywhere else); the phone page has the same values in
   controller/src/tokens.css (the same test keeps them equal). The 3D worlds keep their own
   background (#24212f, set by each game's view), and each game's own CSS its game colours.
     --bg          the page, dialogs, sheets (#07070b)
     --ui          a panel over a picture or the 3D world: --bg at 92 % (opaque in high contrast)
     --ui-card     list rows, cards on a panel          --ui-raised  inputs, quiet tiles, badges
     --ui-key      key caps, the code tag               --ui-quiet   a quiet button's fill
     --ui-key-edge a key cap's light outline (#509: a dark edge on a dark button could not be seen)
     --ui-text     text                                 --ui-dim     second lines, row headings
     --ui-faint    notes, "who plays it"                --ui-seat    a dashed empty seat
     --ui-line     a rare divider or a panel's edge
     --do / --on-do   the one "do this" per screen (Play, Start, Join, Next) and its text; yellow
                   text only for a meta line and the host's ★. Never a player's colour on a fill.
                   --do-end: the Tournament tile's fill runs from --do to it (Home's one exception).
     --focus       the focus ring: white, with a --bg gap (never yellow)
     --warn        a destructive step (Quit, Leave), a clock running out
   Beyond 3.1's table, the colours the shell already gave a meaning:
     --ink         dark text on a light fill (a picked chip, a player's colour)
     --ok / --on-ok / --ok-16   ready, connected, done (and its tint)
     --notice      an away player, a locked room, a link on its way
     --scrim       behind a dialog; under a label in the world
     --shadow      every shadow                  --edge   a label's outline in the world
     --pad-a … --pad-y, --on-pad-a …   the pad's face buttons on their badges (Ⓐ green, Ⓑ red…)
   A PLAYER'S COLOUR is --c, and beside it, written by the same code (C# Palette.Vars, TS
   playerColour in js/player-colour.ts, which the phone page uses too), --c-16 and --c-60: the
   colour at 16 % and 60 %, for fills and edges (no color-mix(): TV browsers before Chromium 111
   drop it).

   TYPE (3.2): --fs-display (Home's game title, results' "wins!": an element with class fs-display
   that sets --display-box, its box's width, and --display-len, the letters of its longest word,
   gets the size that fits), --fs-title, --fs-score, --fs-heading, --fs-label; never under --fs-small.

   SHAPES, FADES, MOTION (3.3): --r-l, --r-m, --r-s, --r-pill; --badge, --tile, --tile-on, --hero-w;
   --fade-dir (90deg, 270deg right to left), --fade-side, --fade-foot, --fade-top, --fade-board (the
   results over the board), --fade-top-strong (under Home's top bar); --t-move (0
   under reduced motion); --do-glow, the one yellow button's static glow.

   THE FLOOR: Chromium 105 (TV browsers). No color-mix(), oklch(), CSS nesting or unprefixed
   mask-image; no backdrop-filter or filter: blur() (tools/css/css.test.ts). */

:root {
  --scale: 1;

  /* l: from 1200 wide and 800 tall */
  --fs-base: 20px;
  --fs-min: 16px;

  --env-top: env(safe-area-inset-top, 0px);
  --env-right: env(safe-area-inset-right, 0px);
  --env-bottom: env(safe-area-inset-bottom, 0px);
  --env-left: env(safe-area-inset-left, 0px);
  --safe-top: var(--env-top);
  --safe-right: var(--env-right);
  --safe-bottom: var(--env-bottom);
  --safe-left: var(--env-left);

  --touch-min: 56px;
  --touch-gap: 12px;
  --touch-menu: 44px;
  --line: normal;
  --line-k: 1;

  /* Colours (3.1). */
  --bg: #07070b;
  --ui: rgb(7 7 11 / 0.92);
  --ui-card: #15151d;
  --ui-raised: #1b1b24;
  --ui-key: #26262f;
  --ui-key-edge: rgb(255 255 255 / 0.32);
  --ui-quiet: rgb(255 255 255 / 0.12);
  --ui-text: #f4f4f7;
  --ui-dim: #b8b8c6;
  --ui-faint: #9d9dae;
  --ui-seat: #6e6e80;
  --ui-line: rgb(255 255 255 / 0.1);
  --do: #ffd23f;
  --on-do: #111111;
  --focus: #ffffff;
  --warn: #ff8a8a;
  --ink: #111111;
  --ok: #3ddc84;
  --on-ok: #10261a;
  --ok-16: rgb(61 220 132 / 0.16);
  --notice: #ffb86b;
  --scrim: rgb(7 7 11 / 0.6);
  --shadow: rgb(0 0 0 / 0.45);
  --edge: #000000;
  --pad-a: #3ddc84;
  --on-pad-a: #10261a;
  --pad-b: #f2545b;
  --on-pad-b: #2a0f11;
  --pad-x: #3d8bfd;
  --on-pad-x: #0d1a33;
  --pad-y: #f7c548;
  --on-pad-y: #2b220a;
  --do-glow: 0 0.35em 1.4em rgb(255 210 63 / 0.35);
  /* The Tournament tile's fill runs from --do to it (plan 2.5: the one exception to one yellow). */
  --do-end: #ff7a59;
  color-scheme: dark;

  /* Type (3.2): tied to --fs-base, so they do not balloon at 200 %, never under the body text. */
  --fs-title: max(1.3rem, calc(var(--fs-base) * 2.1));
  --fs-score: max(1.2rem, calc(var(--fs-base) * 1.8));
  --fs-heading: max(1.1rem, calc(var(--fs-base) * 1.5));
  --fs-label: max(var(--fs-small), 0.95rem);
  --display-box: 90vw;
  --display-len: 8;

  /* Shapes, sizes, motion (3.3). */
  --r-l: calc(var(--fs-base) * 1.15);
  --r-m: calc(var(--fs-base) * 0.7);
  --r-s: calc(var(--fs-base) * 0.4);
  --r-pill: 999px;
  --badge: calc(var(--fs-base) * 3.1);
  --tile: calc(var(--fs-base) * 6.3);
  --tile-on: calc(var(--fs-base) * 8);
  --hero-w: min(48vw, 34em);
  --t-move: 320ms;
  /* The focus ring: 0.18 em of white, 0.1 em off the box (at least 3 and 2 px). */
  --ring: max(3px, 0.18em) solid var(--focus);
  --ring-gap: max(2px, 0.1em);
}

/* The display size fits its box (3.2): worked out on the element that sets --display-box and
   --display-len (a custom property's var() is resolved where it is declared, so not on :root). */
.fs-display {
  --fs-display: max(
    1rem,
    min(calc(var(--fs-base) * 5.4), calc(var(--display-box) / (var(--display-len) * 0.62)))
  );
  font-size: var(--fs-display);
  line-height: calc(1 * var(--line-k));
}

/* High contrast (3.1): text all --ui-text, panels opaque, edges white, no glow. */
:root.high-contrast {
  --ui: var(--bg);
  --ui-dim: var(--ui-text);
  --ui-faint: var(--ui-text);
  --ui-line: rgb(255 255 255 / 0.6);
  --ui-key-edge: var(--ui-text);
  --do-glow: none;
}

:root.reduced-motion {
  --t-move: 0ms;
}

/* m: 600–1200 wide, or 500–800 tall */
@media (max-width: 1199.98px), (max-height: 799.98px) {
  :root {
    --fs-base: 18px;
    --fs-min: 15px;
  }
}

/* xs (a phone held upright) and s-land (a phone on its side) */
@media (max-width: 599.98px) and (orientation: portrait),
  (max-height: 499.98px) and (orientation: landscape) {
  :root {
    --fs-base: 17px;
    --fs-min: 14px;
  }
}

/* tv: from 1800 wide, or TV mode */
@media (min-width: 1800px) {
  :root {
    --fs-base: clamp(18px, 2.593vh, 200px);
    --fs-min: calc(var(--fs-base) * 24 / 28);
    --safe-top: max(var(--env-top), 5vh);
    --safe-right: max(var(--env-right), 5vw);
    --safe-bottom: max(var(--env-bottom), 5vh);
    --safe-left: max(var(--env-left), 5vw);
  }
}

:root:lang(ar) {
  --line: 1.6;
  --line-k: 1.2;
}

:root.tv {
  --fs-base: clamp(18px, 2.593vh, 200px);
  --fs-min: calc(var(--fs-base) * 24 / 28);
  --safe-top: max(var(--env-top), 5vh);
  --safe-right: max(var(--env-right), 5vw);
  --safe-bottom: max(var(--env-bottom), 5vh);
  --safe-left: max(var(--env-left), 5vw);
}

:root,
[dir='ltr'] {
  --safe-start: var(--safe-left);
  --safe-end: var(--safe-right);
}

:root[dir='rtl'] {
  --safe-start: var(--safe-right);
  --safe-end: var(--safe-left);
}

:root {
  --fs-small: calc(var(--fs-min) * var(--scale));
  --fs-hint: var(--fs-small);
}

/* The fades (3.3): the dark band under the hero follows the hero's box, whatever the screen's shape.
   A custom property's var() is resolved where it is declared: an element that sets its own --hero-w
   or --hero-end (Home's backdrop, #406) has class fades, so the band is worked out there again. */
:root,
.fades {
  --fade-dir: 90deg;
  --hero-end: calc(var(--safe-start) + var(--hero-w) + 2rem);
  --fade-side: linear-gradient(
    var(--fade-dir),
    rgb(7 7 11 / 0.92) 0 var(--hero-end),
    rgb(7 7 11 / 0.2) calc(var(--hero-end) + 20vw),
    transparent calc(var(--hero-end) + 30vw)
  );
  --fade-foot: linear-gradient(0deg, #07070b 0%, rgb(7 7 11 / 0.7) 26%, transparent 45%);
  --fade-top: linear-gradient(180deg, rgb(7 7 11 / 0.6), transparent 18%);
  /* Over the board at the results (#412): it stays in sight, darkest under the winner's line and
     the buttons. */
  --fade-board: linear-gradient(
    180deg,
    rgb(7 7 11 / 0.9),
    rgb(7 7 11 / 0.8) 40%,
    rgb(7 7 11 / 0.86) 75%,
    rgb(7 7 11 / 0.96)
  );
  /* Under Home's top bar (#406): its round buttons are see-through, so over a bright picture
     (the saved tournament's four games) they need more than the plain top fade. */
  --fade-top-strong: linear-gradient(
    180deg,
    rgb(7 7 11 / 0.88),
    rgb(7 7 11 / 0.62) 12%,
    transparent 26%
  );
}

:root[dir='rtl'],
:root[dir='rtl'] .fades {
  --fade-dir: 270deg;
}
