/* app/mobile.css — the client's stylesheet (spec §4). The ONLY sheet the document loads besides
   /shared/theme.css and /shared/design-system.css. Sections, in order:
   1 tokens · 2 resets · 3 type/colour/layout utilities (3b the accessibility floor) · 4 controls · 5 sheet ·
   6 shell (the column, the grid) · 7 auth/pick · 8 title · 9a hub screens (locker, free agents) ·
   9b hub screens (market, leaderboard, front office, Sunday Live) · 10 match · 11 final · 12 hover parity ·
   16 Prime Time Arcade skin · 17 match hierarchy · 18 the guided tour.
   ONE ARRANGEMENT, always the phone's portrait one (designed at 390 px wide). The app lives in a centred column
   (`#col`, section 6): width min(100vw, 75vh), full height, scaled up with CSS `zoom` so its design width fills the
   column; the rest of the window is the decorative ground. Inside the column NOTHING reads the window: no `vw`/`vh`/`dvh`
   (a viewport unit is multiplied by `zoom` like any length — use `--cvw` / `--cvh`, 1 % of the column in local px) and
   no size `@media` (it would measure the window, not the column — use `@container col (…)`). Orientation never
   re-arranges anything: a touch phone held sideways gets the "turn your phone" veil (`#rotate`, end of section 6).
   The `@media` left are the user's own (prefers-reduced-motion, hover) and the veil's (landscape, under 500 px tall, touch). */

/* ==== 1 tokens (spec §4.1, A1–A4) — on body, never on :root (the shared contract) ==== */
body{
  --t-label:12px; --t-body:14px; --t-ui:16px; --t-head:20px; --t-title:26px; --t-num:34px;
  --t-led:28px; --t-led-lg:44px; --t-hero:clamp(32px, calc(11 * var(--cvw)), 48px);
  --lh-tight:1.1; --lh-ui:1.25; --lh-body:1.45; --ls-label:.10em;
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px;
  --gut:16px; --col:calc(100% - 2*var(--gut));
  --sat:env(safe-area-inset-top, 0px); --sab:env(safe-area-inset-bottom, 0px);
  --sal:env(safe-area-inset-left, 0px); --sar:env(safe-area-inset-right, 0px);
  --cvw:calc(var(--colw, 390px) / var(--z, 1) / 100); --cvh:calc(var(--colh, 844px) / var(--z, 1) / 100);   /* 1 % of the column, in its own px (section 6) */
  --ctl-sm:44px; --ctl-input:48px;
  --danger:var(--wp);
}
/* ==== 2 resets ==== */
button{font-size:inherit}              /* MS-06: 20 class-less buttons rendered at the UA 13.333px */
input,select,textarea{font:inherit}
/* The UA's `[hidden]{display:none}` is an ORIGIN below every author rule, so
   any class that sets `display` (`.btn` inline-flex, `.stack` flex, `.pnl`…)
   silently shows a `hidden` element. Settings' delete-confirm box is `.stack`
   and starts hidden; the arm button is a `.btn` and hides itself. Without
   this line neither works, with no error anywhere. */
[hidden]{display:none !important}

/* ==== 3 utilities ==== */
.t-label{font:700 var(--t-label)/var(--lh-tight) var(--ui);letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--dim)}
.t-body{font:600 var(--t-body)/var(--lh-body) var(--ui)}
.t-head{font:var(--t-head)/var(--lh-tight) var(--display);letter-spacing:.01em}
.t-wide{letter-spacing:.3em}
.c-gold{color:var(--gold)}.c-dim{color:var(--dim)}.c-danger{color:var(--danger)}
.tag.ok{background:var(--wt);color:var(--ink)}.tag.bad{background:var(--wr);color:var(--ink)}
.row{display:flex;align-items:center;gap:var(--s2)}
.stack{display:flex;flex-direction:column;gap:var(--s2)}
.gap-3{gap:var(--s3)}
.mt-2{margin-top:var(--s2)}
.grow{flex:1}
.pane{flex:1;min-height:0;display:flex;flex-direction:column;gap:var(--s2)}
.center{text-align:center}.right{text-align:right}.left{text-align:left}
/* FIXED numeric tracks, not `auto auto`: with `auto` each row sized its own
   PWR/CAP columns from its own digit count, so the PWR column started at a
   different x on every row of the lineup (measured on the locker: 4 distinct
   x positions across 24 rows). 44 px is the small-control tier and holds a
   three-digit number over its label — a FOUR-digit one ("+1,050") ellipsises to
   "+10…", which reads as a plausible number. That is why the final screen's
   drive rows (since replaced by the quarter rows, which declare their own
   tracks in section 11) stopped using these two tracks once four-digit drive
   finals became the normal case. The locker's lineup rows keep 44 / 44:
   their numbers are PWR and CAP, and no finding reports one past three digits.
   If one ever does, widening THIS rule moves every locker row, so it needs its
   own six-profile `gate-mobile.js --strict` run and a measured test. */
/* The button reset the lineup rows need — `all:unset` also zeroes box-shadow,
   which would kill the app-wide `:focus-visible` ring; this resets only the
   UA button defaults and leaves box-shadow/outline alone (accessibility
   review finding, carried over from the inline `ROW_RESET` string). */
/* state (A16) — the look is defined where each component is styled; these are the hooks */

/* A stat tile: a figure over its label, three to a row (deck counts, market counters, the bid sheet's floor / price / max). */
.stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s2);flex:none}
.stat{display:flex;flex-direction:column;gap:2px;min-width:0;padding:var(--s2) var(--s3);border-radius:var(--r-lg);background:var(--panel);box-shadow:inset 0 0 0 2px var(--ink)}
.stat small{font-size:var(--t-ui);color:var(--dim)}
.stat .t-label{color:var(--dim);overflow-wrap:anywhere}

/* ---- 3b the accessibility floor (spec §3.9 / §4.1): 12 px labels, 14 px prose — unscoped, the same at every breakpoint ---- */
.hint{font-size:var(--t-body);line-height:var(--lh-body)}
.lbl{letter-spacing:var(--ls-label)}                        /* /shared says .12em; the label role on this scale is .10em */
.chip{font-size:var(--t-label)}                             /* /shared: 11px */
/* A glyph that sits on a position hue (D.POSC) takes dark ink, never white: 5.5-9.5:1 on the five fills. */
.c-photo .ph{font-size:var(--t-label)}                       /* the card face's PHOTO placeholder — /shared floors it at 11 */

/* ==== 4 controls (A3) — override the shared .btn sizes; the skin (ink outline, drop, radius) is /shared's ==== */
/* `min-width` as well as `min-height`, and `flex-shrink:0`: a bare "✕" close
   button (mode picker, bid sheet, scouting) cleared the height floor already but
   measured 30.5 px wide, and an EXPLICIT min-width replaces a flex item's default
   `min-width:auto` — which is what stops nowrap button text shrinking below its
   own content width (Flexbox §7.1.4). Without `flex-shrink:0` a long sheet title
   beside a "CLOSE ✕" rendered it as "LOSE" at a forced 44.5 px. Both came from
   legacy.css's `a11y-floor` area, where they were portrait-only `!important`s;
   they are the tier's own metrics here, at every breakpoint. */
/* The SKIN as well as the size: nothing in /shared styles a text field beyond
   its placeholder and its focus ring, so every input in the client carried the same
   nine inline declarations. Screens that still set their own colours inline
   (the bid amount) keep winning on inline specificity.
   `height` as well as `min-height`: legacy.css's a11y-floor rule
   `body.port input[type]{min-height:44px}` (0,2,2) used to out-specify this one
   and pin a 16/13-padded field at its 46 px content height. That area is gone
   (Task 9) and this is the only tier left, but `height` stays — it is what makes
   the 48 a real number rather than a floor the padding can grow past. */
input[type],input:not([type]){height:var(--ctl-input);min-height:var(--ctl-input);padding:13px var(--s3);font:600 var(--t-ui)/var(--lh-ui) var(--ui);box-sizing:border-box;width:100%;color:var(--cream);background:var(--ink);border:2px solid var(--ink);border-radius:var(--r-md);outline:none;box-shadow:inset 0 0 0 2px var(--chalk-14)}
input:focus-visible{box-shadow:inset 0 0 0 2px var(--wt)}
/* THE PANEL EDGE (finish review item 7). The 4 px coloured left rail is gone from every panel:
   a `.pnl` is framed the match mock's way — a full 1 px hairline all round, cut by the chamfer
   at its top-right corner (the notch), `/shared`'s clip-path doing the cut. The hairline's
   colour is `--edge`: a quiet white by default, and the MEANING colour where the old rail
   carried one (the boss red, a Front Office tier, a coloured HOW TO PLAY step, a selected team,
   the mode sheet's RANKED/PRACTICE cards) — screens.js writes `--edge`, never a border.
   Padding: the rail + 12 used to put the content 16 px in; the hairline + 15 does the same, and
   the other three sides lose the hairline's pixel too. `.pnl.boss` is re-declared here because
   `/shared`'s `.pnl.boss{border-left-color}` (0,2,0) would out-rank a bare `.pnl` rule. */
/* A panel inside a panel is a subdivision: a quieter edge. `:where()` keeps this at (0,1,0),
   so every component that names its own `--edge` further down wins on order. */
/* `.tag.tap` is the SHARED tap-target tier for a chip that is really a
   button: the match HUD's COACHES/AUDIBLES chips and the locker / deck-view
   position filters. /shared floors it at 40 px and gives it no width floor at
   all, so the 2-letter filters (QB/RB/TE/OL) measured 42-44 wide. Moved here
   out of legacy.css's portrait-only block (area `match`, day-0 L741) so the
   floor holds at every column width; each family's SKIN stays with
   its own component (`.mchip` section 10, `#deckView .tag.tap` section 9a). */
.tag.tap{min-height:var(--ctl-sm);min-width:var(--ctl-sm);justify-content:center}
/* `<a class="btn">` is a real control on the Settings sheet (PRIVACY,
   SUPPORT — they open a document, so they must be links, not buttons);
   /shared only ever styled `.btn` for <button>, which has no underline. */
a.btn{text-decoration:none}
/* THE keyboard focus ring (A20 "pointer parity"). NOT in the A20 breakpoint:
   a ring is a KEYBOARD affordance, not a pointer one — a tablet with a
   Bluetooth keyboard needs it as much as a desktop does — and `:focus-visible`
   already keeps it off a tap (a pointer-driven focus never matches it).
   /shared ships its own ring as a two-layer `box-shadow` and kills the outline
   with `:is(button,[data-act],a,input,.tag.tap):focus-visible` — specificity
   (0,3,0). An unscoped restatement here would lose to it whatever the source
   order, so the two containers every control in this document lives in carry
   the rule instead: `#app` and `#overlay` take it to (1,2,0). The shadow ring
   is zeroed in the same breath so a focused control shows ONE ring, not a gold
   outline wrapped around a gold halo; /shared already drops a `.btn`'s inset
   bevel while focused, so nothing is lost that it kept. `.card` is a div today
   (the hand's cards are dragged, not tabbed) — it is in the list so a future
   `tabindex` inherits the ring rather than needing its own rule. */

/* Item 7 — drawn icons, one stroke (screens.js#icon*): sized by their control, never a glyph.
   A chevron follows its label; a tag's icon leads its text. */
.btn svg,.tag svg{flex:none;display:block}
.btn:not(.icon) svg{margin-left:var(--s1)}
.tag svg{display:inline-block;vertical-align:-2px;margin-right:3px}
/* the sound toggles (Settings and the match menu) */
.key.tg{justify-content:space-between;gap:var(--s3)}
.key.tg .tg-state{font-weight:700;letter-spacing:.06em;color:var(--dim)}
.key.tg[aria-checked="true"] .tg-state{color:var(--wt)}

/* a key whose request is in flight (game.js sets `data-busy` + aria-busy): dimmed and inert until it answers */
[data-busy]{opacity:.6;pointer-events:none}

/* ==== 5 sheet — one chrome for every overlay (COVERAGE.md §2, spec §4.7/A18) ====
   Before this, four widths, seven header sizes, five close-control shapes and
   three gaps across 17 renderers, all of it inline on each modal. The chrome
   is here and ONLY here now; a renderer contributes markup, never geometry. */
/* `.wrap` for the three sheets whose title IS the thing the sheet exists to
   show — a player's name (card info, a bid, a boss). Ellipsising "Christian
   McCaffrey" out of the sheet you opened to read it defeats the sheet. The
   header is `min-height:48`, not `height`, so a two-line title (`--t-sheet` 40, this modifier 80 % of it) simply
   grows it; `overflow-wrap:break-word` covers the one unbreakable surname the
   league eventually invents. Fixed titles (SETTINGS, PLAYBOOK, KICKOFF…) keep
   the one-line ellipsis — they are labels, and their length is ours. */
#overlay .sheet .scroll{overflow-y:auto;min-height:0;max-height:min(calc(52 * var(--cvh)), 480px);-webkit-overflow-scrolling:touch}
/* Both came over from the `sheet-modal` area of legacy.css (LEGACY-MAP §4 T7 —
   the keyframes, the rule that reads it and the reduced-motion kill travel
   together). */
/* THE document's reduced-motion query. The shared layer's
   own block (`shared/design-system.css`: every animation and transition a millisecond) covers the
   mockup's components — the Season screen's week cards included; this list is what that block cannot
   reach, because the rule below is declared LATER in this same file than the animation it kills and
   equal specificity then loses on order. Each entry takes one extra element to clear its own rule
   (an `!important` where a later rule would otherwise win on order). Every killed animation here fills `both` or is a
   pure opacity blink, so its resting state IS what the element should look like, and colour still
   carries each state. */
@media (prefers-reduced-motion: reduce){.pips i.cur,.wk-slide.now .wk-disc::after,.mcard:hover .pc[data-tier="hi"] .pc-f::after,.lot:hover .pc[data-tier="hi"] .pc-f::after,.match-root .playname span,.match-root .card.settled .in,.match-root .card[aria-pressed="true"] .in,.match-root .card[aria-pressed="true"] .pc-f::after,.match-root .cbox.mlt.hot,.match-root .cbox.mlt.hot::before,.match-root .rzv{animation:none!important}.lot.bump .lot-price .big{animation:none!important}.match-root .fx,.match-root .flash,.fin-root .fx,.reveal .fx,.reveal .flash{display:none}.match-root .ln,.match-root .yardpop{animation:none!important}}
/* the same, when the player turned "Reduce motion" on in Settings (shared/prefs.js) */
:root[data-motion="reduce"] .pips i.cur,:root[data-motion="reduce"] .wk-slide.now .wk-disc::after,:root[data-motion="reduce"] .mcard:hover .pc[data-tier="hi"] .pc-f::after,:root[data-motion="reduce"] .lot:hover .pc[data-tier="hi"] .pc-f::after,:root[data-motion="reduce"] .match-root .playname span,:root[data-motion="reduce"] .match-root .card.settled .in,:root[data-motion="reduce"] .match-root .card[aria-pressed="true"] .in,:root[data-motion="reduce"] .match-root .card[aria-pressed="true"] .pc-f::after,:root[data-motion="reduce"] .match-root .cbox.mlt.hot,:root[data-motion="reduce"] .match-root .cbox.mlt.hot::before,:root[data-motion="reduce"] .match-root .rzv{animation:none!important}:root[data-motion="reduce"] .lot.bump .lot-price .big{animation:none!important}:root[data-motion="reduce"] .match-root .fx,:root[data-motion="reduce"] .match-root .flash,:root[data-motion="reduce"] .fin-root .fx,:root[data-motion="reduce"] .reveal .fx,:root[data-motion="reduce"] .reveal .flash{display:none}:root[data-motion="reduce"] .match-root .ln,:root[data-motion="reduce"] .match-root .yardpop{animation:none!important}


/* ---- 5b what a sheet CONTAINS — the parts that are not chrome ----
   The 14 px prose floor for `.hint` used to be declared here, sheet-scoped;
   section 3b carries it for the whole document now (legacy.css's portrait-only
   `body.port .hint{font-size:14px!important}` was the other half of it).
   The pix cells of a `.rank` row inside a sheet were authored at 16/18/22px per
   renderer and are one UI size here. */



/* KICKOFF (mockup kickoffSheet, screens.js#modeOverlay): the sheet is the shared one — head, `.seg`, `.rowc`
   rows, `.acts`. Only what the mockup leaves to inline styles is a rule here: a label or a boss row
   over a list gets the gap the mockup wrote by hand, and the mode's gate line sits under the segment. */
