/* ============================================================
   css/responsive.css — the game-wide responsive system
   ------------------------------------------------------------
   Loaded last, after tailwind.css / styles.css / desktop.css, so it can
   set the shell contract without every screen having to opt in.

   WHY THIS FILE EXISTS
   Every screen in the game is rendered by js/ui/render.js with the same
   three-part skeleton:

       <div class="… min-h-screen main-gradient">   ← screen root
         <header> or renderHeader()                 ← fixed chrome
         <main>                                     ← content
         <footer> (optional)                        ← fixed chrome
       </div>

   That shared skeleton is the correct level to solve responsiveness at.
   Rather than 14 per-screen fixes, this file turns `.main-gradient` into a
   viewport-locked flex shell exactly once, and every screen — current or
   future — inherits it. The rules below are ordered:

     1. Viewport truth      (--app-h / --app-w / safe areas)
     2. Fluid scale         (space + type ramps, one set of tokens)
     3. Document lock       (html/body/#app never scroll)
     4. Screen shell        (the .main-gradient contract)
     5. Layout modes        (width AND height driven)
     6. Shared components   (cards, tables, controls, modals, overlays)
     7. Per-screen fitting  (only where a screen's content needs it)

   THE ONE RULE TO KEEP
   The page never scrolls; a *panel* scrolls. Any new screen gets this for
   free as long as its content lives inside <main>. If you find yourself
   adding `overflow: hidden` to hide something that is being clipped, the
   fix is upstream: something above it is not allowed to shrink (it needs
   `min-height: 0`), or it should be inside the scrolling panel.
   ============================================================ */

/* ============================================================
   1 · VIEWPORT TRUTH
   ============================================================ */
:root {
  /* Height/width of the box the user can actually see.
     Declaration order is the fallback chain — later wins where supported:
       px  … ancient browsers
       vh  … everything
       dvh … modern, follows the mobile URL bar
     js/utils/viewport.js then overrides --app-h/--app-w inline with the
     live visualViewport measurement, which is the only value that also
     accounts for the on-screen keyboard and pinch-zoom. */
  --app-h: 100vh;
  --app-w: 100vw;
  /* Zoom applied to the desktop canvas (css/desktop.css). 1 until
     js/utils/viewport.js measures the window; always 1 below 1024px. */
  --app-scale: 1;
  /* Height of the site-nav strip under the game (index.html .site-nav): a
     30px row plus the home-bar safe area it now clears instead of the shell.
     #app is --app-h minus this, the desktop canvas scales to the remainder
     (css/desktop.css, js/utils/viewport.js), and the strip is gone — and
     this is 0 — on viewports shorter than 500px (see below). */
  --site-nav-h: calc(30px + var(--safe-b));

  /* Safe areas — 0 everywhere that isn't a notched/rounded device. Read
     through variables so every consumer stays readable. */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
}

@supports (height: 100dvh) {
  :root {
    --app-h: 100dvh;
    --app-w: 100dvw;
  }
}

/* ============================================================
   2 · FLUID SCALE
   ------------------------------------------------------------
   One ramp, used everywhere, so intermediate sizes interpolate instead of
   jumping. Each clamp is `min` / preferred / `max`:
     - the min is the smallest value that is still legible/tappable at
       320x568, and it is what a genuinely tiny screen gets;
     - the max is the current desktop value, so wide screens are unchanged.
   Space ramps track BOTH axes (`vmin`) because a short landscape phone is
   as constrained as a narrow portrait one.
   ============================================================ */
:root {
  /* Spacing ramp */
  --sp-4:  clamp(3px,  0.8vmin,  6px);
  --sp-6:  clamp(4px,  1.1vmin,  8px);
  --sp-8:  clamp(5px,  1.5vmin, 10px);
  --sp-12: clamp(7px,  2.2vmin, 14px);
  --sp-16: clamp(9px,  2.8vmin, 18px);

  /* Gutter between the screen edge and content. Grows with width, and
     always clears the safe area on notched devices. */
  --gutter: clamp(10px, 2.5vw, 24px);
  --gutter-l: calc(var(--gutter) + var(--safe-l));
  --gutter-r: calc(var(--gutter) + var(--safe-r));

  /* Vertical rhythm inside a screen's content column. */
  --stack-gap: clamp(6px, 1.4vh, 16px);

  /* Type ramp — only applied to shared primitives (see §6). Screen-specific
     type keeps its existing sizes so the visual identity is untouched. */
  --fs-sm:  clamp(11px, 2vmin,   13px);

  /* Minimum comfortable hit target. Never scaled below this. */
  --tap: 34px;
}