.sheet>.lbl{margin-bottom:var(--s2)}
.sheet>.rowc.bossrow{margin-bottom:var(--s4)}
.sheet>.seg{margin-bottom:var(--s3)}
.sheet>.mode-gate p{margin-bottom:var(--s3)}
.sheet .starters .rowc .t{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The one deviation from the mockup's kickoff sheet, for short screens: the primary key is pinned to the
   sheet's foot, on the sheet's own ground — "Not now" follows it. */
/* The KICKOFF sheet is one screenful, no scroll: tighter head, rows and gaps than the other sheets (it is the only one with #modeErr) */
.sheet:has(#modeErr){max-height:94%;padding-top:var(--s4)}
.sheet:has(#modeErr) .rows{gap:var(--s1);margin-bottom:var(--s2)}
.sheet:has(#modeErr) .rowc{min-height:44px;padding-block:var(--s1)}
.sheet:has(#modeErr)>.lbl{margin-bottom:var(--s1)}
.sheet:has(#modeErr)>.rowc.bossrow{margin-bottom:var(--s2)}
.sheet:has(#modeErr)>.seg{margin-bottom:var(--s2)}
.sheet:has(#modeErr)>.note{line-height:1.4;margin-bottom:var(--s2)}
.sheet>.pin{position:sticky;bottom:0;margin:0 calc(-1 * var(--s4)) var(--s2);padding:var(--s3) var(--s4);background:var(--wg);box-shadow:0 -2px 0 var(--ink)}
.sheet .sg[disabled]{opacity:.5;cursor:default}
.sheet .err:not(:empty){display:block;margin-bottom:var(--s3)}
/* The Cut Day Gazette prints on cream paper (§4.7 / COVERAGE §3): `--dim`
   is scoped so every `.lbl`/`.t-label` on it reads against #F4F0E6 instead
   of failing contrast at #93A3BC, and a cut row is `34px 1fr` with its
   score/cap pair on a SECOND line — the four-column row wrapped and
   overlapped the row above at 362 px of content width (C-06). */

/* The row shape never shows the tile's card face, whatever a later `.mini` rule says. */
/* ---- the Cut Day report keeps the night sheet (the paper gazette of section 5b went with the navy skin) ---- */





 

/* v0.2 Task 7: the quiet week. A bare `.pnl` inside the gazette keeps the
   shared dark gradient under `.gazette .pnl{color:#101512}` — dark on dark.
   Every other panel in this sheet paints itself from the headline palette
   inline; this one is static, so it is a class. */

/* Two rows, two columns, declared: a 34 px position chip, the name beside it,
   and everything numeric on a second line under the name. Task 4 needed
   `!important` on all three declarations to beat legacy.css's
   `body.mob.port .rank{display:flex!important;flex-wrap:wrap!important;
   gap:4px 10px!important}` (area `ranks`), which was written to defeat the
   eight INLINE `grid-template-columns` the `.rank` call sites carried and
   could not tell this class apart from those. That area is GONE (Task 7), so
   the `!important`s are gone with it: `#overlay .sheet .rank.sheet-row`
   (1,2,1) out-specifies section 9b's own `.rank` (0,1,0) on its own. */

/* THE BOSS SHEET's scheme rows (D-52, app/screens.js#bossInfoSheet): the
   /shared `.threat` row — code chip · name + the server's sentence · strength —
   at the phone scale. The sentence is 14 px body copy (it is the rule the
   player reads), the name line a 14 px head; the code chip stays a 12 px
   label. The strength column (`.sev`: word + pips) hugs its content, so the
   sentence takes whatever the sheet has left. A resting scheme (`.is-off`) is
   one line — code, name, "off this week" — never a card with nothing in it. */


/* The recipe book (P4 Task 14, D12, RD59/RD64/RD65) — screens.js#recipeBookSheet, from the deck
   screen (with the respec) and read-only from the match. One group per size, one row per recipe:
   the name and its level on the first line, its cards and starting MULT under it, the meter to
   the next threshold across the foot (none at the cap, none while locked). Two tracks, not four
   fixed ones: a 358 px sheet on a 390 phone gives the name every pixel the LVL tag does not take. */








/* The respec block: the cost tag in gold (money), the two pickers as wrapping chips, the one
   gold CONFIRM. A chosen chip carries the gold inset every other toggle of the client uses. */






/* Settings (§4.6). The danger zone is a panel inside the sheet, not a
   different sheet — one tap to arm, one to confirm, both in view. */

/* Settings: a row is a name, one plain line, and its control; a switch is a 64 x 44 target around a
   56 x 32 track (the mock's `.sw`). */










/* ==== 6 shell — the column, then #app: a grid; #side holds the hub header + tab bar, #main the screen ====
   THE COLUMN (`#col`, viewport.js). Everything but the decorative ground (`#bg`, and swirl.js's `#lights`) lives in it:
   `#app`, `#cards`, `#top`, `#overlay`, `#fx`, the banner and the toast. It is a centred, full-height box, `--colw` wide
   on screen (min(window width, 3/4 of its height)), scaled by `zoom: --z` — a phone is 1, a wider column scales the
   390 px design up to 1.6x and the layout simply flows wider past that. Its LOCAL size is therefore
   `--colw / --z` by `--colh / --z`; viewport.js writes the three custom properties (px, px, unitless) and the defaults
   below are what the very first paint sees. Three consequences the rest of the sheet and the scripts live by:
   · `zoom` multiplies EVERY length of a descendant, viewport units included — so none is used in the column: `--cvw` / `--cvh`
     are 1 % of the column's local width / height, and a size query is `@container col`, not `@media`.
   · `contain:layout` (implied by `container-type:size`) makes `#col` the containing block of every `position:fixed`
     descendant: the sheets, the banner, the toast, the boss reveal and the tour all stay inside the column with no rule of
     their own. `contain:paint` clips what a shake or a flyer pushes past its edge.
   · layout reads (`clientWidth`, `offsetWidth`, computed styles) are LOCAL px; `getBoundingClientRect` and pointer
     coordinates are on-screen px — divide a rect's size by `--z` before using it as a CSS length (viewport.js#zoom, #toLocal). */
:root{--z:1;--colw:min(100vw, 75dvh);--colh:100dvh}
#bg{position:fixed;inset:0;width:100%;height:100%;z-index:0}
#col{position:fixed;top:0;left:50%;z-index:var(--z-app);width:calc(var(--colw) / var(--z));height:calc(var(--colh) / var(--z));margin-left:calc(var(--colw) / var(--z) / -2);zoom:var(--z);container:col / size;contain:paint;
  --sat:calc(env(safe-area-inset-top, 0px) / var(--z));--sab:calc(env(safe-area-inset-bottom, 0px) / var(--z));--sal:calc(env(safe-area-inset-left, 0px) / var(--z));--sar:calc(env(safe-area-inset-right, 0px) / var(--z))}
/* The screen: where the window is wider than the column (a desktop window, a landscape tablet) the column is drawn as a
   screen set into the ground — a thin chalk rim, a dark bezel, a deep drop shadow under it, and over the picture a faint
   glass sheen and an inner vignette (`#col::after`, above every layer, never hit-tested). A phone or a portrait tablet IS
   the column and shows none of it. Lengths are divided by --z, which `zoom` multiplies back: the same on-screen px at
   any zoom. */
@media (min-aspect-ratio: 3/4){
  #col{box-shadow:
    0 0 0 calc(2px / var(--z)) var(--chalk-28),
    0 0 0 calc(10px / var(--z)) var(--ink),
    0 0 0 calc(11px / var(--z)) var(--chalk-10),
    0 calc(30px / var(--z)) calc(90px / var(--z)) color-mix(in srgb,var(--ink) 65%,transparent),
    0 0 calc(180px / var(--z)) color-mix(in srgb,var(--ink) 45%,transparent)}
  #col::after{content:"";position:absolute;inset:0;z-index:100;pointer-events:none;
    background:linear-gradient(125deg,color-mix(in srgb,var(--cream) 5%,transparent) 0%,transparent 32%);
    box-shadow:inset 0 0 calc(60px / var(--z)) color-mix(in srgb,var(--ink) 38%,transparent)}
}
#cards{position:absolute;inset:0;z-index:var(--z-cards);pointer-events:none}
#top{position:absolute;inset:0;z-index:var(--z-top);pointer-events:none;overflow:hidden}
#fx{position:absolute;inset:0;z-index:var(--z-fx-page);pointer-events:none;overflow:hidden}

#app{position:absolute;inset:0;z-index:var(--z-app);display:grid;grid-template-columns:auto 1fr;grid-template-rows:auto minmax(0,1fr) auto;grid-template-areas:"hd hd" "side main" "nav nav";padding:var(--sat) var(--sar) var(--sab) var(--sal);box-sizing:border-box}
#app.noside{grid-template-columns:1fr;grid-template-rows:minmax(0,1fr);grid-template-areas:"main"}
#app.noside #side{display:none}
/* #side is the element every renderer fills (game.js#go, match.js#renderSide).
   On a hub screen it holds the header + tab bar and stops being a box, so its
   two children take the `hd` and `nav` areas directly. In a match it is empty
   (match.js#renderAll clears it) and takes the `side` column, which is 0 wide when it holds nothing. */
#side{grid-area:side;min-height:0;display:flex;flex-direction:column;overflow:auto;overflow-x:hidden;scrollbar-width:none}
#side:has(> header){display:contents}
#side:empty{display:none}
#side::-webkit-scrollbar{display:none}
/* The mockup's page chrome (shared/design-system.css: `.sub-hd`, `.sub-foot`, `.tabbar`, `.purse`) takes
   the grid's `hd` and `nav` areas: a tab screen's header is its title and the purse (`tabHead`), the
   Season screen's the same, Sunday Live's a back key before it
   (`subHead`); the footer is the tab bar. Only where they sit is this sheet's. */
#side>.sub-hd{grid-area:hd}
#side>.sub-foot{grid-area:nav}
#side>.sub-foot.home-tabs{padding-top:0;border-top:0;background:color-mix(in srgb,var(--ink) 94%,transparent)}
#main{grid-area:main;min-width:0;min-height:0;display:flex;flex-direction:column}
/* `display:flex;flex-direction:column` is load-bearing, not decoration: every
   screen that bounds a list to the viewport (#lockerRoot's ACTIVE/RESERVE
   `.scroll` panes, the market's list) does it with `flex:1;min-height:0` on a
   child, which only has anywhere to grow into if THIS box is a column flex
   container. Without it #lockerRoot grew to its content height (measured 1648
   px inside an 820 px viewport) and #screen scrolled the whole roster instead
   of each pane scrolling inside its own box. */
#screen{position:static;flex:1;min-height:0;display:flex;flex-direction:column;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;padding:var(--s4) var(--gut) var(--s6);gap:var(--s4);box-sizing:border-box}
/* The match screen draws to its own edges (match.js#renderTable's own margins)
   and is the one screen that must not scroll (PORTRAIT-SPEC.md §4.1). */
#app:has(#zone) #screen{padding:var(--s1);overflow:hidden}


/* THE VEIL — a phone held sideways (a touch device, landscape, shorter than the column can use: under 500 px) is too short
   for any arrangement of this app, and the app has none but the portrait one: it says so and waits. `#col` is only hidden,
   never unmounted or paused, so the screen, the match, the sheet that was open are all exactly as they were when the phone
   turns back. `(pointer:coarse)` is what makes it a phone: a short landscape DESKTOP window (devtools docked, a 1280x500
   browser, a mouse and a keyboard) is not told to turn anything — it gets the column, however small — and a landscape
   tablet or desktop window taller than 500 gets the column too. No script locks the orientation (fullscreen.js says why);
   game.js only writes the words into #rotate. */
#rotate{display:none;position:fixed;inset:0;z-index:100;flex-direction:column;align-items:center;justify-content:center;gap:var(--s3);padding:var(--s4) calc(var(--s4) + var(--sar)) var(--s4) calc(var(--s4) + var(--sal));box-sizing:border-box;text-align:center;background:var(--ink);color:var(--cream)}
#rotate .rt-ico{width:34px;height:56px;border:4px solid var(--gold);border-radius:var(--r-md);box-sizing:border-box;animation:rt-turn 2.4s var(--ease-out) infinite}
#rotate .rt-t{font:var(--t-title)/var(--lh-tight) var(--display);letter-spacing:.04em;text-transform:uppercase;color:var(--gold)}
#rotate .rt-s{font:700 var(--t-body)/var(--lh-body) var(--ui);color:var(--dim)}
@keyframes rt-turn{0%,25%{transform:rotate(-90deg)}60%,100%{transform:none}}
@media (prefers-reduced-motion:reduce){#rotate .rt-ico{animation:none}}
:root[data-motion="reduce"] #rotate .rt-ico{animation:none}
@media (orientation:landscape) and (max-height:500px) and (pointer:coarse){
  #rotate{display:flex}
  #col{visibility:hidden}
}

/* ==== 7 onboarding + Cut Day keep — the OpenDesign screens (auth, your team, draft, keep) ====
   The mockup's own rules ("THE OTHER SCREENS": auth, team pick, mini cards, the draft, keep), value for value; the
   shared components (`.key .seg .chipd .panel .mgrid .mcard .pc .sub-hd .sub-body .sub-foot .lead .note .sec-t`) are
   /shared's. A page is `.od-screen.onb`: header / scrolling `.sub-body` / pinned `.sub-foot`, filling `#screen`
   (whose gutter it drops) — the mockup's `.screen.sub`. The mockup has one phone column: a wider column
   centres its content at `--onb-w`. */
#screen:has(> .onb){padding:0;overflow:hidden}
.onb{--onb-w:560px;flex:1;min-height:0;height:auto}
.onb>.sub-hd,.onb>.sub-body,.onb>.sub-foot{padding-inline:max(var(--s4),calc((100% - var(--onb-w)) / 2))}
.onb .sub-hd .od-spacer{height:44px}   /* a header without its back key keeps the key's height */
.sub-foot .err{text-align:center}
.sub-foot .cut-foot{grid-template-columns:minmax(0,1fr) minmax(0,1.4fr)}   /* the quiet report key beside the gold Cut Day key */
.err{font-size:var(--fs-small);font-weight:700;color:var(--wp)}
.err[hidden]{display:none}
.rules-row{display:flex;flex-wrap:wrap;gap:var(--s2)}
.chips{display:flex;flex-wrap:wrap;gap:var(--s2)}

/* auth */
.logo span{display:block;color:var(--gold)}
.tagline{text-align:center;color:var(--soft);font-weight:500;line-height:1.5}
.form{display:flex;flex-direction:column;gap:var(--s3)}
.fld{display:grid;gap:6px}
.fld input{min-height:48px;padding:0 12px;border:var(--outline);border-radius:var(--r-md);background:var(--ink);color:var(--chalk);font:600 16px var(--ui);box-shadow:inset 0 0 0 2px var(--chalk-14)}
.fld input:focus-visible{outline:3px solid var(--first);outline-offset:2px}
.fld input[aria-invalid="true"]{box-shadow:inset 0 0 0 2px var(--wp)}

/* team pick: the conference switch, then the four divisions as identical centred rows of four */
.conf-hd{display:grid;gap:var(--s3)}
.divs{display:grid;gap:var(--s4)}
.divs[hidden]{display:none}
.div-blk{display:grid;gap:var(--s2)}
.div-t{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:var(--s3);text-align:center}
.div-t::before,.div-t::after{content:"";height:2px;background:var(--chalk-14);border-radius:var(--r-xs)}
.teamgrid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s2)}
.tm-b{position:relative;display:grid;place-items:center;min-height:56px;padding:0;border:var(--outline);border-radius:var(--r-md);background:var(--panel);box-shadow:var(--drop-sm);font-family:var(--display);font-weight:400;font-size:28px;line-height:1;letter-spacing:.04em;text-align:center;color:var(--chalk);transition:transform var(--t-fast) var(--ease-out),background var(--t-fast) var(--ease-out)}
.tm-b[aria-pressed="true"]{background:var(--chalk);color:var(--ink);transform:translateY(-3px);box-shadow:0 6px 0 var(--ink)}
.tm-b .tick{position:absolute;top:-8px;right:-8px;width:22px;height:22px;display:grid;place-items:center;border:2px solid var(--ink);border-radius:50%;background:var(--wt);color:var(--ink)}
.tm-b .tick .ico{width:14px;height:14px;stroke-width:3}
.tm-pick{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:var(--s3);padding:var(--s2) var(--s3);border:var(--outline);border-radius:var(--r-md);background:var(--ink-35)}
.tm-pick .big{font-size:34px;color:var(--gold)}
.tm-pick .big.c-dim{color:var(--dim)}
.tm-pick .d{font-size:var(--fs-small);color:var(--dim);font-weight:500}
@media (hover:hover){.tm-b:not([aria-pressed="true"]):hover{background:var(--panel-hi);transform:translateY(-2px)}}
/* The picker rows (`.pick-seg`) are still the KICKOFF sheet's mode segment and the deck's pane segment. */

/* the draft: round header, the round strip, a 10-card board in two rows of 5, this round's tray, the deck's make-up */
.dr-body{gap:var(--s3)}
.dr-hd{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:var(--s3)}
.dr-count{justify-items:end;text-align:right}
.dr-count .big{font-size:44px;white-space:nowrap;color:var(--gold)}
.dr-count .big small{font-size:22px;color:var(--dim)}
.dr-count.c-win .big{color:var(--win)}
.dr-rounds{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:var(--s2)}
.dr-rd{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:2px;padding:3px;border:2px solid var(--ink);border-radius:var(--r-sm);background:var(--ink-35)}
.dr-rd i{display:block;height:6px;border-radius:var(--r-xs);background:var(--chalk-14)}
.dr-rd.done i{background:var(--gold)}
.dr-rd.now{background:var(--panel-hi);box-shadow:0 0 0 2px var(--gold)}
.dr-rd.now i.on{background:var(--wt)}
.mgrid.dr-board{grid-template-columns:repeat(5,minmax(0,1fr));gap:var(--s2) 6px;padding-top:6px}
@container col (max-width:359px){.mgrid.dr-board{grid-template-columns:repeat(4,minmax(0,1fr))}}
.dr-legend{text-align:center;font-size:13px}
.dr-tray{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:var(--s3);padding:var(--s2) var(--s3);border:var(--outline);border-radius:var(--r-md);background:var(--ink-35)}
.dr-slots{display:grid;grid-template-columns:repeat(4,44px);justify-content:end;gap:var(--s2)}
.dr-slot{position:relative;display:grid;place-items:center;width:44px;height:62px;border-radius:var(--r-sm)}
.dr-slot .pc-w{width:44px}
.dr-slot[disabled]{opacity:.55;cursor:default}
.dr-slot.empty{border:2px dashed var(--chalk-28);color:var(--faint);font-family:var(--display);font-size:20px}
.dr-x{position:absolute;top:-6px;right:-6px;width:20px;height:20px;display:grid;place-items:center;border:2px solid var(--ink);border-radius:50%;background:var(--wr);color:var(--ink);font-weight:800;font-size:14px;line-height:1}
.dr-comp{display:grid;grid-template-columns:1fr 1fr 1.7fr;gap:var(--s2)}
.dr-c{display:flex;align-items:baseline;justify-content:center;gap:3px;min-height:36px;padding:4px;border:2px solid var(--ink);border-radius:var(--r-sm);background:var(--panel);font-size:12px;font-weight:700;letter-spacing:.06em;color:var(--dim);white-space:nowrap}
.dr-c b{font-family:var(--display);font-weight:400;font-size:22px;line-height:1;color:var(--chalk);letter-spacing:0}
.dr-c small{font-size:12px}
.dr-c.miss{border-color:var(--wp)}
.dr-c.miss b{color:var(--wp)}

/* keep: the header line with its pips, then the grid (`.mgrid` / `.mcard`, /shared's) */
.draft-hd{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--s3)}
.draft-hd .pips{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px;max-width:172px;margin-top:0;padding-bottom:6px}
.pip{width:16px;height:16px;border:2px solid var(--gold);border-radius:50%}
.pip.on{background:var(--gold)}
.keep-first-list{margin:0;padding-left:1.4em}
.keep-first-list li::marker{font-family:var(--display);color:var(--gold)}

/* ==== 8 title / home = the Season screen (the OpenDesign mockup's home, rule for rule) ====
   The header (`pageHead`, the same as every hub screen) and the tab bar are `#side`'s (section 6); the
   screen is `.home` — the week banner, the week carousel and PLAY at the foot, filling `#screen`
   (which drops its padding and scroll for it). Landscape re-lays it in two columns (sections 12, 14). */
#screen:has(> .home){padding:0;gap:0;overflow:hidden}
.home{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
.home-mid{flex:1 1 auto;display:flex;flex-direction:column;min-height:0;overflow:hidden}
.week-banner{margin:var(--s3) var(--s4) 0;padding:var(--s3) var(--s4);border:var(--outline);border-radius:var(--r-lg);background:var(--panel);box-shadow:var(--drop);display:grid;grid-template-columns:minmax(0,1fr) auto;gap:var(--s3);align-items:center;text-align:left}
.week-banner .cd{text-align:right}
.week-banner .big{font-size:34px;color:var(--gold);white-space:nowrap}
.week-banner .txt{font-size:var(--fs-small);font-weight:500;color:var(--soft);line-height:1.35}
button.week-banner{width:auto;cursor:pointer}
.report-row{display:flex;justify-content:center;padding:var(--s2) var(--s4) 0}
.season{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:var(--s2);padding:var(--s3) 0 var(--s2)}
.wk-stage{position:relative;flex:1 1 auto;min-height:0;container-type:size;overflow:hidden;touch-action:pan-y}
.wk-track{--sw:calc(58 * var(--cq));--gap:14px;display:flex;gap:var(--gap);height:100%;align-items:center;transform:translateX(calc((calc(100 * var(--cq)) - var(--sw)) / 2 - var(--cur,0) * (var(--sw) + var(--gap)) + var(--drag,0px)));transition:transform 380ms cubic-bezier(.22,1,.36,1)}
.wk-track.dragging{transition:none}
.wk-slide{flex:none;width:var(--sw);height:min(calc(100% - 12px),340px);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s2);padding:var(--s4) var(--s3);border:var(--outline);border-radius:var(--r-lg);background:var(--panel);box-shadow:var(--drop);text-align:center;transform:scale(.84);opacity:.65;transition:transform 380ms cubic-bezier(.22,1,.36,1),opacity var(--t-med) var(--ease-out),background var(--t-med) var(--ease-out)}
.wk-slide.cur{transform:none;opacity:1;background:var(--panel-hi)}
.wk-slide.now.cur{box-shadow:var(--drop),inset 0 0 0 3px var(--gold)}
.wk-slide>*{display:block}
.wk-kind{color:var(--dim)}
.wk-slide.now .wk-kind{color:var(--gold)}
.wk-slide.past .wk-kind{color:var(--first)}
.wk-slide.playoff .wk-kind,.wk-slide.sb .wk-kind{color:var(--wbt)}
.wk-disc{width:64px;height:64px;display:grid;place-items:center;border:var(--outline);border-radius:50%;background:var(--felt-2);box-shadow:var(--drop);font-family:var(--num);font-size:24px;line-height:1;color:var(--chalk)}
.wk-disc>span{display:block}
.wk-slide.past .wk-disc{background:var(--panel);box-shadow:var(--drop),inset 0 0 0 3px var(--gold)}
/* a played week that shows the boss it faced: the same slot as the boss week's, greyed out (desaturated, dimmed, still legible) and
   still: the ring and the beat are the current week's alone (`.now`), and nothing here animates beyond the card's own entry */
.wk-slide.past.scouted .wk-disc,.wk-slide.past.scouted .wk-name,.wk-slide.past.scouted .wk-meta,.wk-slide.past.scouted .wk-boss{filter:grayscale(1) brightness(.78)}
.wk-slide.past.scouted .wk-disc{box-shadow:var(--drop),inset 0 0 0 3px var(--dim);color:var(--soft)}
.wk-slide.past.scouted .wk-boss{color:var(--dim)}
.wk-slide.now .wk-disc{position:relative;width:76px;height:76px;background:var(--wr-deep);color:var(--wy);font-size:28px;text-shadow:0 2px 0 var(--ink);box-shadow:var(--drop),0 0 0 4px var(--wo)}
.wk-slide.now .wk-disc::after{content:"";position:absolute;inset:-9px;border:2px solid var(--gold);border-radius:50%;animation:beat 1.8s var(--ease-out) infinite;pointer-events:none}
/* when the soundtrack plays, the boss-week ring fires on each kick instead of its free-running loop
   (game.js#startBossBeat puts `beat-sync` on the body and `ring`/`kick`/`snare` on the disc) */
.beat-sync .wk-slide.now .wk-disc::after{animation:none;opacity:0}
.beat-sync .wk-slide.now .wk-disc.ring::after{animation:beat 640ms var(--ease-out) both}
.wk-slide.now .wk-disc{transition:scale 90ms var(--ease-out)}
.beat-sync .wk-slide.now .wk-disc.kick{scale:1.06}
.beat-sync .wk-slide.now .wk-disc.snare{scale:1.03}
.wk-slide.future .wk-disc{border:2px dashed color-mix(in srgb,var(--wy) 34%,transparent);box-shadow:none;color:var(--faint)}
.wk-name{font-size:40px;line-height:.9}
.wk-slide.now .wk-name{color:var(--gold)}
.wk-meta{font-size:var(--fs-small);font-weight:600;color:var(--soft);line-height:1.3}
.wk-boss{display:flex;flex-wrap:wrap;justify-content:center;gap:4px;color:var(--wp)}
.wk-slide.cur .wk-kind,.wk-slide.cur .wk-disc,.wk-slide.cur .wk-name,.wk-slide.cur .wk-meta,.wk-slide.cur .wk-boss{animation:wkIn 300ms var(--ease-out) both}
.wk-slide.cur .wk-disc{animation:wkPop 360ms var(--ease-pop) both}
.wk-slide.cur .wk-name{animation-delay:40ms}
.wk-slide.cur .wk-meta{animation-delay:80ms}
.wk-slide.cur .wk-boss{animation-delay:120ms}
@keyframes wkIn{from{opacity:0;transform:translateY(8px)}}
@keyframes wkPop{from{transform:scale(.6)}}
@keyframes beat{from{transform:scale(1);opacity:.9}to{transform:scale(1.3);opacity:0}}
/* the side tap zones: the whole peeking neighbour is the target */
.wk-nav{position:absolute;top:0;bottom:0;z-index:2;width:max(44px,calc((calc(100 * var(--cq)) - calc(58 * var(--cq))) / 2 - 4px));display:grid;align-items:center}
.wk-nav.prev{left:0;justify-items:start;padding-left:var(--s2)}
.wk-nav.next{right:0;justify-items:end;padding-right:var(--s2)}
.wk-nav .nb{display:grid;place-items:center;width:40px;height:40px;border:var(--outline);border-radius:50%;background:var(--ink);color:var(--chalk);box-shadow:var(--drop-sm);transition:transform var(--t-fast) var(--ease-pop)}
.wk-nav:active .nb{transform:scale(.9)}
.wk-nav[disabled]{visibility:hidden}
.wk-nav:focus-visible{outline:none}
.wk-nav:focus-visible .nb{outline:3px solid var(--first);outline-offset:2px}
@media (hover:hover){.wk-nav:hover .nb{transform:scale(1.1);background:var(--panel-hi)}}
/* the rail is centred on its own row (W1 · dots · W18); the way back to the current week has a row of its own under it,
   reserved even while hidden so the week card never jumps when the player leaves or returns to the current week */
.wk-foot{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;column-gap:var(--s2);row-gap:var(--s1);padding:var(--s1) var(--s4) 0}
.wk-rail{display:flex;gap:2px;align-items:center}
/* an explicit width as well as the flex basis: WebKit sizes the grid's `auto` track from the items' `width`, not their basis,
   and collapsed the rail to a sliver under the W18 label */
.wk-rail .rt{flex:0 0 8px;width:8px;height:6px;border-radius:var(--r-xs);background:color-mix(in srgb,var(--wy) 18%,transparent);transition:transform var(--t-fast) var(--ease-out)}
.wk-rail .rt.past{background:var(--gold)}
.wk-rail .rt.now{background:var(--wr)}
.wk-rail .rt.view{transform:scaleY(2.3);box-shadow:0 0 0 2px var(--wy);opacity:1}
.wk-foot>.lbl:first-child{justify-self:end}
.wk-foot>.lbl:nth-child(3){justify-self:start}
.wk-back{position:relative;grid-column:1 / -1;justify-self:center;min-width:0;min-height:36px}
.wk-back::after{content:"";position:absolute;inset:-4px 0}   /* 36px plate, 44px hit area */
.wk-back[aria-hidden="true"]{visibility:hidden}
/* The week card adapts to the room IT has, not to the column's height (a zoomed tablet/desktop column is 664-740 local px
   tall, under any column threshold, yet leaves the card as much room as a phone): `.wk-stage` is the size container (an
   unnamed query reads the nearest one) and the slide is `min(stage - 12px, 340px)` tall. Measured slide content height at
   a 226 px wide card (3 tags = a boss's two rules + the modifier; "worst" = three long labels such as EIGHT IN THE BOX /
   GROUND AND POUND / INJURY REPORT, wrapping to 3 rows):
     full disc (76) + name (40) + tags     260, worst 285  ->  stage >= 272, worst 297
     compact disc (60) + name (32) + tags  237, worst 262  ->  stage >= 249, worst 274
     compact, no tags                      183
   so under 300 the disc and the name shrink (from 300 up even the worst case fits full size), and under 276 the tags go
   (with the small disc the worst case needs 274). Stage heights seen: 462 at 390x844, 285 at 390x667, 365 at 820x1180,
   292 at 1280x800, 302 at 1920x1080, 258 at 360x640, 186 at 320x568. */
@container (max-height:299px){
  .wk-disc{width:52px;height:52px;font-size:20px}
  .wk-slide.now .wk-disc{width:60px;height:60px}
  .wk-name{font-size:32px}
}
/* Hidden WITHOUT `display`: Chromium replays an element's CSS animation each time its container is laid out again when a
   `display` value sits in an @container rule on it (game.js#homeClock rewrites #weekLbl each second), so `display:none`
   here made the tags' entry animation (`wkIn`) blink once a second. Taken out of the flow, zero-sized and invisible is
   the same layout (and drops out of the accessibility tree) with no such replay. */
@container (max-height:275px){
  .wk-boss{position:absolute;width:0;height:0;margin:0;overflow:hidden;visibility:hidden}
}
.home-foot{padding:var(--s3) var(--s4);background:linear-gradient(180deg,color-mix(in srgb,var(--ink) 0%,transparent),color-mix(in srgb,var(--ink) 94%,transparent) 26%);display:flex;flex-direction:column;gap:var(--s3)}
/* the tag on a week card (the mockup's `.tag2`): a rule of the boss, or today's modifier */
.tag2{justify-self:start;padding:0 6px;border:2px solid currentColor;border-radius:var(--r-xs);font-size:var(--fs-label);font-weight:700;letter-spacing:.06em;text-transform:uppercase;line-height:1.4}

/* ==== 9 the Locker, Ranks, Office, Cut Day, report, Sunday Live, Settings and How to play — the OpenDesign mockup's screens ====
   Rules copied from the mockup's "THE OTHER SCREENS" and "BOSS REVEAL" blocks. The shared pieces they are made of (`.rows .rowc`,
   `.seg .sg`, `.chipd`, `.sw`, `.panel`, `.confirm`, `.acts`, `.mgrid .mcard`, `.sheet-card`, `.od-stat`, `.relic`) are
   shared/design-system.css's; what is here is what only these screens use, and the few places where the wire needs a row
   the mockup did not draw (the coach keys under a coach, the respec block). `.hub` is the body every one of them fills: the
   mockup's `.sub-body` column, the padding being `#screen`'s. */
.hub{flex:none;display:flex;flex-direction:column;gap:var(--s4);min-width:0}
.hub .rows{margin:0}
.stats .od-stat{padding:var(--s2) var(--s3);border:var(--outline);border-radius:var(--r-md);background:var(--panel)}
.stats .big{font-size:34px;white-space:nowrap}
.stats .big small{font-size:20px;color:var(--dim)}
.sheet-stats{margin-bottom:var(--s4)}
.face{display:block;width:56px;border:2px solid var(--ink);border-radius:var(--r-sm);overflow:hidden;background:var(--ink)}
.face.tagface{display:grid;place-items:center;height:40px;background:var(--gold);color:var(--ink);font-family:var(--display);font-size:22px}
.pc-w.w48{width:48px}
.tag2{justify-self:start;padding:0 6px;border:2px solid currentColor;border-radius:var(--r-xs);font-size:var(--fs-xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;line-height:1.4}
.cd-big{font-size:72px;color:var(--gold);white-space:nowrap}
.chipd[disabled]{opacity:.4;cursor:default}
a.rowc{text-decoration:none;color:inherit}
/* one tier palette: `--tier` paints the tag, the figure and the tier's relic */
.tr-gold{--tier:var(--gold)}.tr-silver{--tier:var(--tier-silver)}.tr-bronze{--tier:var(--tier-bronze)}.tr-wood{--tier:var(--tier-wood)}
:is(.tag2,.big).tr-gold,:is(.tag2,.big).tr-silver,:is(.tag2,.big).tr-bronze,:is(.tag2,.big).tr-wood{color:var(--tier)}
.of-tier .relic{--rc:var(--tier)}
/* the Locker: the counts turn red under the minimum, the starters' thumbnail, one grid per position */
.deck-counts{display:flex;flex-direction:column;gap:var(--s2)}
.deck-counts.is-short .od-stat:first-child{box-shadow:inset 0 0 0 2px var(--wp)}
.deck-counts.is-short .deck-line{color:var(--wp)}
.deck-needs .key{justify-self:start}
.deck-groups{display:grid;gap:var(--s4)}
.deck-group{display:grid;gap:var(--s2)}
/* coaches: the keys (◂ ▸ UNEQUIP, EQUIP) sit under the coach's own row, a legendary coach wears the ice relic */
.coach-tile,.coach-bench-row{align-items:center}
.coach-ctl{grid-column:1/-1;display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap}
.coach-ctl .coach-pos{flex:1 1 auto}
.coach-short{grid-column:1/-1}
.coach-tile.is-rare .relic{--rc:var(--legend)}
/* the playbook: the level sits where the mockup's play type does */
.recipe-book{display:grid;gap:var(--s3)}
.recipe-lv{min-width:48px}
.recipe-bar{display:block;height:6px;margin-top:6px}
.recipe-bar i{background:var(--win);transition:none}
.recipe-bar.is-max i{background:var(--gold)}
/* ranks: the rank figure (podium in metal), the day rail flush with the column */
.rk{min-width:2ch;text-align:center}
.rk.is-p1{color:var(--gold)}.rk.is-p2{color:var(--tier-silver)}.rk.is-p3{color:var(--tier-bronze)}
.rk-row .d.od-cluster{gap:var(--s1) var(--s2);align-items:center}
.rank-tds{letter-spacing:0}
.rk-days{--od-rail-pad:0px}
#ranksErr[hidden]{display:none}
/* Sunday Live: four tiles, the delta as the row's big figure */
.sun-stats{grid-template-columns:repeat(4,minmax(0,1fr))}
.sun-stats .big{font-size:30px}
.sun-delta{min-width:44px;font-size:26px;text-align:center}
.sun-name{overflow-wrap:anywhere}
/* winners and losers apart: a headed section each, a tinted row, and an arrow before the figure (shape as well as colour) */
.sun-sec{display:flex;align-items:center;justify-content:space-between;margin:var(--s4) 0 var(--s2);padding:var(--s1) var(--s3);border:var(--outline);border-radius:var(--r-md);font-family:var(--display);font-size:var(--fs-lead);letter-spacing:.06em;text-transform:uppercase}
.sun-sec b{font-family:var(--num);font-weight:400}
.sun-sec.up{background:var(--first);color:var(--ink)}
.sun-sec.down{background:var(--wr-deep);color:var(--wy)}
.sun-sec.rest{background:color-mix(in srgb,var(--ink) 55%,transparent);color:var(--soft)}
.sun-list.rest{opacity:.85}
.sun-row.is-up{background:color-mix(in srgb,var(--first) 14%,color-mix(in srgb,var(--ink) 28%,transparent))}
.sun-row.is-down{background:color-mix(in srgb,var(--wr) 16%,color-mix(in srgb,var(--ink) 28%,transparent))}
.sun-delta.c-first::before,.sun-delta.c-loss::before{display:block;font-family:var(--sys);font-size:14px;line-height:1}
.sun-delta.c-first::before{content:"▲"}
.sun-delta.c-loss::before{content:"▼"}
.sun-name .tag2{margin-left:var(--s1)}
.sun-status{margin-right:var(--s1)}
/* the report: the headline panel, the three piles */
.report-head{justify-items:start}
.gz-pile{display:grid;gap:var(--s2)}
/* Cut Day: the pending Cut Day's panel, the tiers */
.of-pending{border-color:var(--wp)}
/* ==== the boss reveal — the mockup's cinematic, over everything ====
   Above the report (the effects layer, 40, plays over it). `.still` is reduced motion: the final frame, no animation.
   Every animation fills `forwards`, so the resting state IS the final state. */
.reveal{position:fixed;inset:0;z-index:var(--z-reveal);display:grid;padding:calc(var(--sat) + var(--s4)) var(--s4) calc(var(--sab) + var(--s4));background:var(--ink);opacity:0;transition:opacity 200ms var(--ease-out);overflow-y:auto;overflow-x:hidden}
.reveal.on{opacity:1}
.reveal.off{opacity:0;transition-duration:160ms;transition-timing-function:var(--ease-in)}
/* letterbox bars sweep in, then hold a red stripe behind the crest */
.rv-bars{position:absolute;left:0;right:0;top:50%;height:180px;margin-top:-150px;background:repeating-linear-gradient(90deg,transparent 0 24px,color-mix(in srgb,var(--ink) 22%,transparent) 24px 48px),var(--wr);transform:scaleY(0);animation:rvbars 420ms var(--ease-out) 700ms forwards}
.rv-stage{position:relative;margin:auto;display:grid;justify-items:center;gap:var(--s3);width:100%;max-width:340px;text-align:center;animation:rvshake 360ms var(--ease-out) 900ms}
.rv-kick{color:var(--wp);opacity:0;animation:rvfade 240ms var(--ease-out) 200ms forwards}
.rv-crest{transform:translateY(-140%) scale(2.2) rotate(-14deg);opacity:0;animation:rvslam 700ms cubic-bezier(.55,0,.8,.2) 200ms forwards}
.rv-crest .crest{filter:drop-shadow(0 6px 0 var(--ink))}
.rv-name{font-size:64px;line-height:.9;color:var(--wy);text-shadow:0 4px 0 var(--ink);white-space:nowrap}
.rv-name.is-long{font-size:44px;white-space:normal}
.rv-w{display:inline-block;white-space:nowrap}
.rv-name span[style]{display:inline-block;opacity:0;transform:translateY(18px);animation:rvletter 260ms var(--ease-pop) forwards;animation-delay:calc(1250ms + var(--i) * 45ms)}
.rv-sub{color:var(--soft);font-weight:600;opacity:0;animation:rvfade 240ms var(--ease-out) 1650ms forwards}
.rv-rules{display:grid;gap:var(--s2);width:100%;margin-top:var(--s2)}
.rv-rule{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:var(--s3);padding:var(--s2) var(--s3);border:var(--outline);border-radius:var(--r-md);background:var(--panel);text-align:left;opacity:0;transform:rotateX(90deg);animation:rvflip 300ms var(--ease-pop) forwards;animation-delay:calc(1900ms + var(--i) * 350ms)}
.rv-rule .t{font-weight:700;line-height:1.3}
.rv-rule .d{font-size:var(--fs-small);color:var(--dim);font-weight:500}
.rv-go{margin-top:var(--s3);opacity:0;animation:rvfade 240ms var(--ease-out) 3000ms forwards}
.rv-skip{position:absolute;top:calc(var(--sat) + var(--s2));right:var(--s4);min-height:44px;min-width:64px;padding:0 12px;border:2px solid var(--faint);border-radius:var(--r-pill);color:var(--dim);font-size:13px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
@media (hover:hover){.rv-skip:hover{color:var(--chalk);border-color:var(--chalk)}}
.reveal.still *,.reveal.still{animation:none !important;transition:none !important;opacity:1 !important;transform:none !important}
.reveal.still .rv-bars{transform:scaleY(1) !important}
@keyframes rvbars{to{transform:scaleY(1)}}
@keyframes rvslam{0%{opacity:1}100%{opacity:1;transform:none}}
@keyframes rvshake{20%{translate:-6px 4px}40%{translate:5px -4px}60%{translate:-3px 2px}80%{translate:2px -1px}}
@keyframes rvletter{to{opacity:1;transform:none}}
@keyframes rvflip{to{opacity:1;transform:none}}
@keyframes rvfade{to{opacity:1}}

/* ==== 9a hub screens · THE DECK (Balatro spec §4.1–§4.2, master §3.4.5; the screen id stays
   `locker`) ====
   Every card the player owns, in five position groups (QB · LINE · RB · WR · TE), the server's
   counts on top, one row per card: the card itself (a button to its sheet) and RELEASE. No cap,
   no salary, no reserve (RD38), so nothing here is a meter or a pane. Nothing below is scoped on
   body.mob / body.port, and no rule of this screen lives in a breakpoint block: the groups flow
   into columns by their own width (`columns`). */

/* ---- the screen ---- */
/* ONE SCROLL at every breakpoint: the panel is content-sized and `#screen` scrolls it (a
   53-card deck is several screens tall; a bounded well inside a panel is the worse trade, the
   locker's fix round 1 ruling kept). (2,0,0) beats section 6's `#screen>.pnl{overflow-y:auto}`. */



/* The counts line: a readout plaque on the blue rail while the deck is legal, the red rail and
   the missing counts under it when it is short. The state is a class (A16); the colour is not
   the only signal — `.deck-needs` says in words what is missing. */







/* ---- the segment, the starters, the five groups ---- */
/* The DECK · COACHES · PLAYBOOK segment is the pick screen's (`.pick-seg-row`) in three columns. */


/* A starter: the card face at the game's 70 x 100 beside its name and its figures. */

/* The deck: a grid of card faces, one group under its position tag. The faces are fluid (72 px at
   least): four to a row in a phone's panel, more in a wider column, no rule of its own. */






/* The card sheet's enlarged face, centred over its figures. */

/* The value: number over its unit, blue YARDS or red MULT (DESIGN-DNA: blue = YARDS, red = MULT). */






/* ---- the card sheet (screens.js#cardInfoSheet) ---- */






/* ---- coaches ---- */
/* The coach row (P4 Task 14, D11, RD50): the equipped coaches, read left to right, each tile
   with its place, its effect line, its level and ◂ ▸ to swap it with its neighbour; the bench
   under it. `auto-fill` instead of a breakpoint (the grid the shipped block used): one column in
   a 326 px phone panel, two or three wherever the panel is wider. A legendary ×MULT coach wears
   the ice rail (DESIGN-DNA: ice cyan = a legendary ×MULT coach), every other one the gold.
   The column floor is 280 px, not 240: a tile carries UPGRADE $200 and its neighbour button side by
   side, and at 240 the four-column 1280 px grid gave each button 104 px for a 107 px label
   (tests/smoke-widths.spec.js). */
















#deckView .hint,#deckView .meter{margin-top:0}
.meter.is-over i{background:var(--red)}
/* Item 6 — a coach the coins cannot upgrade says by how much. */


/* ---- the in-match DECK VIEW sheet (screens.js#lockerBody's one caller) ----
   The view/filter chips and the list shape left with the v0.2 locker (bêta
   v0.1, B5 Task 5): the sheet is the card grid below. */
/* The mobile thumbnail size, moved out of legacy's `body.mob .grid.zoom .mini`
   with the `locker` area. Shared by the deck view and the market plates —
   /shared's own 116×160 is the desktop value. */
.grid.zoom .mini{width:100px}
/* …and the DECK VIEW's own copy of that grid, which is a different problem
   from the market plates' and the card-reveal sheets' (the three other users
   of the selector above), so it is scoped rather than shared.
   ONE: 100 px was a hard pixel width at every breakpoint, so `.mm` had an
   88 px box whether the viewport was 360 or 1112 — eleven ellipsised names on
   a 820 px tablet ("Amon-Ra St. …" wants 116, "TreVeyon Henderson" 127,
   "Jacory Croskey-Merritt" 146) while the last row left six empty card slots
   of width doing nothing. A fluid `auto-fill` track spends that width, and
   `aspect-ratio` keeps the face's proportions while it does. Not `100cqi`:
   nothing in `#deckView > .scroll > .grid.zoom > .mini` is a container, so
   `cqi` would fall back to the small-viewport size and blow the type up.
   TWO: a wider track alone is not enough (105 px against a 146 px name), so
   the load-bearing half is a two-line clamp (measured on the v0.2 Free Agent
   board, since gone) with the photo band shortened to make
   the room. `bottom` is a PERCENTAGE here, not that board's literal 30px:
   `.mst` sits at `bottom:5%;height:15%`, i.e. its top edge is at 20% of the
   card's height, and a fluid card has no one height to hard-code against.
   THREE: the stat chips are cut by `.mini .mst span`'s own parallelogram
   (`clip-path:polygon(4px 0,…)`) — at this size the diagonal passes through
   the first and last glyph, so PWR painted as "PWF" on every card and the
   tens digit of a two-digit value was shaved to a bare stem ("19 PWR" as
   "|9 PWR"). Same repair as `.draft-card`, `.plate` and `#bidOverlay`: the
   WORD goes, the numbers stay in their colours, and the match screen's own
   "■ PWR ■ CAP" legend names them. */
#deckView .grid.zoom{display:grid;grid-template-columns:repeat(auto-fill,minmax(100px,1fr));align-content:start}
#deckView .grid.zoom .mini{width:100%}

/* Item 4 — under the minimum the alert carries the way back (the Kaiten). */








/* ==== 9b hub screens · KAITEN MARKET, LEADERBOARD, FRONT OFFICE, SUNDAY LIVE
   (spec §4.4/§4.7/§4.8, SIZING-AUDIT §2.3, LEGACY-MAP §5.11/§5.13/§5.14) ====
   Three legacy areas die here: `market` (94 chunks and 117 lines, the file's
   largest — a whole mouse-only stage set), `ranks` (8) and `sunday` (4). The
   last two were opposite contracts on ONE component: `body.mob.port .rank`
   forced every `.rank` into a wrap-anywhere flex row with `!important`, to
   beat the eight inline `grid-template-columns` its call sites carried, and
   `body.mob.port .rank.sun-row` then forced Sunday Live back out of it with
   four more. `.rank` is a grid here, once, and the call sites ask for it by
   class. Nothing below is scoped on body.mob / body.port. */

/* ---- the shared `.rank` row (leaderboard, Sunday Live, My Bids, the
   Gazette, the playbook, the final screen) ---- */
/* `34px 1fr auto`: the chip that identifies the row, everything nameable,
   everything numeric. `--ctl-std` because these rows ARE tap targets in half
   their consumers, and `tabular-nums` because a score column sitting under a
   per-second clock must not jitter as its digits change. `margin:0` drops
   /shared's `margin-top:4px` — every list that holds these rows is a `.stack`
   with a real gap now, and the two stacked to 4+4. */

/* …and the 4 px back, for the lists that are NOT a flex column with a gap of
   their own. Six of the eight `.rank` call sites ARE (`.rk-list`, `.sun-list`,
   the playbook's `.scroll.stack.gap-1`, and the scouting reveal's `.stack.gap-2`
   until that sheet went, 2026-09-24),
   so the separation is the container's; the other three — My Bids' OPEN and
   LAST 24H lists, the Gazette's cut / free-agent lists and the final screen's
   Drives and Biggest-factors panels — drop their rows straight into a `<div>`
   or a `.pnl`, where `margin:0` above left consecutive rows touching and
   reading as one dark slab (measured 0 px at 390). `:not(.stack) >` is what
   keeps it from doubling the gap in the six that already have one. */


  
/* The middle cell wraps rather than ellipsising the tags off the row: a
   display name is the leaderboard's whole point, and YOU/TIER are 2 chips. */




/* The leaderboard's tie-break (A1, 2026-09-23): the run's TD count under its
   score, dim like the finish date it replaced — until the count separated the
   row from another on the same score, where it decided the order and reads
   gold (an equal score on an equal count is the week's draw: no gold). The
   tie itself is told by the equal scores; the colour only points at the
   figure that broke it. */
   /* the label role's .10em pulled "7 TD" apart into "7 T D" */

/* One tier palette for the leaderboard chip, the Front Office cards and the
   four reward tiles — three call sites that each carried their own `TC` map
   inline. `--tier` is a local variable, not a token: it exists only on the
   element that sets it. */
/* `--tier` paints the whole card, heading AND body, so each rung must clear 4.5:1 for the 14 px body
   text on the night grounds (`.pnl.dark` = --felt2 #072819 at worst): wood #8A9AA8 reads 5.5 there
   (5.0 on --panel), a real step below bronze. tests/polish.spec.js pins it on resolved pixels. */
   

.pnl.tr-gold,.pnl.tr-silver,.pnl.tr-bronze,.pnl.tr-wood{--edge:var(--tier)}
/* The three screens below are one panel that fills `#screen` and scrolls its
   own list, plus a centred empty/error state with a real RETRY control. */

#overlay .sheet .pnl.dark,#overlay .sheet .pnl{clip-path:none;border-radius:var(--r-md);background:color-mix(in srgb,var(--ink) 34%,transparent);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--wy) 6%,transparent)}


/* The ranks screen's scope segment (DAY · WEEK · SEASON, D13) and the day strip (days 1-7):
   keycaps at the 44 px `.btn.sm` tier, the current one ringed gold like the auth segment and
   `aria-pressed` for the reader. The day strip shares its seven keys across the panel width
   (`flex:1`), so a phone at 320 still fits all seven without a horizontal scroll. A day after
   today is `disabled` — nothing to read there yet — and dims rather than vanishing, so the
   week keeps its shape. */




/* The podium title (RD58): gold on the tag's black wash — a trophy, not a tier, so it borrows
   the gold of the player's score rather than one of the four tier colours. */
/* The season's standing lines: the closed notice reads gold (the standing is final), the
   double week stays a hint. */

#ranksErr:empty{display:none}
/* …and `.of-body` SCROLLS its children instead of crushing them. `.hint`
   carries an explicit `min-height:16px` (/shared), and an explicit min-height
   REPLACES a flex item's `auto` content minimum — so inside this overflowing
   flex column `#cutsLine` shrank to a 16 px box while its text still needed
   two or three lines, and ~24 px of live ink painted outside its own box and
   under the tier grid ("…bel… 24" in EN; in FR the whole of « jamais sous »
   and the number 24 itself, plus an orphan "2" adrift between the GOLD and
   SILVER cards). The box probe cannot see it: the 16 px box overlaps nothing,
   only the ink does. It is the one sentence that states Cut Day's own rule,
   on the screen the product is named after. */


/* Item 5 — the week board: the days line is short (« 5/5 j ») and sits under the score, so
   the name keeps the row's width. Item T15 — the seven day keys are 44 px wide at least and
   wrap to two rows (4 + 3) where seven do not fit. */



/* ---- Kaiten Market (9b) — the OpenDesign KAITEN BELT, rule for rule (mockup "KAITEN BELT" 784-870 and the
   market chunk 742-753). One vertical conveyor: lots come out of the hood at the top (it only says "Paused") and leave under
   the hood at the bottom. `.mk-root` is the body (screens.js#marketScreen), whatever wraps it: a shell
   `.sub-body` around it takes the mockup's `.sub-body.mk-body` rules through `:has`. game.js#beltFrame writes each
   `.conv-slot`'s transform from SERVER time; nothing here moves anything. The mockup's 11 px labels are 12 here
   (the label floor); nothing else differs from its values. ---- */
.mk-root{display:flex;flex-direction:column;gap:var(--s3);flex:1 1 auto;min-height:0}
.sub-body:has(>.mk-root){overflow:hidden;padding-bottom:var(--s3)}
.stats .od-stat{padding:var(--s2) var(--s3);border:var(--outline);border-radius:var(--r-md);background:var(--panel)}
.stats .big{font-size:34px;white-space:nowrap}
.mk-stats .od-stat{padding:4px var(--s3)}
.mk-stats .big{font-size:26px}
.mk-stats .big small{font-size:16px;color:var(--dim)}
.feed{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:var(--s2);min-height:44px;padding:0 var(--s3);border:var(--outline);border-radius:var(--r-md);background:var(--ink);font-size:var(--fs-small);font-weight:600;color:var(--soft)}
.feed .live{padding:0 6px;border-radius:var(--r-xs);background:var(--wr);color:var(--ink);font-size:12px;font-weight:700;letter-spacing:.1em;text-transform:uppercase}
.belt-hd{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--s3)}
.belt-ctl{display:flex;gap:var(--s2)}
.tchip{font-size:14px;font-weight:700;letter-spacing:.04em;color:var(--dim);font-variant-numeric:tabular-nums;white-space:nowrap}
.tchip.hot{color:var(--wp)}
.tag2{justify-self:start;padding:0 6px;border:2px solid currentColor;border-radius:var(--r-xs);font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;line-height:1.4}
.conv{position:relative;flex:1 1 auto;min-height:240px;border:var(--outline);border-radius:var(--r-lg);background:var(--ink);box-shadow:var(--drop);overflow:hidden}
.conv-lane{--slot-h:124px;--lot-cw:76px;position:absolute;inset:0 8px;margin:0;padding:0;list-style:none;border-inline:3px solid var(--panel-hi);
  background:repeating-linear-gradient(180deg,var(--chalk-08) 0 2px,transparent 2px 16px) 0 0 / 100% 16px,var(--felt-2)}   /* game.js#beltFrame moves it with `background-position-y` */
.conv::before,.conv::after{content:"";position:absolute;top:0;bottom:0;z-index:1;width:4px;pointer-events:none;background:repeating-linear-gradient(180deg,var(--wo) 0 4px,transparent 4px 22px);opacity:.7}
.conv::before{left:2px}
.conv::after{right:2px}
.conv-slot{position:absolute;top:0;left:0;right:0;height:var(--slot-h);padding:0 8px;will-change:transform;transition:opacity .45s}
.conv-slot.gone{opacity:0;pointer-events:none}
.conv-hood{position:absolute;left:0;right:0;z-index:3;height:24px;display:grid;place-items:center;pointer-events:none;background:repeating-linear-gradient(135deg,var(--panel-hi) 0 8px,var(--panel) 8px 16px)}
.conv-hood span:empty{display:none}
.conv-hood span{padding:0 8px;border-radius:var(--r-xs);background:var(--ink);font-size:12px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--dim)}
.conv-hood.top{top:0;border-bottom:2px solid var(--ink);box-shadow:0 4px 0 color-mix(in srgb,var(--ink) 50%,transparent)}
.conv-hood.bot{bottom:0;border-top:2px solid var(--ink);box-shadow:0 -4px 0 color-mix(in srgb,var(--ink) 50%,transparent)}
.conv.paused .conv-hood.top span{color:var(--gold)}
.conv-empty{position:absolute;inset:0;display:grid;place-items:center;padding:var(--s5);text-align:center;color:var(--dim);font-weight:600}
.conv-empty[hidden]{display:none}
.lot{width:100%;height:100%;display:grid;grid-template-columns:var(--lot-cw) minmax(0,1fr);grid-template-rows:minmax(0,1fr);align-items:center;gap:var(--s3);padding:8px 10px 8px 8px;border:var(--outline);border-radius:var(--r-md);background:var(--panel);box-shadow:0 4px 0 var(--ink-90);text-align:left;transition:background var(--t-fast) var(--ease-out)}
.lot:disabled{color:inherit;cursor:default}
.lot-card{display:block;width:var(--lot-cw)}
/* a short lane (game.js#beltFrame sets `data-compact` under a 100 px slot): the card shrinks with the slot and the info
   becomes two lines — name · price over meta · clock and BID — so about five lots show on a phone */
.conv-lane[data-compact]{--lot-cw:max(30px,calc((var(--slot-h) - 14px) / 1.4))}
.conv-lane[data-compact] .lot{gap:8px;padding:2px 8px 2px 6px}
.conv-lane[data-compact] .lot-info{grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"t p" "d f";align-items:center;column-gap:8px;row-gap:0}
.conv-lane[data-compact] .lot .t{grid-area:t;font-size:var(--fs-body);line-height:1.2}
.conv-lane[data-compact] .lot .d{grid-area:d;font-size:var(--fs-xs);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.conv-lane[data-compact] .lot-price{grid-area:p;justify-content:flex-end;gap:4px}
.conv-lane[data-compact] .lot-price .big{font-size:22px;line-height:1}
.conv-lane[data-compact] .lot-foot{grid-area:f;gap:6px}
.conv-lane[data-compact] .lot-st{min-height:30px;min-width:72px;padding:0 14px;font-size:var(--fs-body)}
.conv-lane[data-tight] .lot{padding-block:0}
.conv-lane[data-tight] .lot-price .big{font-size:18px}
.conv-lane[data-tight] .lot-price .lbl{display:none}
.conv-lane[data-tight] .lot-st{min-height:26px;min-width:60px;padding:0 10px;font-size:var(--fs-small)}
.lot-info{display:grid;grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"t t" "d d" "p f";align-items:center;gap:2px 10px;min-width:0}
.lot-info .t{grid-area:t}
.lot-info .d{grid-area:d}
.lot-info .lot-price{grid-area:p}
.lot-info .lot-foot{grid-area:f}
.lot .t{display:block;font-weight:700;line-height:1.25}
.lot .d{display:block;font-size:14px;font-weight:500;line-height:1.3;color:var(--dim)}
.lot .star{color:var(--gold)}
.lot-price{display:flex;align-items:baseline;gap:6px}
.lot-price .big{font-size:30px;color:var(--gold);white-space:nowrap}
.lot-price .lbl{font-size:12px}
.lot-foot{display:flex;align-items:center;justify-content:flex-end;gap:var(--s2)}
.lot-st{display:inline-flex;align-items:center;justify-content:center;min-height:40px;min-width:88px;padding:0 18px;border:var(--outline);border-radius:var(--r-sm);background:var(--panel-hi);color:var(--chalk);box-shadow:0 2px 0 var(--ink);font-family:var(--display);font-size:24px;line-height:1;text-transform:uppercase;white-space:nowrap}
.lot-st.gold{background:var(--wo);color:var(--ink)}
.lot-st.teal{background:var(--wt);color:var(--ink)}
.lot-st.red{background:var(--wr);color:var(--ink)}
.lot-st.ghost{background:transparent;color:var(--dim);box-shadow:none}
.lot.lead{box-shadow:0 4px 0 var(--ink-90),inset 0 0 0 2px var(--wt)}
.lot.outbid{box-shadow:0 4px 0 var(--ink-90),inset 0 0 0 2px var(--wr)}
.lot.closed{background:color-mix(in srgb,var(--panel) 70%,transparent)}
.lot.closed .lot-price .big{color:var(--dim)}
.lot.bump .lot-price .big{animation:kbump 420ms var(--ease-pop)}
@keyframes kbump{40%{scale:1.25;color:var(--wy)}}
/* the weekly coach lane (API.md §3.7 D9): rows, not lots — not biddable yet */
.coach-lane{flex:none}
.coach-lane .rowc{min-height:48px}
/* the bid sheet */
.bs-card{display:grid;grid-template-columns:72px minmax(0,1fr);align-items:center;gap:var(--s3);margin-bottom:var(--s3)}
.bs-card .pc-w{width:72px}
.bs-card .t{font-weight:700}
.bs-card .d{font-size:var(--fs-small);color:var(--dim)}
.bs-note{margin:var(--s3) 0 0;font-weight:600}
.bs-lbl{margin:var(--s4) 0 var(--s2)}
.bs-proxy{margin-top:var(--s3)}
/* The bid sheet scrolls as a whole (`.sheet` is `max-height:88%`); on a short phone (667 px tall) PLACE BID would sit below
   the fold of the one surface that runs a 40-second clock. The keys stay pinned to the sheet's foot, on its own ground,
   and the gap the sheet pads under them is painted too, so a scrolled form never shows through it. */
.bs-acts{margin-top:var(--s4);position:sticky;bottom:calc(var(--sab,0px) + var(--s4));padding-top:var(--s2);background:var(--wg);box-shadow:0 calc(var(--sab,0px) + var(--s4)) 0 0 var(--wg)}
.stepper{display:grid;grid-template-columns:56px minmax(0,1fr) 56px;align-items:center;gap:var(--s2)}
.stepper .key{width:56px;font-size:30px}
.stepper .big{font-size:56px;color:var(--gold);text-align:center}
.stepper input.big{width:100%;min-width:0;height:auto;min-height:0;padding:0;border:0;border-radius:0;box-shadow:none;background:none;font:400 56px/.85 var(--num);letter-spacing:.01em;color:var(--gold);font-variant-numeric:tabular-nums;-moz-appearance:textfield;appearance:textfield}
.stepper input.big::-webkit-outer-spin-button,.stepper input.big::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.stepper input.big:focus-visible{outline-offset:0;border-radius:var(--r-md)}
.quick{display:flex;gap:var(--s2);margin-top:var(--s3)}
.quick .chipd{flex:1}
.relpick{display:flex;flex-wrap:wrap;gap:var(--s2);max-height:184px;overflow-y:auto;overscroll-behavior:contain}
.relpick .chipd:disabled{opacity:.5;cursor:default}
.relpick .chipd small{font-size:12px;color:var(--wp)}
/* My Bids */
.mb-lbl{margin-bottom:var(--s2)}
.mb-lbl:not(:first-child){margin-top:var(--s4)}
.rowc .mb-thumb{width:44px}
#myBids .rows{margin-bottom:var(--s2)}
#myBids .acts{margin-top:var(--s4)}
/* Title left, live clock right — ONE header shape for the two hub screens
   that carry a clock (the market's next-card countdown and the Front
   Office's tick). The title's `flex:1 1 200px` is what makes the clock drop to its
   own line rather than squeezing the title: 200 + a 170 px `0D 13:58:21`
   does not fit a 326 px phone panel, 200 + a 100 px `30s` does. */


/* The Office's entries (officeMenu): the account, then Sunday Live / How to play / Settings — the mock's
   front-office rows. `.of-row` is a real button that must lose the UA chrome before `.pnl` reads. */




/* The next-card clock. `flex:none` alone left it at flex-START once the header
   wrapped: `justify-content:space-between` does nothing for a lone item on line
   two, so in French — where `PROCHAINE CARTE DANS` will not share line one with
   a 200 px title — the block fell to the left of a 326 px panel AND stacked its
   label over its value, spending 60 px of a phone's header on two half-empty
   lines. `margin-inline-start:auto` re-anchors it right, and is a no-op while
   the line is unwrapped — so English, where the clock still shares line one
   with the title, renders exactly what it rendered before.
   MEASURED, and NOT done: also turning the block into a label-beside-value ROW
   would make the French case one line instead of two — but it widens the block
   past what can share line one with a `flex:1 1 200px` title, so English LOSES
   a line (header 220 -> 243, belt 480 -> 457) to save French 17 px. The
   wrapped case needs the row and the unwrapped case needs the stack, and CSS
   cannot tell them apart here without a container query; it is a spec question,
   not a declaration. `.of-tick` keeps the old rule — the Front Office's half of
   this is its own repair (docs/FOLLOWUPS.md). */


/* ---- Sunday Live (§4.8) ---- */
/* `34px 1fr auto auto`, four cells for what used to be five fixed tracks
   (34 + 1fr + 3×90 = 304 px of columns before the name got any, on a 326 px
   panel). The two weekly PWR figures share the third cell. */



/* The name WRAPS (two lines, clamped) instead of ellipsising. Measured at
   390: the name cell is 121-123 px and every NFL name on a starter roster
   wants 126-161 — one ellipsised line loses 11-40 px of exactly the thing
   the row identifies. Two lines cost the LONG rows 5 px of height (53) and
   leave every short one at the 48 tier, the same trade `.lrow-name` made in
   the locker. `overflow-wrap:anywhere` covers the unbreakable surname. */

/* `.is-played` — this player's team has kicked off this week. The dot is the
   row's own live light; the delta cell reads DNP without it. Inline (not a
   flex item) so it rides inside the clamped name box. */


/* Bêta v0.1 (API.md §3.14): « WED: 5 → 6 », this boss week's frozen value and the projection
   for the next tick, beside the team; green when it rises, red when it falls (a comparison of
   the two wire numbers, nothing more). A `belt`/`bid` row carries its scope tag. */





/* A11Y T3 office-live-07: white on `--red` measured 3.53:1 at 12 px. `--ink`
   on the same red reads 5.30:1 — the badge language the app uses everywhere
   else (`.btn.gold`, `.btn.green`, `.box.gold`) — and `--red` is untouched.
   NB for whoever picks up the sibling: the finding cited the steel default
   above as the example to follow, but #05080F on `--steel` measures 2.54:1,
   so that one is a failure of its own. It is a different repair (lighten the
   ink on a dark fill, not darken it on a light one) and no T3 finding covers
   it, so it is left standing and reported rather than smuggled in here. */


/* Item 2 — Sunday Live: YARDS (or a QB's / a Line's +MULT) now → next is the row's figure,
   the unit under it; the delta is next − now. */




/* ---- the deck screen's FRANCHISE / absence tags on a card face (`.keep-badge`, `.keep-abs`; also the keep grid's) ---- */
.keep-badge{position:absolute;z-index:2;font-family:var(--display);font-size:var(--t-label);line-height:1;padding:3px 5px;border-radius:var(--r-sm);pointer-events:none}
.keep-badge{top:31%;right:-4px;background:var(--gold);color:var(--ink)}
.keep-badge.is-fr{background:var(--green);color:var(--ink)}
/* A Line's trigram fills the photo slot KEEP sits on over a player's photo: on a Line the badge
   rides the tab instead, over the team tag the trigram already prints whole. */
/* THE HUB'S ONE CARD FACE (cards.js#faceHTML in flow — finish review item 8): the keep grid,
   the draft and redraft boards and kept pile, the Kaiten bid sheet. The match mock's anatomy:
   the position tab with its team code, the photo, the surname in caps, the skewed value chip
   (YARDS blue, a QB's or a Line's +MULT red) and, on a card that tires, the four fatigue pips.
   `.card` is an absolutely-placed flying card in /shared (the match layer); a `.bface` is in
   flow, `--cw` × `--ch` by default and fluid where a grid sizes it. It is its own inline-size
   container, so the surname's size follows the card: the largest that fits its `--nl`
   characters (cards.js writes it), 14 px at most and never under the 12 px label floor. */
.card.bface{position:relative;left:auto;top:auto;display:block;width:var(--cw,70px);height:var(--ch,100px);padding:0;border:0;background:none;font:inherit;color:inherit;will-change:auto;container-type:inline-size;flex:none;pointer-events:auto}
/* THE ONE PLAYER CARD is `.pc` (shared/design-system.css, markup cards.js#pcHTML — the mockup's card).
   A `.card.bface` above and a `.pc-w.mini` here only say how wide it is where it sits; the card
   scales from its own width. */
.pc-w.mini{width:84px}
/* The report's three piles (`.gz-kept/.gz-released/.gz-redraft`): mini-cards in a wrapping
   grid, the Franchise tag under its card. */





/* A pending Cut Day on the Front Office (RD71). */



/* A signature recipe's name is wider than a coins figure: the row wraps and the value keeps
   the right edge instead of pushing the label onto two broken lines. */




/* ---- Front Office (§4.7) ---- */
/* Both blocks are `auto-fit` grids: the day-0 `flex:1 1 220px` / `1 1 150px`
   rows put two cards on the first line and left the rest on a short second
   one at every width. `auto-fit` and not `auto-fill` because the item count
   is FIXED here (3 standings, 4 tiers) — auto-fill would leave the four tier
   cards at 130 px each beside three empty tracks in a 1004 px tablet column.
   Cards stretch, so a row of them shares one height. */



/* The unread report's CTA pins to the bottom of its card whatever the two
   beside it need. */





/* What a tier pays (shared/tiers.js#tierRewards, 2026-09-24): coins, coach
   roll, signature plays — one label/value row each, the value right-aligned so
   the four cards read as one table. A reward the tier does not pay stays on
   its row in the dim ink: "nothing" is part of the ladder. */






@container (max-width: 340px){}
/* ==== 10 match — the OpenDesign mockup's MATCH, rule for rule (mockup CSS "MATCH", "juice layer", "overlays",
   "RED-ZONE VIGNETTE"); markup in app/match-view.js, behaviour in match.js ====
   One screen, no scroll: fixed-height strips on top, the fanned hand takes whatever height is left (cards are sized
   from it in match.js#layoutFan). The mockup is one phone column: portrait is that column at any width (capped at
   440 px), Colour: gold = the score and PLAY, blue = YARDS, red =
   MULT and the boss, teal = success and a first down. Every rule is scoped under `.match-root` (the mockup's
   `#match`): its names (`.card`, `.hint`, `.field`, `.tag`, `.pos`…) are generic ones the rest of the sheet knows. */
#screen:has(> .match-root){padding:0;gap:0;overflow:hidden}
.match-root{position:relative;display:flex;flex-direction:column;width:100%;max-width:440px;height:100%;min-height:0;margin:0 auto}
.match-root .mhead{display:grid;grid-template-columns:44px minmax(0,1fr);align-items:center;gap:var(--s2);padding:var(--s1) var(--s3) var(--s2)}
.match-root .mhead.has-fs{grid-template-columns:44px minmax(0,1fr) 44px}   /* the fullscreen key mirrors the menu key; absent, the board keeps the full width */
.match-root .board{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:var(--s3);min-height:48px;padding:2px var(--s3);border:var(--outline);border-radius:var(--r-md);background:var(--ink);box-shadow:var(--drop-sm),inset 0 0 0 1px var(--chalk-05)}
.match-root .board .r{text-align:right}
.match-root .board .lbl{white-space:nowrap}   /* "Q4 · 2-MIN" must never wrap into two lines, at any width */
.match-root .board .score{font-family:var(--num);font-size:40px;line-height:.85;color:var(--wy);white-space:nowrap;text-align:center;text-shadow:0 3px 0 var(--ink);font-variant-numeric:tabular-nums}
.match-root .board .score small{font-size:18px;color:var(--dim);margin-left:2px}
.match-root .board .score.pop{animation:mt-pop var(--t-slow) var(--ease-pop)}
.match-root .board .clk{font-family:var(--num);font-size:24px;line-height:.9;color:var(--chalk);white-space:nowrap}

.match-root .mbody{display:flex;flex-direction:column;gap:var(--s2);min-height:0;overflow:hidden;padding:0 var(--s3) var(--s1)}
.match-root .mbody>:not(.hand){flex:none}
.match-root .mbody.a{flex:none;padding-bottom:0}
.match-root .mbody.b{flex:1 1 auto;padding-top:var(--s2)}

/* field strip — broadcast graphic, one row (a button: it opens the drive log) */
.match-root .field{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:var(--s3);width:100%;padding:6px var(--s3);border:var(--outline);border-radius:var(--r-md);background:var(--panel);box-shadow:var(--drop-sm);text-align:left;color:inherit;font:inherit;cursor:pointer}
.match-root .dd{font-family:var(--num);font-size:26px;line-height:.9;white-space:nowrap}
.match-root .los{font-size:12px;color:var(--dim);font-weight:700;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap}
.match-root .turf{--ball:25;--fd:35;position:relative;height:28px;border:var(--outline);border-radius:var(--r-sm);overflow:hidden;
  background:
    repeating-linear-gradient(90deg,transparent 0 calc(10% - 1px),color-mix(in srgb,var(--wy) 35%,transparent) calc(10% - 1px) 10%),
    repeating-linear-gradient(90deg,var(--panel-hi) 0 10%,var(--panel) 10% 20%)}
.match-root .turf .play{position:absolute;top:0;bottom:0;left:8%;right:8%}
.match-root .turf .ez{position:absolute;top:0;bottom:0;width:8%;display:grid;place-items:center;font-family:var(--display);font-size:14px;color:var(--wy)}
.match-root .turf .ez.own{left:0;background:var(--ink)}
.match-root .turf .ez.opp{right:0;background:repeating-linear-gradient(135deg,var(--wy) 0 5px,var(--dim) 5px 10px);color:var(--ink)}
.match-root .turf .mark{position:absolute;inset:0;transform:translateX(calc(var(--at) * 1%));transition:transform 700ms var(--ease-out);pointer-events:none}
.match-root .turf .mark.fd{--at:var(--fd)}
.match-root .turf .mark.ball{--at:var(--ball)}
.match-root .turf .mark.fd::before{content:"";position:absolute;top:0;bottom:0;left:-2px;width:4px;background:var(--first)}
.match-root .turf .mark.ball::before{content:"";position:absolute;top:0;bottom:0;left:-1px;width:3px;background:var(--wp)}
.match-root .turf .mark.ball::after{content:"";position:absolute;top:50%;left:-10px;width:20px;height:13px;margin-top:-6.5px;border-radius:50%;background:var(--wp);border:2px solid var(--ink);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--wy) 20%,transparent)}
.match-root .turf.fd-off .mark.fd,.match-root .turf.is-off .mark{opacity:0}

/* boss — the enemy and its intents, one row */
.match-root .boss{display:grid;grid-template-columns:36px minmax(0,1fr) 16px;align-items:center;gap:var(--s2);width:100%;min-height:48px;padding:4px var(--s3) 4px var(--s2);border:var(--outline);border-radius:var(--r-md);background:var(--ink);box-shadow:var(--drop-sm),inset 0 0 0 2px var(--p-16);text-align:left;color:inherit;cursor:pointer}
.match-root .boss>.ico{color:var(--dim)}
.match-root .intents{display:flex;gap:4px;overflow:hidden}
.match-root .intent{flex:none;display:inline-flex;align-items:center;gap:4px;min-height:26px;padding:1px 8px 1px 4px;border:2px solid var(--wp);border-radius:var(--r-pill);background:var(--ink);color:var(--wp);font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap}
.match-root .intent .ico{width:14px;height:14px}
.match-root .intent.mod{background:var(--ink);color:var(--wy);border-color:var(--dim);padding-left:8px}
.match-root .intent.hot{animation:mt-shake 360ms var(--ease-out);background:var(--p-16)}
@keyframes mt-shake{20%{transform:translateX(-3px)}40%{transform:translateX(3px)}60%{transform:translateX(-2px)}80%{transform:translateX(2px)}}

/* the bench — starters, coach relics, the audible key (a Slay the Spire top bar) */
.match-root .bench{display:flex;align-items:center;gap:var(--s2);min-height:44px}
.match-root .starters{display:flex;align-items:center;gap:4px;min-height:44px;padding:0 6px;border:var(--outline);border-radius:var(--r-md);background:var(--panel);box-shadow:var(--drop-sm)}
.match-root .bench-coaches{flex:1 1 auto;min-width:0;display:grid;gap:2px}
.match-root .bench-lbl{font-size:var(--fs-xs);font-weight:700;letter-spacing:.14em;line-height:1;text-transform:uppercase;color:var(--dim)}
.match-root .bench .relics{flex:1 1 auto;justify-content:flex-start}
.match-root .relics .relic.is-rare{--rc:var(--wt)}
.match-root .auds{display:flex;gap:4px}
.match-root .aud{position:relative;display:grid;place-items:center;width:44px;height:44px;border:var(--outline);border-radius:var(--r-md);background:var(--panel);color:var(--wy);box-shadow:var(--drop-sm),inset 0 0 0 2px var(--y-16);transition:transform var(--t-fast) var(--ease-out)}
.match-root .aud .ico{width:22px;height:22px}
.match-root .aud .k{position:absolute;right:3px;bottom:1px;font-family:var(--num);font-size:14px;line-height:1;color:var(--chalk)}
.match-root .aud[aria-pressed="true"]{background:var(--win);color:var(--ink);transform:translateY(-3px)}
.match-root .aud[aria-pressed="true"] .k{color:var(--ink)}
.match-root .aud:disabled{background:color-mix(in srgb,var(--wy) 4%,transparent);color:var(--faint);box-shadow:none;cursor:default}
.match-root .aud:disabled .k{color:var(--faint)}

/* the counter — YARDS × MULT, fixed height in every state */
.match-root .counter[aria-busy="true"] .playname{color:var(--first)}
.match-root .counter{display:grid;gap:6px;border:var(--outline);border-radius:var(--r-lg);background:var(--ink);box-shadow:var(--drop);padding:6px var(--s3) var(--s2)}
.match-root .counter .row1{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s2);min-height:26px}
.match-root .playname{font-family:var(--display);font-size:26px;line-height:1;text-transform:uppercase;letter-spacing:.02em;text-shadow:0 2px 0 var(--ink)}
/* Balatro wobble: every letter of the play name bobs on its own beat, twice each time the name changes (a permanent per-letter loop kept the compositor busy) */
.match-root .playname span{display:inline-block;animation:mt-wave 1.8s ease-in-out 2;animation-delay:calc(var(--i,0) * -110ms)}
@keyframes mt-wave{50%{transform:translateY(-2px) rotate(-2deg)}}
.match-root .lvl{flex:none;font-size:12px;font-weight:700;letter-spacing:.08em;color:var(--dim);white-space:nowrap;text-transform:uppercase}
.match-root .calc{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr) auto minmax(0,1.15fr);align-items:center;gap:6px;width:100%;margin-top:4px;padding:0;color:inherit;font:inherit;text-align:center;cursor:pointer}
.match-root .calc:disabled{cursor:default}
.match-root .counter[data-mode="return"] .calc{display:none}
.match-root .counter[data-mode="return"] .lines{flex-wrap:wrap;height:auto;overflow:visible;mask-image:none;-webkit-mask-image:none}   /* the returner's four figures, whole */   /* a kick return prices nothing: no empty YARDS × MULT that looks like a decision (L2) */
.match-root .calc .x{font-family:var(--display);font-size:26px;color:var(--dim);line-height:1}
.match-root .cbox{position:relative;display:grid;place-items:center;height:46px;border:var(--outline);border-radius:var(--r-md);box-shadow:var(--drop-sm);font-family:var(--num);font-size:36px;line-height:1;color:var(--wy);font-variant-numeric:tabular-nums}
.match-root .cbox.yds{background:var(--wb-deep);color:var(--wy);text-shadow:0 3px 0 var(--ink)}
.match-root .cbox.mlt,.match-root .cbox.gain{text-shadow:0 3px 0 var(--ink)}
.match-root .cbox.mlt{background:var(--wr-deep);color:var(--wy)}
.match-root .cbox.gain{background:var(--panel);color:var(--wy)}
.match-root .cbox .cap{position:absolute;top:-10px;left:8px;padding:0 5px;border-radius:var(--r-xs);background:var(--ink);font-family:var(--ui);font-size:var(--fs-xs);font-weight:700;letter-spacing:.1em;color:var(--soft);line-height:1.6}
/* the GAIN box lights up (a ring pulsing outside it) when the play would move the chains (1ST, green) or score (TD, gold) */
.match-root .cbox.gain[data-goal]{--goal:var(--first)}
.match-root .cbox.gain[data-goal="td"]{--goal:var(--gold)}
.match-root .cbox.gain[data-goal]{border-color:var(--goal);box-shadow:var(--drop-sm),0 0 14px var(--goal)}
.match-root .cbox.gain[data-goal]::before{content:"";position:absolute;inset:-6px;border:3px solid var(--goal);border-radius:var(--r-lg);pointer-events:none;animation:mt-goal 1100ms var(--ease-out) infinite}
@keyframes mt-goal{0%{opacity:.95;transform:scale(.94)}100%{opacity:0;transform:scale(1.12)}}
@media (prefers-reduced-motion: reduce){.match-root .cbox.gain[data-goal]::before{animation:none;opacity:.8}}
:root[data-motion="reduce"] .match-root .cbox.gain[data-goal]::before{animation:none;opacity:.8}
.match-root .cbox.pop{animation:mt-pop var(--t-slow) var(--ease-pop)}
@keyframes mt-pop{40%{transform:scale(1.14) rotate(-2deg)}}
/* the itemised lines: one row that scrolls sideways, newest line kept in view */
.match-root .lines{display:flex;align-items:center;gap:4px;height:26px;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;mask-image:linear-gradient(90deg,#000 calc(100% - 18px),transparent);-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 18px),transparent);padding-left:2px;padding-right:18px;overscroll-behavior-x:contain;touch-action:pan-x}   /* one row that scrolls to the right; the right edge fades to say there is more */
.match-root .lines::-webkit-scrollbar{display:none}
.match-root .ln{flex:none;display:inline-flex;align-items:center;min-height:24px;padding:0 8px;border:2px solid var(--ink);border-radius:var(--r-sm);font-size:12px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;white-space:nowrap;background:var(--chalk-08);animation:mt-lnin var(--t-med) var(--ease-pop) both}
.match-root .ln.m{background:var(--ink);color:var(--wp);border-color:var(--wp)}
.match-root .ln.y{background:var(--ink);color:var(--wbt);border-color:var(--wb)}
.match-root .ln.bad{background:var(--p-16);color:var(--wp)}
.match-root .ln.ok{background:var(--y-16);color:var(--wy)}
.match-root .ln.ev{background:var(--chalk);color:var(--ink)}
.match-root .ln.qb{border-color:var(--wo);color:var(--wo)}
.match-root .lines:not(:has(.fresh)) .ln{animation:none}
.dl{font-family:var(--ui);font-size:12px;font-weight:700;margin-left:4px}
@keyframes mt-lnin{from{opacity:0;transform:translateY(6px) scale(.9)}}
.match-root .hint{flex:none;font-size:var(--fs-small);color:var(--dim);font-weight:500;line-height:1.35;white-space:nowrap}
.match-root .hint.is-refusal{flex:1 1 auto;min-width:0;white-space:normal;color:var(--wp)}
.match-root .lines:has(.hint.is-refusal){height:auto;min-height:26px;overflow:visible;mask-image:none;-webkit-mask-image:none}

/* odds — one success figure and a risk bar, the detail on tap */
.match-root .odds{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:var(--s2);min-height:44px;width:100%;padding:0 var(--s1);border-radius:var(--r-sm);text-align:left;color:inherit;font:inherit;cursor:pointer}
.match-root .odds .pct{font-family:var(--num);font-size:24px;line-height:1;color:var(--win);white-space:nowrap}
.match-root .odds .pct small{font-family:var(--ui);font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--dim);margin-left:4px}
.match-root .odds .more{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--dim);white-space:nowrap;display:inline-flex;align-items:center;gap:2px}
.match-root .odds:disabled{cursor:default}
.match-root .odds:disabled .pct{color:var(--faint)}
.dl.is-good{color:var(--win)}
.dl.is-bad{color:var(--loss)}
@media (hover:hover){.match-root .odds:not(:disabled):hover{background:var(--chalk-05)}}
.match-root .riskbar,.riskbar{display:flex;height:12px;border:2px solid var(--ink);border-radius:var(--r-sm);overflow:hidden;background:var(--panel)}
.riskbar i{display:block;height:100%;flex:0 0 auto;width:calc(var(--w,0) * 1%)}
.riskbar .bp{background:repeating-linear-gradient(135deg,var(--wo) 0 4px,var(--ink) 4px 6px)}
.riskbar .sc{background:var(--win)}
.riskbar .ms{background:var(--faint)}
.riskbar .ls{background:var(--loss)}