/* ============================================================
   3 · DOCUMENT LOCK
   ------------------------------------------------------------
   Scoped to the game page only. css/styles.css already locks html/body for
   `<html data-page="game">`; this pins #app to the same measured height and
   — the actual change — stops #app being the page scroller. Screens now
   scroll a panel inside themselves instead of scrolling the whole app.
   Standalone content pages (daily.html, privacy.html) carry no data-page
   attribute and are deliberately untouched.
   ============================================================ */
html[data-page="game"],
html[data-page="game"] body {
  height: var(--app-h);
  max-height: var(--app-h);
  overflow: hidden;
  /* Kill the iOS rubber-band on the document itself. */
  overscroll-behavior: none;
}

html[data-page="game"] #app {
  height: calc(var(--app-h) - var(--site-nav-h, 0px));
  max-height: calc(var(--app-h) - var(--site-nav-h, 0px));
  overflow: hidden;
  overscroll-behavior: contain;
}

/* The site-nav strip sits under #app in normal flow and takes exactly the
   height carved out above, so the two together fill --app-h with nothing to
   scroll. A landscape phone (or any window under 500px tall) has no row to
   give: the strip hides, the variable zeroes, and the shell is as it was.
   The #about panel and the header's How-to-play pill still reach the guide
   and its links there. */
@media (max-height: 500px) {
  :root { --site-nav-h: 0px; }
  .site-nav { display: none; }
}

/* The strip belongs to the home screen (mode-select) only. Every other
   screen hides it and zeroes the variable on #app, so the shell (and the
   desktop canvas inside it) takes the row back. Keyed to [data-phase] so
   nothing changes before render() first stamps it. */
html[data-page="game"] #app[data-phase]:not([data-phase="mode-select"]) { --site-nav-h: 0px; }
#app[data-phase]:not([data-phase="mode-select"]) ~ .site-nav { display: none; }

/* The #about guide/FAQ is display:none until opened as a fixed overlay
   (html[data-about="open"]) — see .seo-content--crawlable in styles.css.
   Neither state adds page scroll to the locked shell, so nothing here. The
   1px clip-rect that used to live here made the guide invisible to every
   visitor, which is hidden text as far as Google is concerned. */

/* ============================================================
   4 · SCREEN SHELL — the contract every screen inherits
   ------------------------------------------------------------
   `.main-gradient` is on all 14 screen roots (mode select, more modes,
   legends, drafting, 1v1 drafting, results, playoffs, championship,
   eliminated, trophy room, series preview/sim/result). Turning it into the
   shell here is what makes the whole game fit one viewport at once.
   ============================================================ */
html[data-page="game"] #app > .main-gradient {
  /* Beats Tailwind's `min-h-screen` (min-height: 100vh), which would
     otherwise let a tall screen push past the fold. */
  min-height: 0;
  height: 100%;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Notched devices: keep chrome out from under the status bar. The home
     bar is cleared by the site-nav strip below #app, which carries --safe-b
     inside its own height (see --site-nav-h). */
  padding-top: var(--safe-t);
  padding-bottom: 0;
}
@media (max-height: 500px) {
  /* No strip: the shell clears the home bar itself again. */
  html[data-page="game"] #app > .main-gradient { padding-bottom: var(--safe-b); }
}
/* Same off the home screen, where the strip is hidden too. */
html[data-page="game"] #app[data-phase]:not([data-phase="mode-select"]) > .main-gradient { padding-bottom: var(--safe-b); }

/* Fixed chrome — never shrinks, never scrolls. */
html[data-page="game"] #app > .main-gradient > header,
html[data-page="game"] #app > .main-gradient > .app-header-wrap,
html[data-page="game"] #app > .main-gradient > footer {
  flex: 0 0 auto;
}

/* The one scrolling panel per screen. `min-height: 0` is load-bearing: without
   it a flex child refuses to shrink below its content and the overflow escapes
   the shell instead of scrolling inside it. */
html[data-page="game"] #app > .main-gradient > main {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-gutter: stable;
  padding-left: var(--gutter-l) !important;
  padding-right: var(--gutter-r) !important;
  padding-top: var(--sp-8) !important;
  padding-bottom: var(--sp-16) !important;
}

/* Screens that centre their content (championship, eliminated) use
   `justify-center`. Plain `center` cuts off the *top* of content taller than
   the box, with no way to scroll to it — `safe` centres only while it fits
   and falls back to start alignment when it doesn't. */
html[data-page="game"] #app > .main-gradient > main.justify-center {
  justify-content: safe center;
}

/* The content column inside <main>. Never let a long word or a wide table
   force the shell wider than the viewport. */
html[data-page="game"] #app > .main-gradient > main > * {
  min-width: 0;
  max-width: min(100%, var(--content-max, 42rem));
}

/* Content column width per layout mode — one variable, set in §5. */
:root { --content-max: 42rem; }

/* Fluid stack rhythm for the content column, replacing the fixed Tailwind
   `gap-*` on the shared skeleton only. */
html[data-page="game"] #app > .main-gradient > main > .flex.flex-col {
  gap: var(--stack-gap);
}

/* ============================================================
   5 · LAYOUT MODES
   ------------------------------------------------------------
   Eight deliberate modes, expressed as the two things that actually drive
   layout: available width and available height. They share one token set,
   so moving between them interpolates rather than snapping between
   unrelated designs. Structural breakpoints appear only where content has
   to reorganise.
   ============================================================ */

/* — Extra-small / short mobile (320x568 and up) —
   Everything decorative yields before anything functional does. */
@media (max-width: 420px) {
  :root {
    --content-max: 100%;
    --stack-gap: clamp(5px, 1.1vh, 10px);
  }
}

/* — Mobile portrait — */
@media (min-width: 421px) and (max-width: 639px) {
  :root { --content-max: 100%; }
}

/* — Tablet portrait — */
@media (min-width: 640px) and (max-width: 1023px) {
  :root { --content-max: 44rem; }
}

/* — Tablet landscape / small laptop — */
@media (min-width: 1024px) and (max-width: 1365px) {
  :root { --content-max: 60rem; }
}

/* — Standard desktop — */
@media (min-width: 1366px) and (max-width: 1919px) {
  :root { --content-max: 72rem; }
}

/* — Large desktop — */
@media (min-width: 1920px) {
  :root { --content-max: 84rem; }
}

/* — Short viewports (laptops at 768px tall, mobile landscape, a windowed
     desktop). Height is the scarce axis here, so the vertical ramps
     compress while type and hit targets hold. — */
/* Height bands below are phone/tablet only: from 1024px the desktop canvas
   is scaled to fit the window (css/desktop.css), so a short desktop window
   must not also compress the design it is about to scale. */
@media (max-width: 1023px) and (max-height: 780px) {
  :root {
    --stack-gap: clamp(5px, 1vh, 11px);
    --sp-16: clamp(7px, 2vmin, 13px);
  }
}

@media (max-width: 1023px) and (max-height: 620px) {
  :root {
    --stack-gap: clamp(4px, 0.8vh, 8px);
    --sp-12: clamp(5px, 1.6vmin, 9px);
    --sp-16: clamp(6px, 1.6vmin, 10px);
  }
}

/* — Mobile landscape: wide but very short. The header is the biggest fixed
     cost on screen, so it is the first thing to compress. — */
@media (max-width: 1023px) and (max-height: 500px) and (orientation: landscape) {
  html[data-page="game"] #app > .main-gradient > main {
    padding-top: var(--sp-4) !important;
    padding-bottom: var(--sp-6) !important;
  }
}

/* ============================================================
   6 · SHARED COMPONENTS
   ============================================================ */

/* ── Headers ──────────────────────────────────────────────
   The in-run header (`.app-header`) is `sticky` for the old scrolling page.
   Inside the shell it is ordinary flex chrome, so sticky is redundant — but
   harmless, and left alone so nothing shifts. What it does need is a fluid
   height and safe-area-aware side padding. (The home screen's header is
   laid out in css/styles.css.) */
html[data-page="game"] .app-header__inner {
  height: clamp(42px, 7.5vh, 56px);
  padding-left: var(--gutter-l);
  padding-right: var(--gutter-r);
}
@media (max-width: 1023px) and (max-height: 500px) and (orientation: landscape) {
  html[data-page="game"] .app-header__inner {
    height: clamp(36px, 9vh, 44px);
  }
}