/* the hand — a Balatro fan filling the rest of the screen, sat on the bottom edge */
.match-root .hand{--step:44px;position:relative;flex:1 1 auto;min-height:0;display:flex;justify-content:center;align-items:flex-end;padding:22px 0 16px;margin:0 calc(var(--s3) * -1)}
.match-root .hand .row{display:flex;gap:0;justify-content:center;align-items:flex-end}
/* the hand always sits in two fanned rows; the front row overlaps the back one, which keeps its index and photo showing */
.match-root .hand.two{flex-direction:column;align-items:stretch;justify-content:flex-end}
.match-root .hand.two .row.back{margin-bottom:calc(var(--ov,0px) * -1)}
.match-root .hand .deck{position:absolute;top:0;right:var(--s3);z-index:0;display:inline-flex;align-items:center;gap:4px;padding:0 8px;min-height:22px;border:2px solid var(--ink);border-radius:var(--r-sm);background:var(--ink-35);font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--dim);pointer-events:none}
.match-root .hand .deck b{font-family:var(--num);font-weight:400;font-size:16px;color:var(--chalk)}
.match-root .card{position:relative;z-index:1;flex:none;width:var(--cw);margin-left:calc(var(--step) - var(--cw));transform-origin:50% 120%;transform:translateY(var(--dy,0)) rotate(var(--rot,0deg));transition:transform var(--t-med) var(--ease-pop)}
.match-root .card:first-child{margin-left:0}
.match-root .card .in{display:block;transform-origin:50% 100%;transform:translateY(var(--ly,0px)) scale(var(--ls,1)) perspective(420px) rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg));transition:transform 180ms var(--ease-pop),box-shadow var(--t-fast) var(--ease-out),filter var(--t-fast) var(--ease-out);pointer-events:none}
/* foil shine on the chosen cards */
.match-root .card[aria-pressed="true"] .pc-f::after{opacity:1;z-index:3;content:"";position:absolute;inset:0 auto 0 0;width:300%;pointer-events:none;background:linear-gradient(115deg,transparent 44%,color-mix(in srgb,var(--wy) 45%,transparent) 50%,transparent 56%);animation:foil 1.6s linear infinite}
.match-root .card .in{position:relative}
.match-root .card.deal .in{animation:mt-deal var(--t-slow) var(--ease-out) both;animation-delay:calc(var(--i,0) * 40ms)}
.match-root .card[aria-pressed="true"]{transform:translateY(calc(var(--dy,0px) - 18px)) rotate(var(--rot,0deg));z-index:5}
.match-root .card[aria-pressed="true"] .in{box-shadow:0 0 0 3px var(--first),0 7px 0 var(--ink)}
.match-root .card[aria-disabled="true"] .in{filter:grayscale(.4) brightness(.72)}
/* a card from the same NFL team as the picked cards (a third would unlock the SAME TEAM MULT): gold and dashed, so it never reads as a selected (teal) card */
.match-root .card.mate .in{box-shadow:0 0 0 3px var(--gold),var(--drop);outline:2px dashed var(--ink);outline-offset:-5px}
.match-root .card:focus-visible{outline:none;z-index:6}
.match-root .card:focus-visible .in{box-shadow:0 0 0 3px var(--wy),0 0 0 6px var(--wp),var(--drop)}
.match-root .card.fly{transform:translateY(-60px) rotate(0deg) scale(.85);opacity:0;transition:transform var(--t-exit) var(--ease-in),opacity var(--t-exit) var(--ease-in)}
.match-root .card.hit .in{animation:mt-hit 280ms var(--ease-pop)}
@keyframes mt-deal{from{opacity:0;transform:translateY(40px) rotate(8deg)}}
@keyframes mt-hit{50%{transform:translateY(-6px) scale(1.1) rotate(-3deg)}}
/* hover focus: the card under the pointer grows and rises above everything, but only its artwork moves —
   the hit box keeps its fan slot, so the next card over is always one pixel away (resolved in JS by fan order) */
.match-root .card.hov{z-index:8}
.match-root .card.hov .in{--ly:-16px;--ls:1.14;box-shadow:0 0 0 3px var(--wy),0 12px 0 var(--ink)}
.match-root .card.hov[aria-pressed="true"] .in{box-shadow:0 0 0 3px var(--first),0 0 0 6px var(--wy),0 12px 0 var(--ink)}
.match-root .hand.hovering .card:not(.hov) .in{filter:brightness(.82) saturate(.85)}
.match-root .hand.hovering .card[aria-disabled="true"]:not(.hov) .in{filter:grayscale(.4) brightness(.62)}
/* chosen cards stay still (they used to bob in a 1.36 s loop that read as dancing to the music); only the counter boxes move with it */
/* the counter breathes with the music: the kick and bass nudge YARDS, the snare MULT, the chords and lead GAIN */
.match-root .cbox{transition:scale 90ms var(--ease-out)}
.match-root.bt-y .cbox.yds,.match-root.bt-m .cbox.mlt,.match-root.bt-g .cbox.gain{scale:1.07}
.match-root[data-heat="1"].bt-y .cbox.yds,.match-root[data-heat="1"].bt-m .cbox.mlt,.match-root[data-heat="1"].bt-g .cbox.gain{scale:1.1}
.match-root[data-heat="2"].bt-y .cbox.yds,.match-root[data-heat="2"].bt-m .cbox.mlt,.match-root[data-heat="2"].bt-g .cbox.gain{scale:1.14}
.match-root .yardpop{position:absolute;left:50%;top:-6px;transform:translateX(-50%);z-index:3;padding:0 6px;border:2px solid var(--ink);border-radius:var(--r-sm);background:var(--wb-deep);color:var(--wy);font-family:var(--num);font-size:20px;line-height:1.1;white-space:nowrap;animation:rise 800ms var(--ease-out) forwards;pointer-events:none}