/* The in-run header's action strip already scrolls horizontally below 640px
   (css/styles.css) so no control is ever unreachable. Below ~480px, though,
   the strip is wider than the space by more than a pill, and a bisected pill
   sitting against the wordmark reads as broken rather than scrollable.

   The coach pill is the one item that can give ground: it is the widest,
   it already has `text-overflow: ellipsis`, and the same coach is named in
   full on the draft screen's own chip. Letting it shrink usually removes
   the overflow entirely at 320px instead of hiding a control. */
@media (max-width: 480px) {
  html[data-page="game"] .app-header__actions > .header-pill--muted {
    flex-shrink: 1;
    min-width: 3.5rem;
  }
}

/* If the strip still overflows (long coach + era names), fade its leading
   edge so the cut is legible as "there is more this way" rather than as a
   clipped element. Purely an affordance — nothing is hidden, and the strip
   scrolls exactly as before. */
@media (max-width: 639px) {
  html[data-page="game"] .app-header__actions {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 18px);
    mask-image: linear-gradient(to right, transparent 0, #000 18px);
  }
}

/* ── Footer ───────────────────────────────────────────────
   6px legal text. It must not consume height it doesn't need, and it must
   clear the home indicator. */
html[data-page="game"] #app > .main-gradient > footer {
  padding-left: var(--gutter-l);
  padding-right: var(--gutter-r);
}

/* ── Tables and lists ─────────────────────────────────────
   A table that cannot fit its columns scrolls inside its own panel rather
   than widening the page. */
html[data-page="game"] table {
  width: 100%;
  max-width: 100%;
}

/* ── Controls ─────────────────────────────────────────────
   Buttons stay tappable no matter how short the screen gets. */
html[data-page="game"] button,
html[data-page="game"] [role="button"] {
  min-height: var(--tap);
}

/* Small pills and inline chips opt out — they are secondary controls sized
   by their own rules, and forcing 34px on them breaks the row they sit in.

   `.header-pill` covers the whole family deliberately, not just the icon
   variant. The header action strip mixes elements: the coach and era pills
   render as <span> when locked and <button> when interactive, the icons are
   <button>, and Restart is a <button>. A min-height that lands on only the
   <button>s made Restart 34px next to 23px pills between 640px and 1023px —
   one control taller than the rest of the row, which reads as misaligned.
   Height here belongs to the header's own rules (css/styles.css sizes the
   pills, css/desktop.css gives them a uniform 38px from 1024px up) so that
   every pill matches whatever its neighbours are, regardless of tag.

   It also keeps them inside `.app-header__actions`, which is an
   `overflow-y: hidden` scroll strip below 640px — anything taller than the
   strip gets clipped rather than overflowing. */
html[data-page="game"] .header-pill,
html[data-page="game"] .theme-toggle,
html[data-page="game"] .draft-card-btn,
html[data-page="game"] .quick-tile,
html[data-page="game"] button.daily-community-copy {
  min-height: 0;
}

/* The menu footer is the opposite case. Its guide toggle is a <button> and
   its two page links are <a>, so the floor above lands on one of the three
   and would leave the row with a single tall item in it. Pin all three to
   the same hit height instead: the row becomes one uniform strip of
   properly sized targets, and it stays that way if --tap is ever retuned.
   Specificity beats the generic button rule above, so the order here does
   not matter. */
html[data-page="game"] .site-footer__link {
  min-height: var(--tap);
}

/* ── Readability floor ────────────────────────────────────
   One piece of type in the game renders below 8px, where text stops being
   readable for most people: the "GRADE" caption inside the results grade
   badge (7px). It is not caused by the fluid ramps — it is that size at
   every viewport — but "keep text readable" is a requirement of this work,
   so the floor is enforced here rather than left to that screen. It stays
   deliberately recessive; this raises it just to the readable threshold, it
   does not restyle it.

   The menu footer used to be the other case: a 6px legal line that this
   rule lifted to 9px. It did so with `footer p, footer a`, which also
   capped the readable link row beside it at 9px. The legal line now lives
   in the Terms of use section of privacy.html, and the links are sized as
   links in css/styles.css (.site-footer), so the footer no longer has any
   type for this floor to catch. */
html[data-page="game"] .grade-badge__label {
  font-size: 8px;
}

/* Inputs must never trigger iOS's zoom-on-focus, which resizes the visual
   viewport and fights the shell. 16px is the threshold. */
html[data-page="game"] input,
html[data-page="game"] select,
html[data-page="game"] textarea {
  font-size: max(16px, var(--fs-sm));
  max-width: 100%;
}