/* keys — the play row gains FG and PUNT on 4th down instead of growing a row. One grid for the whole foot: every key is
   `--keyh` tall (SNEAK · TIMEOUT and the play row alike), the rows share the foot's edges and gap, the PLAYBOOK key is a
   `--keyh` square, PUNT and FG are the same width wherever they appear (alone or together: `--kw`, a seventh of what the
   other keys leave: 2 of them + 1.5 for PLAY = 3.5 shares) and PLAY, the primary key, takes the rest and is the widest. */
.match-root .foot{--keyh:54px;padding:var(--s1) var(--s3) var(--s1);background:linear-gradient(180deg,color-mix(in srgb,var(--ink) 0%,transparent),color-mix(in srgb,var(--ink) 95%,transparent) 22%);display:grid;gap:var(--s2)}
.match-root .keys{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);gap:var(--s2)}
.match-root .keys .key,.match-root .playrow .key{height:var(--keyh);min-height:var(--keyh)}
.match-root .keys .key{font-size:19px;padding:4px;flex-direction:column;gap:0}
/* the PLAYBOOK key sits at the left of the play row (it used to be the header's right key): one thumb, the whole foot */
.match-root .playrow{--kw:calc((100% - var(--keyh) - 3 * var(--s2)) / 3.5);display:grid;grid-template-columns:var(--keyh) minmax(0,1fr);gap:var(--s2)}
.match-root .playrow .pb{padding:0}
.match-root .playrow .pb .ico{width:26px;height:26px}
.match-root .playrow.fourth{grid-template-columns:var(--kw) var(--kw) var(--keyh) minmax(0,1fr)}
.match-root .playrow.fourth.one{grid-template-columns:var(--kw) var(--keyh) minmax(0,1fr)}
.match-root .playrow .kick{font-size:19px;padding:4px 2px;flex-direction:column;gap:0}
.match-root .playrow .kick .sub{max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}   /* a safety: "38% · 67 yd" fits `--kw` at 360px and up, never over the edge */
.match-root .playkey .n{font-family:var(--ui);font-size:15px;font-weight:700;letter-spacing:.06em}
.match-root .playrow.fourth .playkey{font-size:24px}

/* short phones (an iPhone SE, a small Android, Safari's visible 664 px): the hand is what the player reads and taps,
   so every other block gives height back until the cards are as big as the screen allows. Tier 1 (≤ 940 px) tightens
   each strip; tier 2 (≤ 620 px) puts the boss crest and the bench on ONE row. Nothing is hidden at tier 1 (the
   counter's line chips stay, one scrolling row — owner 2026-10-05); at tier 2 the boss' rule chips are one tap away
   (the crest opens the boss sheet that lists them).
   Tier 1 ends at 940 and not lower for one reason: a taller column must never give SMALLER cards. The roomy chrome above
   it costs ~150 px more than the compact one (counter 110 -> 182, head, field, foot), so the card size, read against the
   column's height, is a ramp that falls off a cliff wherever the compact tier stops, unless the compact chrome has by then
   already let the cards reach their ceiling: 2 rows of 4 on a <= 440 px hand are at most 102 px wide (112 for a thin hand),
   and the roomy chrome gives the hand 112 px wide cards from 940 px up (2.8 x card + 626 local px). It used to stop at
   740, where the hand dropped from 89 to 47 px on a 390 px phone: entering fullscreen (Android Chrome 412 x 780 -> 915)
   made the cards smaller. tests/fullscreen.spec.js walks the ramp. */