/* ── Modals ───────────────────────────────────────────────
   Six modals (leaderboard, global board + team detail, daily board, daily
   stats, team report) are built as inline-styled strings in
   js/utils/storage.js and js/ui/render.js. They all carry these two classes
   so their sizing lives here instead of being duplicated six times.

   The panel is capped against the *measured* viewport, so it stays inside
   the screen when the mobile URL bar is showing and when the keyboard is
   open — `90vh` alone overflows in both cases. */
html[data-page="game"] .app-modal-backdrop,
.app-modal-backdrop {
  padding: calc(var(--sp-12) + var(--safe-t))
           calc(var(--sp-12) + var(--safe-r))
           calc(var(--sp-12) + var(--safe-b))
           calc(var(--sp-12) + var(--safe-l)) !important;
  overflow: hidden;
}

.app-modal-panel {
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  min-width: 0;
}

/* ── Anchored overlays (era picker, coach picker) ─────────
   Dropdowns are `position: fixed`, so they escape the shell's overflow — but
   they can still run off the bottom on a short screen. Cap them and let the
   list scroll. */
html[data-page="game"] .era-picker-anchor {
  max-height: calc(var(--app-h) - 4.5rem - var(--safe-t) - var(--safe-b));
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ── Images / media ───────────────────────────────────────
   Nothing in the game is a bitmap that must hold a ratio, but any image
   added later inherits sane behaviour instead of blowing out a column. */
html[data-page="game"] img,
html[data-page="game"] svg {
  max-width: 100%;
}

/* ============================================================
   7 · PER-SCREEN FITTING
   ------------------------------------------------------------
   The shell above already guarantees "no page scroll, chrome always
   visible". These rules go one step further on the screens whose content
   would otherwise need scrolling at common sizes, by compressing spacing
   (never content) until it fits.

   Two screens are deliberately absent: the draft screen and the results
   screen already own bespoke one-viewport systems (css/styles.css for
   <=639px, css/desktop.css for >=1024px). §7 only fills the tablet gap
   between them.
   ============================================================ */

/* ── Height-driven chrome shedding, at every width ────────
   The cold-open welcome banner is one-time onboarding worth ~5rem. Both the
   phone layout (<=639px, css/styles.css) and the desktop workspace
   (>=1024px at <=900px tall, css/desktop.css) already drop it when height is
   scarce — but neither covers 640–1023px, so a landscape tablet or a short
   window spent a quarter of its viewport on it.

   Height is the thing that actually decides this, not width, so the rule
   belongs here. A tall tablet (768x1024) keeps the banner; a short one
   (844x390) does not. Phones (<=639px) are left to css/styles.css, which
   hides the banner outright and brings back a compact, one-paragraph form
   from 700px of height — the sentence that tells a search engine what this
   page is, on the smartphone viewport Googlebot renders with. This rule used
   to cover phones too, with !important, and silently defeated that. */
@media (min-width: 640px) and (max-width: 1023px) and (max-height: 900px) {
  html[data-page="game"] .draft-cold-open { display: none !important; }
}

/* ── Drafting, tablet (640–1023px) ────────────────────────
   Below 640 the mobile one-screen layout owns this; from 1024 the desktop
   workspace does. In between, the screen was free to grow past the fold.
   Same approach as both neighbours: fixed chrome, board scrolls. */
@media (min-width: 640px) and (max-width: 1023px) {
  html[data-page="game"] .draft-screen__main {
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  html[data-page="game"] .draft-screen__inner {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    overscroll-behavior: contain;
    gap: var(--stack-gap) !important;
  }
  html[data-page="game"] .draft-board-wrap {
    flex: 0 0 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }
  html[data-page="game"] .draft-board-scroll {
    flex: 0 0 auto;
    height: clamp(8rem, 30vh, 22rem);
    min-height: 8rem;
    max-height: none;
  }
  html[data-page="game"] .draft-slot-machine,
  html[data-page="game"] .draft-roster,
  html[data-page="game"] .draft-stat-gauges,
  html[data-page="game"] .draft-round-bar { flex: 0 0 auto; }

  /* The team/era spinner is the biggest fixed block on the screen. Both
     neighbouring layouts scale it with height (phone via fixed compaction,
     desktop via vh clamps); this band was still using the uncompacted
     default, which on a landscape tablet pushed the player cards under the
     fold. Same vh-clamp approach, so 640→1023 interpolates between them
     instead of stepping. */
  html[data-page="game"] .draft-slot-machine {
    padding: clamp(8px, 1.8vh, 16px) !important;
  }
  html[data-page="game"] .draft-slot-machine__grid {
    margin-bottom: clamp(6px, 1.5vh, 16px) !important;
  }
  html[data-page="game"] .draft-slot-machine__cell {
    min-height: clamp(46px, 13vh, 88px) !important;
    padding: clamp(6px, 1.6vh, 16px) 12px !important;
  }
  html[data-page="game"] .draft-slot-machine__cell .text-xl {
    font-size: clamp(17px, 3.6vh, 24px) !important;
  }
  html[data-page="game"] .draft-slot-machine [data-action="spin"],
  html[data-page="game"] .draft-slot-machine button[disabled] {
    padding-top: clamp(5px, 1.2vh, 12px) !important;
    padding-bottom: clamp(5px, 1.2vh, 12px) !important;
  }
  html[data-page="game"] .draft-roster-slot {
    min-height: clamp(52px, 12vh, 96px);
  }

  /* Short landscape. The round bar's secondary line is the one genuinely
     redundant item here — it repeats the phase already named in the bar
     itself — so it is the only thing dropped. Everything else compacts.

     At 390px tall the screen cannot show spinner + cards + roster at once.
     The board keeps its own height; the surrounding screen scrolls instead
     of shrinking the wrapper below its children and overlapping the gauges. */
  @media (max-height: 480px) {
    html[data-page="game"] .draft-round-bar__meta { display: none; }
  }
}

/* ── Playoff bracket ──────────────────────────────────────
   The bracket is the one genuinely wide element in the game: four columns
   with a 34rem floor, so below ~560px it cannot fit and must not try. It
   already scrolls inside `.playoff-bracket-wrap` (css/styles.css); this
   caps that wrapper to the column so the overflow stays a panel scroll and
   never becomes a document scroll, and stops the horizontal drag from
   chaining out to the shell. */
html[data-page="game"] .playoff-bracket-wrap {
  max-width: 100%;
  overscroll-behavior-x: contain;
}

/* ── Grids that must not force a minimum width ────────────
   Any grid child that contains long text needs `min-width: 0` or the grid
   track refuses to shrink and pushes the page wide. Applied to the shared
   grid containers rather than hunting individual cards. */
html[data-page="game"] .grid > *,
html[data-page="game"] .flex > * {
  min-width: 0;
}

/* That blanket rule is right for layout but wrong for the handful of
   elements whose whole job is to not shrink (icon columns, fixed badges).
   Tailwind marks those with `flex-shrink-0`; honour it. */
html[data-page="game"] .flex > .flex-shrink-0,
html[data-page="game"] .grid > .flex-shrink-0 {
  min-width: auto;
}

/* ── Reduced motion ───────────────────────────────────────
   Resizing re-lays-out constantly; users who asked for less motion should
   not get transition churn from it. */
@media (prefers-reduced-motion: reduce) {
  html[data-page="game"] * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* …except a busy spinner, which is a status indicator rather than
     decoration. The blanket rule above collapsed it to a 0.01ms single
     iteration, so the boot screen showed a frozen partial circle with no
     sign that anything was loading. `prefers-reduced-motion` is aimed at
     vestibular triggers — large, parallax and zooming motion — and both
     WCAG and the spec exempt animation that carries essential information.
     Kept slower than the default, which is the same accommodation
     Bootstrap and GOV.UK make for their spinners. */
  html[data-page="game"] .app-spinner {
    animation-duration: 1.5s !important;
    animation-iteration-count: infinite !important;
  }
  /* …and the draft board's rolling reel (css/styles.css .slot-reel__strip),
     for the same reason. Frozen by the blanket rule, the spin showed two
     static names for ~0.8s and then snapped to the result — a Windows PC
     with "Animation effects" off, "best performance" or a Remote Desktop
     session reports reduced motion, so the core spin looked broken on
     desktops that never asked for it. The roll is contained to a one-line
     frame; the "?" ring rotation and the landing pop stay off. */
  html[data-page="game"] .slot-reel__strip {
    animation-duration: 0.42s !important;
    animation-iteration-count: infinite !important;
  }
}

/* Phone cards keep readable text and a full finger-sized draft action. */
@media (max-width: 639px) {
  html[data-page="game"] .draft-card-btn { min-height: 44px; }
}