@container col (max-height:940px){
  .match-root .mhead{padding-bottom:var(--s1)}
  .match-root .board{min-height:44px}
  .match-root .board .score{font-size:32px}
  .match-root .mbody{gap:4px}
  .match-root .field{padding:3px var(--s3)}
  .match-root .dd{font-size:22px}
  .match-root .turf{height:22px}
  .match-root .boss{min-height:40px;padding:2px var(--s3) 2px var(--s2)}
  .match-root .intent{min-height:22px}
  .match-root .counter{gap:2px;padding:4px var(--s3) 6px}
  .match-root .counter .row1{min-height:22px}
  .match-root .playname{font-size:22px}
  .match-root .calc{margin-top:2px}
  .match-root .cbox{height:36px;font-size:28px}
  .match-root .odds{min-height:30px}
  /* the short-screen shrink keeps a 44px hit area: the plate is smaller than its tap zone */
  .match-root .calc,.match-root .odds,.match-root .boss{position:relative}
  .match-root .calc::after,.match-root .odds::after,.match-root .boss::after{content:"";position:absolute;inset:-7px 0}
  .match-root .odds .pct{font-size:20px}
  .match-root .riskbar{height:10px}
  .match-root .foot{--keyh:46px;padding-top:2px;padding-bottom:2px;gap:4px}
  .match-root .hand{padding:20px 0 4px}
}
@container col (max-height:620px){
  .match-root .mbody.a{flex-flow:row wrap;align-items:center;column-gap:6px}
  .match-root .mbody.a>*{flex:0 0 100%}
  .match-root .mbody.a>.boss{flex:0 0 auto;width:auto;grid-template-columns:auto;min-height:44px;padding:2px 8px}
  .match-root .boss .intents,.match-root .boss>.ico{display:none}
  .match-root .mbody.a>.bench{flex:1 1 0;min-width:0}
  .match-root .bench .relics{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none}
  .match-root .bench .relics::-webkit-scrollbar{display:none}
}
/* the fullscreen key takes 52 px off the scoreboard: its two small stats and the score give the room back (the widest
   reading is "Q4 · 2-MIN" next to a 3-digit score, tests/fullscreen.spec.js measures it down to 320 px) */
@container col (max-width:400px){
  .match-root .mhead.has-fs .board{gap:var(--s2)}
  .match-root .mhead.has-fs .board .score{padding:0 var(--s2)}
}
@container col (max-width:380px){.match-root .mhead.has-fs .board .lbl{font-size:11px;letter-spacing:.02em}}
/* the narrowest phones (320 px): the scoreboard's two small stats keep one line each */
@container col (max-width:350px){
  .match-root .mhead.has-fs{gap:var(--s1)}
  .match-root .mhead.has-fs .board{padding:2px var(--s1)}
  .match-root .mhead.has-fs .board .score{padding:0 var(--s1)}
  .match-root .mhead.has-fs .board .lbl{font-size:10px}
  .tabhd .fs-key{display:none}   /* the header would leave its title ~24px: fullscreen stays in the settings sheet and the Settings screen */
  .match-root .playrow .kick .sub{font-size:11px;letter-spacing:0}
  .match-root .playrow.fourth .playkey{font-size:22px}
  .match-root .board{gap:var(--s2);padding:2px var(--s2)}
  .match-root .board .score{font-size:28px}
  .match-root .board .score small{font-size:14px}
  .match-root .board .clk{font-size:20px}
}

/* the Kaiten market on a short phone: the counters, the live line and the belt header give height to the lane */
@container col (max-height:740px){
  .mk-root{gap:var(--s2)}
  .mk-stats .od-stat{padding:2px var(--s3)}
  .mk-stats .big{font-size:22px}
  .feed{min-height:32px}
  .belt-hd .note{display:none}
  .conv{min-height:200px}
  .conv-hood{height:16px}
}
@container col (max-width:350px){.mk-stats .lbl{letter-spacing:.02em;white-space:nowrap}}

/* ---- juice layer (juice.js) ---- */
.match-root .fx,.fin-root .fx,.reveal .fx{position:absolute;inset:0;z-index:var(--z-fx);pointer-events:none;overflow:hidden}
/* the particle canvas (particles.js) fills its layer; its bitmap is sized in device pixels by JS */
.fx-canvas{position:absolute;inset:0;width:100%;height:100%;display:block;pointer-events:none}
.match-root .flash,.reveal .flash{position:absolute;inset:0;z-index:var(--z-flash);pointer-events:none;background:var(--fc,var(--gold));opacity:0;mix-blend-mode:screen}
.match-root .flash.go,.reveal .flash.go{animation:mt-flash 420ms var(--ease-out)}
@keyframes mt-flash{0%{opacity:var(--fo,.38)}100%{opacity:0}}
.match-root.shake{animation:mt-shake-s 320ms var(--ease-out)}
.match-root.shake-hard{animation:mt-shake-h 520ms var(--ease-out)}
@keyframes mt-shake-s{20%{transform:translate(-3px,2px)}40%{transform:translate(3px,-2px)}60%{transform:translate(-2px,1px)}80%{transform:translate(1px,0)}}
@keyframes mt-shake-h{10%{transform:translate(-6px,3px) rotate(-.6deg)}25%{transform:translate(6px,-4px) rotate(.6deg)}40%{transform:translate(-5px,2px)}60%{transform:translate(4px,-2px)}80%{transform:translate(-2px,1px)}}
.match-root .flyer{position:absolute;inset:0;transition:transform 440ms cubic-bezier(.5,0,.75,.2)}
.match-root .flytext{position:absolute;left:0;top:0;transform:translate(-50%,-50%);padding:0 8px;border:2px solid var(--ink);border-radius:var(--r-sm);background:var(--first);color:var(--ink);font-family:var(--num);font-size:28px;line-height:1.1;white-space:nowrap;box-shadow:var(--drop-sm)}
.match-root .flytext.neg{background:var(--ink);color:var(--wp);border-color:var(--wp)}
.match-root .flyer.go .flytext{animation:mt-shrinkfly 440ms var(--ease-in) forwards}
@keyframes mt-shrinkfly{to{transform:translate(-50%,-50%) scale(.55);opacity:.7}}
/* MULT on fire: past ×4 the red box heats up, embers flicker above it */
.match-root .cbox.mlt.hot{animation:mt-heat 900ms ease-in-out infinite alternate}
.match-root .cbox.mlt.hot::before{content:"";position:absolute;left:10%;right:10%;top:-14px;height:18px;background:linear-gradient(0deg,var(--gold) 0 35%,var(--mult) 35%);clip-path:polygon(0 100%,8% 40%,22% 70%,34% 10%,50% 60%,64% 0,78% 55%,90% 30%,100% 100%);animation:mt-ember 240ms steps(2) infinite alternate;pointer-events:none}
@keyframes mt-heat{from{transform:scale(1)}to{transform:scale(1.04)}}
@keyframes mt-ember{from{transform:scaleY(.8) translateY(2px);opacity:.85}to{transform:scaleY(1.15) translateY(-1px);opacity:1}}
/* a pick is the card's own lift (its transition rides --ease-pop, whose overshoot is the pop): no keyframe on `.in` — it replaced the whole transform for 220 ms and dropped the hover lift under it */

/* ---- RED-ZONE VIGNETTE — hard pixel bands, no blur ---- */
.match-root .rzv{position:absolute;inset:0;z-index:20;pointer-events:none;opacity:0;box-shadow:inset 0 0 0 3px var(--wr),inset 0 0 0 8px color-mix(in srgb,var(--wr) 40%,transparent),inset 0 0 0 14px color-mix(in srgb,var(--wr) 18%,transparent);transition:opacity var(--t-slow) var(--ease-out)}
.match-root[data-rz="1"] .rzv{opacity:.75;animation:mt-rzpulse 1.36s ease-in-out infinite}
.match-root[data-rz="2"] .rzv{opacity:1;box-shadow:inset 0 0 0 5px var(--wr),inset 0 0 0 11px color-mix(in srgb,var(--wr) 50%,transparent),inset 0 0 0 18px color-mix(in srgb,var(--wr) 25%,transparent),inset 0 0 0 26px color-mix(in srgb,var(--wr) 10%,transparent);animation:mt-rzpulse .6s ease-in-out infinite}
@keyframes mt-rzpulse{50%{opacity:.3}}

/* ---- the match's sheets: what the mockup's sheets hold beyond the shared rows (`.rowc`, `.acts`, `.note`) ---- */
.sheet .rows.tight .rowc,.sheet .rowc.nl{min-height:44px}
.sheet .rowc.nl{grid-template-columns:minmax(0,1fr) auto}
.sheet .rows.tight .rowc.nl{padding-block:var(--s1)}
.sheet .rowc.sub{margin-left:var(--s4);width:auto;background:transparent}
.sheet .rowc.is-total{background:color-mix(in srgb,var(--wy) 6%,transparent)}
.sheet .rowc.is-lit .v{color:var(--wo)}
.sheet .rowc.is-off,.sheet .rowc.is-out{opacity:.55}
.sheet .rowc.is-malus .v{color:var(--loss)}
.sheet .rowc.spec .t{color:var(--wp)}
.sheet .rowc .riskbar.bar{position:relative;width:100%;margin-top:4px}
.sheet .rowc .riskbar.bar[style*="--avg"]::after{content:"";position:absolute;top:0;bottom:0;left:var(--avg);width:2px;margin-left:-1px;background:color-mix(in srgb,var(--wbt) 60%,transparent)}
.sheet .rule-n{font-size:32px;width:20px}
.sheet .evt b{font-weight:700;color:var(--chalk)}
.sheet p.lbl{margin-bottom:var(--s2)}
.sheet .rowc .v small{font-size:12px}

/* ==== 11 final — the OpenDesign mockup's finalSheet ("Match over"), as the screen a match ends on ====
   A gold-edged `.sheet` sat on the bottom of the mockup's phone column; the hero figure, BY QUARTER rows that open
   their plays (`.qplays`), a note and one gold key are the mockup's own (its `.final-hero`, `.rows`, `.rowc`,
   `.qplays`, `.acts` are /shared's). Markup: app/final-view.js. */
#screen:has(> .fin-root){padding:0;gap:0;overflow:hidden}
.fin-root{position:relative;display:flex;flex-direction:column;justify-content:flex-end;width:100%;max-width:440px;height:100%;min-height:0;margin:0 auto}
.fin-root>.sheet{position:relative;left:auto;right:auto;bottom:auto;flex:0 1 auto;min-height:0;max-height:100%;transform:none;transition:none}
.fin-root .fin-hero .big{line-height:.85}
.fin-root .fin-pity{margin-top:var(--s2);background:var(--gold);color:var(--ink)}
.fin-root .q-n{font-size:30px;width:36px}
.fin-root .fin-q.is-loading{cursor:default;color:var(--dim)}
.fin-root .fin-q-err{font-weight:700}
.fin-root .fin-level.is-up .t{color:var(--gold)}
.fin-root .rowc.empty .d{font-size:var(--fs-small)}
.fin-root .fin-card{display:grid;gap:var(--s1);justify-items:start;margin-bottom:var(--s3);padding:var(--s3) var(--s4);border:var(--outline);border-radius:var(--r-md);background:var(--ink);box-shadow:var(--drop-sm)}
.fin-root .fin-card-score{font-size:34px;color:var(--gold)}
.fin-root .fin-card-line{font-size:var(--fs-small);color:var(--soft);font-weight:500}
.fin-root #shareUrl{position:fixed;left:-9999px;top:0;width:1px;height:1px;min-height:0;padding:0;opacity:0;pointer-events:none}
.fin-root .fin-share{display:grid;margin-bottom:var(--s3)}

/* ==== 12 hover parity — a mouse on a desktop browser (the client is the same portrait column there). /shared already lifts `.btn`
   (brightness + a 1 px rise), `.lrow` and `.mini`; these are the controls that had nothing, and each borrows the
   affordance of the family it belongs to rather than inventing a look. `.card` is deliberately absent:
   cards.js#initTilt gives it a 3D tilt on pointermove, a stronger hover response than any of these. */
@media (hover:hover){
  .lot:not(.closed):hover{background:var(--panel-hi)}
  .lot:not(.closed):hover .lot-card .pc{transform:translateY(-3px) scale(1.04)}
}

/* ==== 16 Prime Time Arcade skin (2026-09-30) — mostly the look: fills, bevels, rules, pseudo-element dressing.
   The sections above own layout at every breakpoint; the few padding / inset / skew values here belong to the dressing itself. Balatro's chips and
   jokers, dressed like a Sunday-night broadcast: a score bug with a slanted slab, poker-chip counters, a
   lower-third strip behind every banner, a stadium-light ground. ==== */
/* the scoreboard reads as a TV score bug: a gold rule on top, a slanted slab under the score */
.match-root .board{background:linear-gradient(180deg,var(--panel) 0 46%,var(--ink) 46%);box-shadow:var(--drop-sm),inset 0 3px 0 var(--gold)}
.match-root .board .score{position:relative;isolation:isolate;padding:0 var(--s3)}
.match-root .board .score::before{content:"";position:absolute;inset:-6px 0 -4px;z-index:-1;border-inline:3px solid var(--gold);background:var(--panel-hi);transform:skewX(-12deg)}
/* the three counters are poker chips: a bevel and a dashed inner edge */
.match-root .cbox{box-shadow:var(--drop-sm),inset 0 2px 0 var(--chalk-28),inset 0 -4px 0 var(--ink-35)}
.match-root .cbox::after{content:"";position:absolute;inset:4px;border:2px dashed var(--chalk-28);border-radius:var(--r-sm);pointer-events:none}
.match-root .cbox.gain::after{border-color:var(--chalk-14)}
.match-root .calc .x{color:var(--gold);transform:rotate(-8deg);text-shadow:0 2px 0 var(--ink)}
/* the field strip: a leather-tan ball marker over the turf */
.match-root .field{box-shadow:var(--drop-sm),inset 0 -3px 0 var(--ink-35)}
/* coach tokens stand like jokers: a chalk rim and a deeper floor */
.relic:not(.empty){box-shadow:0 3px 0 var(--ink),inset 0 0 0 2px var(--chalk-28),inset 0 -4px 0 var(--ink-25)}
/* a key's label sits on the athletic face: a touch more tracking */
:is(.key,.btn){letter-spacing:.05em}
.sub-title,.sh-title,.sec-t{letter-spacing:.02em;text-transform:uppercase}
/* the cascade's tension (match.js#resolve): the calc climbs a level as MULT passes ×2 / ×4 / ×8 — a scale on the
   calc (the beat's scale stays on the boxes), a gold then white-hot ring on MULT */
.match-root .calc{transition:scale var(--t-med) var(--ease-pop)}
.match-root .calc[data-tension="1"]{scale:1.02}
.match-root .calc[data-tension="2"]{scale:1.04}
.match-root .calc[data-tension="3"]{scale:1.06}
.match-root .calc[data-tension="2"] .cbox.mlt{box-shadow:var(--drop-sm),0 0 0 3px var(--wo),inset 0 2px 0 var(--chalk-28),inset 0 -4px 0 var(--ink-35)}
.match-root .calc[data-tension="3"] .cbox.mlt{box-shadow:var(--drop-sm),0 0 0 3px var(--wy),0 0 0 6px var(--wo),inset 0 2px 0 var(--chalk-28),inset 0 -4px 0 var(--ink-35)}
.match-root.punch{animation:mt-punch 300ms var(--ease-out)}
@keyframes mt-punch{35%{scale:1.02}}
@media (prefers-reduced-motion: reduce){.match-root .calc[data-tension]{scale:none}.match-root.punch{animation:none}}
:root[data-motion="reduce"] .match-root .calc[data-tension]{scale:none}
:root[data-motion="reduce"] .match-root.punch{animation:none}
/* the final whistle: the score is gold (the palette's rule — gold is the score) on a lower-third slab */
.fin-root .fin-hero .fin-score{position:relative;isolation:isolate;padding:0 var(--s4);color:var(--gold);text-shadow:0 4px 0 var(--ink)}
.fin-root .fin-hero .fin-score::before{content:"";position:absolute;inset:18% -8%;z-index:-1;border-block:3px solid var(--gold);background:var(--ink-90);transform:rotate(-2deg)}
/* the boss reveal: the name sits on the red lower third, the crest under a stadium spotlight */
.rv-name{position:relative;isolation:isolate}
.rv-name::before{content:"";position:absolute;inset:14% -6%;z-index:-1;border-block:3px solid var(--wr);background:var(--ink-90);transform:rotate(-2deg)}
.rv-crest{background:radial-gradient(closest-side,var(--chalk-14),transparent)}
/* the Kaiten: the lot you lead wears the teal rule on its top edge, the lot you lost the lead on the red one */
.lot.lead{box-shadow:0 4px 0 var(--ink-90),inset 0 0 0 2px var(--wt),inset 0 4px 0 var(--wt)}
.lot.outbid{box-shadow:0 4px 0 var(--ink-90),inset 0 0 0 2px var(--wr),inset 0 4px 0 var(--wr)}
/* the hand (match.js#layoutFan, 2026-09-30): two straight rows, every card whole — no fan, no overlap; a picked card
   rises less than the row gap so it never covers the row above; the kick return's row sits in the middle of the box */
.match-root .hand.two{gap:8px}
.match-root .hand.two .row.back{margin-bottom:0}
.match-root .hand.two .card[aria-pressed="true"]{transform:translateY(-6px)}
.match-root .hand[data-mode="return"]{align-items:center}   /* the returner row sits mid-box, not at its top */

/* ==== 17 match hierarchy — the counter, the hand and PLAY are the heavy bands; the scene strips (field, boss,
   starters) sit flat on the felt: no outline, no drop, a quiet fill. They stay buttons (the field and the boss open
   their sheets), only quieter. ==== */
.match-root .field,.match-root .starters{border-color:transparent;background:var(--ink-35);box-shadow:none}
.match-root .boss{border-color:transparent;background:var(--ink-35);box-shadow:inset 0 0 0 2px var(--p-16)}

/* ==== 18 the guided tour (guide.js) — a ring on the one thing to tap (its 100vmax shadow is the dim) and a chalk
   bubble beside it with its own NEXT. Nothing here takes a tap but the bubble: the game stays live under the dim.
   On a step that points at something to tap the ring pulses (transform + opacity only); the motion switch and the OS setting still it. ==== */
.guide{position:fixed;inset:0;z-index:var(--z-guide);pointer-events:none}
.guide[hidden]{display:none}
.guide-ring{position:fixed;left:0;top:0;border-radius:var(--r-md);box-shadow:0 0 0 3px var(--gold),0 0 0 calc(100 * var(--cvh)) var(--ink-60)}
.guide[data-kind="tap"] .guide-ring::after{content:"";position:absolute;inset:-3px;border:3px solid var(--gold);border-radius:inherit;animation:guide-pulse 1.4s var(--ease-out) infinite}
@keyframes guide-pulse{from{transform:scale(1);opacity:.9}to{transform:scale(1.08);opacity:0}}
.guide-tip{position:fixed;left:0;top:0;width:min(320px,calc(100 * var(--cvw) - 24px));display:grid;gap:var(--s2);padding:var(--s3) var(--s4) var(--s4);border:var(--outline);border-radius:var(--r-lg);background:var(--chalk);color:var(--ink);box-shadow:var(--drop);pointer-events:auto}
.guide-tip.in{animation:guide-in 220ms var(--ease-out)}
@keyframes guide-in{from{opacity:0;transform:translateY(6px)}}
.guide-tip::before{content:"";position:absolute;left:calc(var(--ax,50%) - 8px);width:16px;height:16px;background:var(--chalk);border:var(--outline);transform:rotate(45deg)}
.guide-tip[data-side="below"]::before{top:-10px;border-right:0;border-bottom:0}
.guide-tip[data-side="above"]::before{bottom:-10px;border-left:0;border-top:0}
.guide-tip[data-side="over"]::before{display:none}
.guide-hd{display:flex;align-items:center;justify-content:space-between;gap:var(--s2);margin-bottom:calc(-1 * var(--s2))}
.guide-k{font-size:var(--fs-xs);font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--ink);opacity:.72}
.guide-skip{min-height:44px;min-width:44px;padding:0 var(--s1);color:var(--ink);font-size:var(--fs-label);font-weight:700;text-decoration:underline;text-underline-offset:3px}
@media (hover:hover){.guide-skip:hover{text-decoration-thickness:2px}}
.guide-t{margin:0;font-family:var(--display);font-size:var(--fs-h3);line-height:1.1;letter-spacing:.02em;text-transform:uppercase}
.guide-b{margin:0;font-size:var(--fs-body);font-weight:600;line-height:1.45}
.guide-b b{font-weight:800}
.guide-ok{justify-self:end;min-width:96px}
.guide-ok[hidden]{display:none}
.guide-tip :focus-visible{outline:3px solid var(--ink);outline-offset:2px}
.guide-live{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
@media (prefers-reduced-motion:reduce){.guide-tip.in,.guide[data-kind="tap"] .guide-ring::after{animation:none}}
:root[data-motion="reduce"] .guide-tip.in,:root[data-motion="reduce"] .guide[data-kind="tap"] .guide-ring::after{animation:none}

/* The played week's stats sheet (screens.js#weekStatsSheet): the head carries the boss's shield; the body is the week's score,
   its chips and the ranked days, the ones that count lit gold. A sheet scrolls on its own, so the day list needs no height. */
.ws-title{display:flex;align-items:center;gap:var(--s3)}
.ws-title .crest{flex:none}
.ws-body{display:flex;flex-direction:column;gap:var(--s3)}
.ws-state{margin:0;text-align:center}
.ws-score{align-items:center;text-align:center}
.ws-score .big{font-size:52px;line-height:.95}
.ws-chips{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--s2)}
.ws-chips .tag2{justify-self:auto;font-size:14px}
.ws-rule{margin:var(--s2) 0 0}
.ws-days{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s1)}
.ws-day{display:grid;grid-template-columns:4.2em minmax(0,1fr) auto 28px;align-items:center;gap:var(--s3);min-height:38px;padding:2px var(--s3);border:var(--outline);border-radius:var(--r-md);background:color-mix(in srgb,var(--ink) 28%,transparent)}
.ws-day .ws-n{font-size:var(--fs-label);font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--dim)}
.ws-day .ws-s{font-family:var(--num);font-size:22px;line-height:1}
.ws-day .ws-t{font-size:var(--fs-small);font-weight:600;color:var(--dim);white-space:nowrap}
.ws-day .ws-m{display:grid;place-items:center;color:var(--gold)}
.ws-day.counted{background:color-mix(in srgb,var(--wo) 14%,transparent);box-shadow:inset 0 0 0 2px var(--gold)}
.ws-day.counted .ws-s{color:var(--gold)}
.ws-day.off{opacity:.5;border-style:dashed;background:transparent}
/* the card stats sheet (screens.js#cardStatsBody): the per-game line, HOW IT PLAYS (a 5-pip meter per quality, the
   boss bars' ruler), the traits, the last games as one compact row. Every card face that opens it carries
   `data-card`: no iOS callout, no text selection under a long press. */
[data-card]{-webkit-touch-callout:none;-webkit-user-select:none;user-select:none}
.cs-body{display:grid;gap:var(--s3);margin-bottom:var(--s3)}
.cs-body:empty{display:none}
.cs-body .cs-pg{margin:0;color:var(--soft)}
.cs-body .cs-loading{margin:0}
.cs-sec{display:grid;gap:var(--s2)}
.cs-hd{margin:0;font-size:var(--fs-xs);letter-spacing:.08em;color:var(--dim)}
.cs-q{display:grid;gap:2px}
.cs-q-hd{display:grid;grid-template-columns:minmax(0,1fr) auto 76px;align-items:center;gap:var(--s2)}
.cs-q-hd .t{font-weight:700;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cs-meter{display:flex;gap:3px}
.cs-meter i{display:block;width:14px;height:10px;border:2px solid var(--ink);border-radius:var(--r-xs);background:var(--panel)}
.cs-meter i.on{background:var(--gold)}
.cs-q[data-level="1"] .cs-meter i.on,.cs-q[data-level="2"] .cs-meter i.on{background:var(--loss)}
.cs-q[data-level="4"] .cs-meter i.on,.cs-q[data-level="5"] .cs-meter i.on{background:var(--win)}
.cs-lv{font-size:var(--fs-xs);font-weight:700;text-transform:uppercase;letter-spacing:.04em;text-align:right;color:var(--dim)}
.cs-lv.lv-1,.cs-lv.lv-2{color:var(--loss)}
.cs-lv.lv-4,.cs-lv.lv-5{color:var(--win)}
.sheet .cs-desc{margin:0;font-size:var(--fs-xs);line-height:1.35;color:var(--dim)}
.cs-games{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s1)}
.cs-g{display:grid;justify-items:center;gap:0;padding:4px 2px;border:2px solid var(--ink);border-radius:var(--r-sm);background:var(--panel);text-align:center;min-width:0}
.cs-g .lbl{font-size:var(--fs-xs);color:var(--dim)}
.cs-g b{font-size:var(--fs-body);line-height:1.2}
.cs-g .d{font-size:var(--fs-xs);color:var(--dim);white-space:nowrap}
.bs-face{padding:0;border:0;background:none;cursor:pointer}
