  /* Self-hosted fonts so the game looks identical on desktop, iOS and Android (Arial Narrow does
     not exist on phones, so the old stack lost the condensed look there). SAME two faces the
     marketing pages use — Saira Condensed + JetBrains Mono, OFL-licensed, latin-subset woff2. These
     are same-origin assets precached by the service worker (SHELL in sw.js), NOT an external
     request, so the game stays offline-instant. font-display:swap shows the fallback for the first
     paint and swaps in when ready; after the SW caches them (usually on the landing visit first) it
     is instant. Add a new weight file here + to sw.js SHELL + game/fonts if the design needs one. */
  @font-face{ font-family:'Saira Condensed'; font-style:normal; font-weight:600; font-display:swap; src:url('/game/fonts/sairacond-600.woff2') format('woff2'); }
  @font-face{ font-family:'Saira Condensed'; font-style:normal; font-weight:700; font-display:swap; src:url('/game/fonts/sairacond-700.woff2') format('woff2'); }
  @font-face{ font-family:'Saira Condensed'; font-style:normal; font-weight:800; font-display:swap; src:url('/game/fonts/sairacond-800.woff2') format('woff2'); }
  @font-face{ font-family:'Saira Condensed'; font-style:normal; font-weight:900; font-display:swap; src:url('/game/fonts/sairacond-900.woff2') format('woff2'); }
  @font-face{ font-family:'JetBrains Mono'; font-style:normal; font-weight:100 800; font-display:swap; src:url('/game/fonts/jbmono.woff2') format('woff2'); }
  :root{
    --enamel:#D8DEE3; --panel:#F4F6F7; --ink:#141F29; --ink2:#46555F;
    --red:#D8432F; --green:#1F9D63; --amber:#D9941B; --line:#B6C0C7;
    --mono:'JetBrains Mono',ui-monospace,'SF Mono','Cascadia Mono',Menlo,Consolas,monospace;
    --disp:'Saira Condensed','Arial Narrow','Helvetica Neue',Arial,sans-serif;
  }
  *{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
  html,body{height:100%}
  /* Safari's body->viewport propagation is reliable for `overflow`, NOT for this. Declared on
     both, or a drag that overshoots the arena edge rubber-bands the whole card. */
  html{overscroll-behavior:none}
  body{
    background:var(--enamel); color:var(--ink);
    font-family:var(--disp);
    display:flex; justify-content:center;
    /* The shells declare viewport-fit=cover, so the page is drawn UNDER the notch and the home
       indicator. Pad by the safe-area insets (max() keeps the original 12px on devices with none)
       or the header row sits under the status bar and the bottom border under the home indicator. */
    padding:max(12px, env(safe-area-inset-top)) max(12px, env(safe-area-inset-right))
            max(12px, env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left));
    overscroll-behavior:none;
    /* When .rig's content is taller than the viewport (a long room board, history, etc.), plain
       `center` overflows it equally above and below the visible area — but only the BELOW half
       is reachable by scrolling; the top border/rounded corner scrolls off above the top of the
       page and can never be scrolled back into view. `safe center` falls back to start-alignment
       once content overflows, keeping the top of the card reachable. The plain `center` stays as
       a fallback for engines that don't parse `safe` (an unsupported value is ignored, not
       applied, so the working declaration above it wins). */
    align-items:center;
    align-items:safe center;
  }
  .rig{
    width:100%; max-width:430px; background:var(--panel);
    border:2px solid var(--ink); border-radius:10px;
    box-shadow:0 6px 0 rgba(20,31,41,.18);
    overflow:hidden; user-select:none; -webkit-user-select:none;
  }
  header{border-bottom:2px solid var(--ink)}
  .util{display:flex; justify-content:space-between; align-items:center; padding:9px 16px 0}
  .home{font-family:var(--mono); font-size:12px; color:var(--ink2); text-decoration:none; letter-spacing:.04em; padding:4px 2px}
  .home:active{color:var(--ink)}
  .ctx{font-family:var(--mono); font-size:12px; color:var(--ink2); letter-spacing:.06em; padding:4px 2px}
  .ctx.exit{color:var(--red); cursor:pointer}
  .ctx.exit:active{opacity:.7}
  .brand{display:flex; justify-content:space-between; align-items:baseline; padding:2px 16px 12px}
  .logo{font-weight:900; font-size:24px; letter-spacing:.06em; text-transform:uppercase}
  .logo i{font-style:normal; color:var(--red)}
  .dayno{font-family:var(--mono); font-size:13px; color:var(--ink2)}
  .ticks{display:flex; gap:6px; padding:10px 16px; border-bottom:1px solid var(--line); background:#EBEFF1}
  .tick{flex:1; height:8px; border:1.5px solid var(--ink); border-radius:2px; background:transparent; transition:background .25s}
  .tick.done-hi{background:var(--green)} .tick.done-mid{background:var(--amber)}
  .tick.done-lo{background:var(--red)} .tick.now{background:repeating-linear-gradient(45deg,transparent,transparent 3px,var(--ink) 3px,var(--ink) 4px)}
  .stage{padding:18px 16px 16px; min-height:430px; display:flex; flex-direction:column; position:relative}
  /* Every play screen (any stage with an arena) is the SAME height, capped to the viewport — so the card
     is identical on every module and fits on-screen. The arena (below) is flex:1, so it FILLS the leftover
     room: modules with few controls get a bigger play-box instead of empty space, tall-control modules a
     smaller one, but the outer card never changes size. Non-play screens (intro/verdict/history/versus
     boards — no #arena) keep their natural height. */
  /* The 111px allowance is the part of the chrome that is NEVER a safe-area inset: header(86) +
     tick strip(21) + .rig's own top+bottom border(4) -- all fixed, width/height-independent (no
     media query touches them). What's left to subtract is exactly what `body` pads by on each
     side, so this uses the SAME max(12px, env(...)) expression body's own padding uses (line ~31)
     instead of a second copy of the "12" literal -- the two can now never drift apart again.
     History: first shipped as a flat `120px` that covered the chrome + ONE 12px (top padding only)
     and did not subtract env() at all, so on a notched phone the card computed 691px tall inside
     607px of usable space and the bottom (Lock / I'M READY) sat below the fold (invariant 3). That
     was fixed by subtracting env(safe-area-inset-top/bottom), but the 120px baseline still only
     ever accounted for ONE side's flat 12px -- so on a desktop/no-notch viewport (env()=0) the
     formula still overshot by 15px (measured: 707px card in a 716px-tall viewport, 12px gap above,
     -3px below -- the button crowded the bottom edge with no matching gap). Both bugs are the same
     root cause: the allowance must equal chrome + BOTH sides' actual body padding, on every device,
     not a single flat guess. Drop the floor to 400px so a short screen shrinks the play-box rather
     than pushing the button off. The arena keeps its own min-height:150px, so the play area can
     never collapse. */
  .stage:has(#arena){
    height:clamp(400px, calc(100svh - 111px - max(12px, env(safe-area-inset-top)) - max(12px, env(safe-area-inset-bottom))), 648px);
    min-height:0;
  }
  /* ONLY the arena flexes: every other row keeps its full content height and the arena shrinks to absorb
     the change. Without this the flex column squeezes a tall/late control row (DURATION's readout+slider+
     Lock) so the Lock button spills below its box and overlaps the hint. */
  .stage:has(#arena) > *{ flex-shrink:0 }
  .stage:has(#arena) > .arena{ flex:1 1 auto }
  .brief{
    font-family:var(--mono); font-size:12px; letter-spacing:.08em;
    text-transform:uppercase; color:var(--ink2); margin-bottom:4px;
    /* Reserve 2 lines. "PRACTICE / VERSUS · MODULE NN · NAME" wraps on the longer instrument names
       and stays on one line for the short ones, which moved the play-box by 15px between modules --
       one of the two reasons the card visibly changed shape as you flicked through them. */
    min-height:2.2em;
  }
  h2{font-size:26px; font-weight:900; text-transform:uppercase; letter-spacing:.02em; line-height:1.05; margin-bottom:14px}
  h2 .tgt{color:var(--red); font-family:var(--mono); letter-spacing:0}
  /* the play-round title reserves 2 lines and sits at the bottom of that space (via the .qt wrapper, which
     keeps the text wrapping) — so a 1-line vs 2-line title puts the play-box at the SAME spot on every
     module. Scoped to the play screens; the intro/versus titles (inside .center) are untouched. */
  .stage>h2{ min-height:2.15em; display:grid; align-content:end }
  /* #ctl is a SIBLING of #arena, so it inherits nothing from the rule below. manipulation keeps
     scrolling/pinch but kills the 300ms double-tap-zoom gesture on the rapid-tap controls (the
     +/-1 stepper on 18 sliders, the nudge pad). user-scalable=no does NOT do this on iOS. */
  #ctl, #ctl button, .stepper button, .nudge button, #lock, input[type=range]{ touch-action:manipulation }
  /* long-press on a link/button must not raise the iOS callout sheet over a running round */
  .rig{ -webkit-touch-callout:none }
  .arena{
    /* Fill the leftover stage height (the stage is a fixed, viewport-capped height above). This makes the
       card the SAME size on every module with NO empty space: fewer controls -> bigger play-box, more
       controls -> smaller, card unchanged. Same in the briefing + round (both flex the same), so parity holds. */
    /* The floor is what forces the chrome out of the stage box on short screens (a notched phone in
       Safari): the arena refuses to shrink, content exceeds the fixed stage height, and .rig's
       overflow:hidden CLIPS the bottom -- which is how the Lock / I'M READY button ended up as a
       sliver at the edge of the card. Stepped down by viewport height in the media queries below. */
    flex:1 1 auto; min-height:150px;
    border:1.5px solid var(--ink); border-radius:8px; background:#fff;
    position:relative; touch-action:none; overflow:hidden;
  }
  .controls{display:flex; gap:10px; margin-top:14px}
  button{
    font-family:var(--disp); font-weight:900; text-transform:uppercase; letter-spacing:.08em;
    font-size:16px; padding:14px 18px; border-radius:8px; cursor:pointer;
    border:2px solid var(--ink); background:var(--ink); color:#fff;
    transition:transform .05s;
  }
  .controls button{flex:1}
  button:active{transform:translateY(2px)}
  button.ghost{background:transparent; color:var(--ink)}
  button:disabled{opacity:.35; pointer-events:none}
  .timertgl{ display:inline-block; width:auto; margin:0 0 6px; padding:3px 10px;
    font:600 12px/1 ui-monospace,monospace; letter-spacing:.04em; text-transform:uppercase;
    color:var(--ink); background:var(--panel); border:1.5px solid var(--ink2); border-radius:2px; cursor:pointer; }
  /* read-first briefing + how-to-play demo (feedback #3) */
  /* the strip directly above the arena — the briefing's caption+toggle row and the round's shot-clock
     row — MUST be the same height, or the (centred) card places the arena at a different Y between the
     two = a jump on Ready. Shared metrics keep the arena in the exact same spot for every module. */
  .demohead, .clockrow{ min-height:28px; margin:2px 0 8px; display:flex; align-items:center }
  .demohead{ justify-content:space-between; gap:10px }
  .democap{ flex:1; text-align:left; font:600 12px/1.3 ui-monospace,monospace; letter-spacing:.04em;
    text-transform:uppercase; color:var(--ink2) }
  .demolink{ flex:0 0 auto; padding:5px 12px; min-height:44px; display:inline-flex; align-items:center; font:700 11px/1 ui-monospace,monospace; letter-spacing:.03em;
    text-transform:uppercase; color:var(--ink); background:var(--panel); border:1.5px solid var(--ink2); border-radius:4px }
  /* while a briefing demo is showing: the module is watch-only and its Lock + nudge pad are dead, so
     make them invisible + block poking (Removed on Ready). visibility:hidden (NOT display:none) so the
     controls KEEP their box — the briefing #ctl is then exactly as tall as the round's, for every
     archetype (place-a-point nudgepad+Lock, slider+Lock, single Lock, tap), so the card doesn't resize. */
  .demoing #arena, .demoing #ctl{ pointer-events:none }
  /* The dead Lock/nudge pad were RESERVED (visibility:hidden) so the briefing and the round measured
     the same. But reserving costs the Lock's full 57px, and on a short screen that is exactly what
     pushed I'M READY out of the card and got it clipped -- the button was simply missing on a phone.
     The briefing has its own action button, so remove the dead controls from the flow entirely; the
     arena flexes to absorb the change, which is what keeps the two views the same size anyway. */
  .demoing #lock, .demoing .nudgepad{ display:none !important }
  /* floor the control slot on BOTH the briefing and the round it leads into, so a module with no/late
     controls (tap rounds, DURATION's after-the-light slider) reserves the same space in each = no jump */
  #ctl{ min-height:48px }
  .demoempty{ text-align:center; color:var(--ink2); font:500 14px/1.6 ui-monospace,monospace; padding:0 18px }
  /* every briefing (all 100): a warm tint + HOW IT WORKS badge mark the preview vs the clean-panel scored round */
  .stage.briefing{ background:#F3EBD6 }
  /* keep the briefing's MODULE label clear of the absolute HOW IT WORKS badge (it wraps instead of running under it) */
  .stage.briefing > .brief{ padding-right:118px }
  .howbadge{ position:absolute; top:10px; right:12px; z-index:2;
    background:var(--amber); color:#fff; font:700 10px/1 ui-monospace,monospace; letter-spacing:.14em; padding:5px 8px; border-radius:2px }
  .teachout{ text-align:center; font:800 26px/1.1 ui-monospace,monospace; color:var(--ink); min-height:30px; margin:2px 0 6px; letter-spacing:.02em; white-space:nowrap }
  .ready{ width:100%; margin-top:12px; background:var(--green); border-color:var(--green) }
  /* the practice round shows a ⏱ timer toggle above the clock for timed modules; the briefing reserves
     the same slot (matches .timertgl's box + margin) so the arena stays put there too */
  .ttglghost{ height:21px; margin:0 0 6px }
  /* Reserve the teach readout row on EVERY briefing. It was only emitted for teachable modules, so
     the play-box came out 38px taller on the 35 passive ones and the card visibly changed shape as
     you moved between modules. Reserved (not hidden) so every module's box is identical. */
  .teachout{ min-height:30px }
  /* Phone-height screens: tighten the chrome around the play-box so the tallest modules (2-line title +
     readout+slider+Lock stack, e.g. MODULE 23/25/28) fit on screen without cutting off the top or the
     Lock/Ready button. Applies equally to the briefing + the round, so the two stay the same size. */
  @media (max-height:850px){
    .stage{ padding:12px 16px 14px }
    h2{ font-size:22px; line-height:1.04; margin-bottom:10px }
    .demohead, .clockrow{ min-height:24px; margin:1px 0 6px }
    .hint{ margin-top:6px }
    .ready{ margin-top:8px }
    .guessnum{ font-size:23px }
    .timertgl{ margin:0 0 3px }
    .ttglghost{ height:21px; margin:0 0 3px }
    .arena{ min-height:120px }
  }
  /* Short phones (SE/mini, or any phone in Safari with both toolbars AND a notch). Nothing may be
     clipped: every module must show its play-box, its hint and its action button without scrolling. */
  @media (max-height:720px){
    .stage{ padding:10px 14px 12px }
    h2{ font-size:20px; margin-bottom:8px }
    .brief{ margin-bottom:2px }
    .demohead, .clockrow{ min-height:22px; margin:0 0 4px }
    .teachout{ min-height:26px; margin:0 0 4px }
    .hint{ margin-top:5px; font-size:10.5px }
    .ready{ margin-top:6px }
    .arena{ min-height:92px }
  }
  /* Very short screens (SE/mini in Safari, or a phone in landscape-ish letterboxing). Below this the
     fixed stage height cannot hold the tallest control stack (readout + slider + stepper + Lock)
     without crushing the play-box to an unusable size, so stop forcing a fixed height and let the
     card take its natural height. The page then scrolls a little instead of CLIPPING the Lock /
     I'M READY button, which is the one outcome that must never happen. `align-items:safe center`
     keeps the top of the card reachable while it scrolls. */
  @media (max-height:620px){
    h2{ font-size:18px; margin-bottom:6px }
    .stage:has(#arena){ height:auto; min-height:0 }
    .arena{ min-height:132px }
    .hint{ min-height:2.7em }
  }
  /* The briefing mounts the module's REAL control row so the demo can drive it, and on the 18
     integer sliders that row includes the -1/+1 stepper. That is ~50px the briefing has no room
     for: the arena is already at its floor, so the overflow pushed I'M READY out of the card and
     .rig{overflow:hidden} clipped it -- on a phone the button simply was not there. The stepper is
     fine-precision for the SCORED round; the tutorial teaches the mechanic, and the demo drives the
     slider itself, so it is not needed here. Hiding it also makes every briefing the same shape. */
  .stage.briefing .stepper{ display:none }

  /* fine-adjust nudge pad for place-a-point modules (feedback #1) */
  .controls:has(.nudgepad){ flex-wrap:wrap }
  .nudgepad{ flex:1 0 100%; display:flex; gap:8px; margin:0 0 2px }
  .nudgepad button{ flex:1; padding:10px 0; font:700 15px/1 system-ui,sans-serif; letter-spacing:0;
    background:var(--panel); color:var(--ink); border:2px solid var(--ink2); border-radius:6px; }
  /* fine ±1 adjust row for number sliders (below the slider, above Lock) — same language as the nudge pad */
  .stepper{ display:flex; gap:8px; margin:0 0 8px }
  .stepper button{ flex:1; padding:10px 0; font:700 16px/1 var(--mono); letter-spacing:.02em;
    background:var(--panel); color:var(--ink); border:2px solid var(--ink2); border-radius:6px; }
  .demoing .stepper{ display:none }
  /* reserve 2 lines for the hint so a 1-line vs 2-line hint doesn't change the card height (this is what
     made MODULE 97/98 taller than 99/100) — same module archetype now measures the same */
  .hint{font-family:var(--mono); font-size:11px; line-height:1.35; color:var(--ink2); margin-top:10px; text-align:center; min-height:2.7em}
  .flash{
    position:absolute; inset:0; background:var(--panel); display:flex; flex-direction:column;
    align-items:center; justify-content:center; gap:6px; z-index:5;
  }
  .flash .big{font-family:var(--mono); font-weight:700; font-size:54px}
  .flash .sub{font-family:var(--mono); font-size:13px; color:var(--ink2); text-align:center; padding:0 16px}
  .flash .verdict{font-weight:900; font-size:20px; text-transform:uppercase; letter-spacing:.1em}
  .center{flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; gap:14px}
  /* When .center lives inside the (non-flex) .arena, flex:1/height:100% don't resolve,
     so the begin-cue collapsed to its own height and pinned to the top. Fill the arena
     so the cue centres. (.arena is position:relative; stage-level .center is untouched.) */
  .arena > .center{position:absolute; inset:0}
  /* per-round gut timer: a thin red track that drains left-to-right over --shot seconds.
     Idle (no .run) it is an invisible spacer so timed/untimed rounds keep the same layout. */
  .shotclock{ --shot:10s; height:3px; width:100%; margin:0 0 6px; background:transparent; }
  .shotclock.run{ background:var(--red); transform-origin:left center;
    animation:gc-drain var(--shot) linear forwards, gc-pulse .5s ease-in-out calc(var(--shot) * .7) 6 alternate; }
  @keyframes gc-drain{ from{transform:scaleX(1)} to{transform:scaleX(0)} }
  @keyframes gc-pulse{ from{opacity:1} to{opacity:.35} }
  @media (prefers-reduced-motion:reduce){
    .shotclock.run{ animation:gc-drain var(--shot) linear forwards; } /* drain stays legible, pulse dropped */
  }
  .rules{font-family:var(--mono); font-size:13px; color:var(--ink2); line-height:1.7; text-align:left}
  .rules b{color:var(--ink)}
  .gauge{width:240px; height:140px}
  .gtick{stroke:var(--ink); stroke-width:2}
  .needle{stroke:var(--red); stroke-width:4; stroke-linecap:round; transform-origin:120px 130px;
    transition:transform 1.4s cubic-bezier(.2,.8,.2,1.05)}
  .rating{font-weight:900; font-size:34px; text-transform:uppercase; letter-spacing:.04em}
  .emoji{display:flex; justify-content:center}
  .emoji svg.dials{height:30px; width:auto; display:block}
  .total{font-family:var(--mono); font-size:16px; color:var(--ink2)}
  .ruler{height:10px; background:repeating-linear-gradient(90deg,var(--ink) 0 1.5px,transparent 1.5px 12px); opacity:.25; width:80%}
  .statrow{display:flex; gap:0; width:100%; border:1.5px solid var(--ink); border-radius:8px; overflow:hidden}
  .stat{flex:1; padding:10px 4px; text-align:center; border-right:1.5px solid var(--ink)}
  .stat:last-child{border-right:none}
  .stat .v{font-family:var(--mono); font-weight:700; font-size:22px}
  .stat .k{font-family:var(--mono); font-size:10px; letter-spacing:.1em; color:var(--ink2); text-transform:uppercase}
  .nextin{font-family:var(--mono); font-size:13px; color:var(--ink2)}
  .nextin b{color:var(--ink); font-size:15px}
  svg text{font-family:var(--mono)}
  .vessel-fill{fill:var(--red); opacity:.85}
  .dot{fill:var(--ink)}
  .slider-wrap{position:absolute; left:16px; right:16px; bottom:18px; display:flex; align-items:center; gap:12px}
  input[type=range]{flex:1; accent-color:var(--red); height:32px}
  .guessnum{font-family:var(--mono); font-weight:700; font-size:28px; min-width:54px; text-align:right}
  .stopread{font-family:var(--mono); font-weight:700; font-size:64px; letter-spacing:.02em}
  .toast{
    position:fixed; bottom:calc(24px + env(safe-area-inset-bottom)); left:50%; transform:translateX(-50%);
    background:var(--ink); color:#fff; font-family:var(--mono); font-size:13px;
    padding:10px 16px; border-radius:8px; opacity:0; transition:opacity .3s; pointer-events:none; z-index:50;
  }
  .toast.show{opacity:1}
  /* history view (calendar + distribution) */
  .histwrap{display:flex;flex-direction:column;align-items:center;gap:7px;width:100%;padding:2px 2px 8px}
  .histsum{font-family:var(--mono);font-size:12px;color:var(--ink2)}
  .calhead{display:flex;align-items:center;justify-content:center;gap:14px;font-family:var(--disp);font-weight:900;font-size:19px;text-transform:uppercase;letter-spacing:.03em}
  .navbtn{background:none;border:none;color:var(--ink);font-size:22px;line-height:1;min-height:44px;min-width:44px;display:inline-flex;align-items:center;justify-content:center;padding:2px 12px;cursor:pointer}
  .navbtn:disabled{opacity:.25;cursor:default}
  .caldow{display:grid;grid-template-columns:repeat(7,1fr);width:100%;font-family:var(--mono);font-size:10px;color:var(--ink2);text-align:center}
  .calgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:4px;width:100%}
  .calgrid .cell{height:36px;display:flex;align-items:center;justify-content:center;border:1.5px solid var(--line);border-radius:6px;font-family:var(--mono);font-size:12px;color:var(--ink2)}
  .calgrid .cell.empty{border:none}
  .calgrid .cell.hi{background:var(--green);border-color:var(--green);color:#fff}
  .calgrid .cell.mid{background:var(--amber);border-color:var(--amber);color:#fff}
  .calgrid .cell.lo{background:var(--red);border-color:var(--red);color:#fff}
  .calgrid .cell.today{outline:2px solid var(--ink);outline-offset:1px}
  .dist{width:100%;display:flex;flex-direction:column;gap:3px}
  .drow{display:flex;align-items:center;gap:8px;font-family:var(--mono);font-size:10px}
  .drow .dlbl{width:92px;color:var(--ink2);text-align:right;text-transform:uppercase;flex:none}
  .drow .dbar{height:11px;background:var(--ink);border-radius:2px;min-width:2px}
  .drow .dct{font-weight:700;font-size:11px}
  .histempty{font-family:var(--mono);font-size:13px;color:var(--ink2);text-align:center;line-height:1.7;padding:24px 10px}
  .histlink{font-family:var(--mono);font-size:12px;color:var(--ink2);text-decoration:underline;cursor:pointer;background:none;border:none;padding:6px}
  @keyframes slam{from{opacity:0;transform:scale(1.14)}to{opacity:1;transform:scale(1)}}
  .slam{animation:slam .22s cubic-bezier(.2,.8,.2,1.05) both}
  @keyframes dpop{from{opacity:0;transform:scale(.55)}to{opacity:1;transform:scale(1)}}
  .emoji svg.dials .dcell{transform-box:fill-box;transform-origin:center;animation:dpop .34s ease-out both}
  .flash .rvdial{transform-box:fill-box;transform-origin:center;animation:dpop .4s ease-out both}
  .coach{position:absolute;left:0;right:0;bottom:12px;display:flex;justify-content:center;pointer-events:none;z-index:4}
  .coach .pill{font-family:var(--mono);font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#fff;background:var(--red);padding:7px 14px;border-radius:20px;box-shadow:0 2px 0 rgba(20,31,41,.2);animation:coachbob 1s ease-in-out infinite}
  @keyframes coachbob{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}
  .tapcue{display:inline-block;font-family:var(--mono);font-weight:700;font-size:13px;letter-spacing:.06em;text-transform:uppercase;color:#fff;background:var(--ink);padding:9px 16px;border-radius:22px;animation:tappulse 1.5s ease-in-out infinite}
  @keyframes tappulse{0%,100%{transform:scale(1);opacity:.9}50%{transform:scale(1.04);opacity:1}}
  @media (prefers-reduced-motion:reduce){ .needle{transition:none} .slam{animation:none} .emoji svg.dials .dcell{animation:none} .coach .pill{animation:none} .tapcue{animation:none} .flash .rvdial{animation:none} }
  .pctline{font:600 13px/1.4 ui-monospace,monospace;color:var(--green);min-height:18px;margin:2px 0 4px}
  /* capped + scrollable so a room with many players stays a fixed compact size instead of
     stretching the whole card (owner feedback: a full room "isn't compact at all") */
  .vboard{width:82%;margin:8px auto;max-height:34vh;overflow-y:auto}
  .vrow{display:flex;justify-content:space-between;font:600 14px ui-monospace,monospace;padding:5px 8px;border-bottom:1px solid var(--line)}.vrow.me{background:var(--panel);color:var(--red)}
  .vrow span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .vrow span:first-child{margin-right:8px}
  /* versus share readout: a selectable field + a copy action, instrument-panel style */
  .sharelabel{width:82%;margin:14px auto 4px;font:700 10px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--ink2);text-align:left}
  .sharefield{width:82%;margin:0 auto;display:flex;gap:8px;align-items:stretch}
  .shareinput{flex:1;min-width:0;font:600 13px var(--mono);color:var(--ink);background:#EBEFF1;border:1px solid var(--line);padding:9px 10px}
  .shareinput:focus{outline:none;border-color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)}
  .shareinput.code{font:800 22px var(--mono);letter-spacing:.22em;text-align:center;color:var(--red)}
  .copybtn{flex:0 0 auto;font:700 12px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--panel);background:var(--ink);border:1px solid var(--ink);padding:0 16px;cursor:pointer}
  .copybtn:hover{background:#243240}.copybtn.ok{background:var(--green);border-color:var(--green)}
  /* versus lobby: name (required) + two equal action rows split by a labeled divider */
  .vlobby{width:88%;max-width:360px;margin:16px auto 0}
  .vlobby-lede{font-family:var(--mono);font-size:13px;color:var(--ink2);line-height:1.6;text-align:center;margin:0 0 14px}
  .vfield{width:100%;font-family:var(--mono);font-weight:600;font-size:16px;color:var(--ink);background:#EBEFF1;border:1px solid var(--line);padding:12px;text-align:center}
  .vfield:focus{outline:none;border-color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)}
  .vfield.bad{border-color:var(--red);box-shadow:inset 0 0 0 1px var(--red)}
  .vfield.code{text-transform:uppercase;letter-spacing:.18em;font-weight:800}
  .vnote{font-family:var(--mono);font-weight:600;font-size:12px;color:var(--red);text-align:left;margin:6px 2px 0;display:none}
  .vnote.show{display:block}
  .vrow{display:flex;gap:10px;margin-top:12px}
  .vrow>*{flex:1 1 0;min-width:0}
  .vdivider{display:flex;align-items:center;gap:10px;margin-top:18px;font-family:var(--mono);font-weight:700;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink2)}
  .vdivider::before,.vdivider::after{content:"";flex:1;height:1px;background:var(--line)}
  .pmodes{display:flex;gap:8px;width:88%;max-width:380px;margin:8px auto 6px}
  .pmode{flex:1;font:800 14px var(--disp);text-transform:uppercase;padding:9px 6px;border:2px solid var(--ink);background:transparent;color:var(--ink);cursor:pointer}
  .pmode.on{background:var(--ink);color:var(--panel)}
  .pcats{display:flex;flex-wrap:wrap;gap:8px;width:88%;max-width:380px;margin:6px auto}
  .pcatbtn{display:flex;align-items:center;gap:7px;font:800 13px var(--disp);text-transform:uppercase;padding:9px 12px;border:2px solid var(--ink);background:transparent;color:var(--ink);cursor:pointer}
  .pcatbtn.on{background:var(--ink);color:var(--panel)}
  .pcount{font:700 11px var(--mono);letter-spacing:.14em;color:var(--ink2);text-align:right;width:88%;max-width:380px;margin:4px auto 0}
  .pgrid{width:88%;max-width:380px;margin:0 auto;max-height:44vh;overflow:auto;text-align:left}
  .pcat{display:flex;align-items:center;gap:7px;margin:10px 0 4px;font:700 10px var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--ink2)}
  .pdot{width:9px;height:9px;border-radius:50%;display:inline-block}
  .pchips{display:flex;flex-wrap:wrap;gap:6px}
  .pchip{font:700 12px var(--mono);padding:6px 9px;border:1px solid var(--line);background:#EBEFF1;color:var(--ink);cursor:pointer}
  .pchip.on{background:var(--ink);color:var(--panel);border-color:var(--ink)}
  .rdlist{width:82%;margin:4px auto 0;max-height:30vh;overflow-y:auto}
  .rdrow{display:flex;justify-content:space-between;align-items:center;font:600 14px var(--mono);padding:6px 4px;border-bottom:1px solid var(--line)}
  .rdscore{color:var(--ink2)} .rdplay{font:800 12px var(--disp);text-transform:uppercase;padding:5px 14px;border:2px solid var(--ink);background:var(--ink);color:var(--panel);cursor:pointer}
  /* duel board: round-by-round dial comparison (you vs the top opponent) */
  .vcompare{width:82%;margin:6px auto 2px}
  .vcrow{display:flex;align-items:center;gap:8px;padding:5px 2px;border-bottom:1px solid var(--line)}
  .vcrow svg.dials{flex:0 1 auto;min-width:0;height:26px;width:auto}
  .vclabel{flex:1 1 auto;min-width:0;font:700 11px var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--ink2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left}
  .vclabel.me{color:var(--red)}
  .vctot{flex:0 0 auto;font:700 13px var(--mono);color:var(--ink)}
  /* lobby: last few duels/rooms this device touched, so a scrolled-away link is still reachable */
  .vrecent{display:flex;flex-direction:column;gap:6px;margin-top:12px}
  .vrecent-item{display:flex;justify-content:space-between;align-items:center;gap:8px;font:700 12px var(--mono);letter-spacing:.05em;text-transform:uppercase;color:var(--ink);background:#EBEFF1;border:1px solid var(--line);padding:10px 12px;text-decoration:none}
  .vrecent-item:hover{border-color:var(--ink)}
  .vrecent-item span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .vrtag{flex:0 0 auto;color:var(--ink2);letter-spacing:.14em}

/* ---- GUT CIRCUIT (board mode) ---- */
.chandoff{display:flex;flex-direction:column;justify-content:center;align-items:center;gap:10px;min-height:340px;text-align:center;cursor:pointer;touch-action:none}
/* .cbeat is the sole child of .stage (no #arena on a beat screen), same as .center's use in
   resumeScreen/jailScreen/recalScreen/surgeScreen/eventScreen/podiumScreen -- height:100% on a
   child of a parent with only min-height (see .stage, game.css:65) resolves to auto and collapses
   to content height (game.css:239-241 documents the same gotcha for .arena>.center). flex:1 fills
   the flex column's remaining space instead, matching those six screens. */
.cbeat{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;flex:1;cursor:pointer;touch-action:manipulation}
.cbeat h2{font-size:34px;letter-spacing:1px;margin:10px 0 6px}
.cbeat.bust h2{color:#D8432F}
.cbeat.reprieve h2{color:#D9941B}
.cbeattap{opacity:.6}
.cboard{display:flex;flex-direction:column;gap:10px}
/* Cap the square board by viewport height (not just card width) so on a short phone or in landscape
   the board shrinks and the ROLL button + move-budget bar below it stay on-screen -- the circuit
   screens are natural-height (not the #arena stage), so without this the primary action can sit below
   the fold. min(100%, 54svh) keeps the board square (no letterbox) and centred. */
.cboardsvg{width:min(100%,54svh);height:auto;margin:0 auto;background:#D8DEE3;border:2px solid #141F29}
.cstatus{display:flex;flex-wrap:wrap;gap:6px 12px;font:600 12px ui-monospace,monospace}
.cstatus .cplayer{display:inline-flex;align-items:center;gap:5px;opacity:.55}
.cstatus .cplayer.on{opacity:1}
.cstatus .cplayer.out{text-decoration:line-through;opacity:.3}
.cstatus .cplayer.out .cmark{opacity:.5}
.cmark{display:inline-block;vertical-align:middle;flex:none}
.ccount{display:flex;gap:6px;justify-content:center;margin:8px 0}
.ccount button.on{outline:2px solid #141F29}
#cnames{display:flex;flex-direction:column;gap:6px;width:100%;max-width:280px;margin:0 auto}
.cnamerow{display:flex;align-items:center;gap:8px}
.cnamerow .vfield{flex:1}
.ctoggles{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin:10px 0 2px}
.ctgl{font:600 12px ui-monospace,monospace;padding:6px 12px;min-height:44px;display:inline-flex;align-items:center;justify-content:center;background:none;border:1px solid rgba(20,31,41,.35);color:#141F29;border-radius:2px;cursor:pointer;width:auto;flex:0 1 auto}
.csurge{flex-direction:column}
.cboard{position:relative}
.cdiewrap{position:absolute;top:42%;left:0;right:0;display:flex;align-items:center;justify-content:center;pointer-events:none}
#cdie svg{filter:drop-shadow(0 3px 6px rgba(20,31,41,.35));animation:cdiepop .18s ease-out}
@keyframes cdiepop{from{transform:scale(.7)}to{transform:scale(1)}}
/* Duration is set ONLY inline (circuit-ui.js LAP_FLOAT_MS) -- no animation-duration here, so
   there is no second literal that could drift out of step with it. */
.clapfloat{position:absolute;top:16%;left:50%;transform:translateX(-50%);font:700 30px/1 'JetBrains Mono',ui-monospace,monospace;color:#1F9D63;pointer-events:none;animation-name:clapup;animation-timing-function:ease-out;animation-fill-mode:forwards}
@keyframes clapup{from{opacity:1;translate:0 0}to{opacity:0;translate:0 -34px}}
.cevent h2{font-size:30px;color:#D8432F}
.cpodium .crow{display:flex;justify-content:space-between;padding:6px 4px;border-bottom:1px solid rgba(20,31,41,.15);font:600 14px ui-monospace,monospace}
.cpodium .crow.win{color:#1F9D63}
@media (prefers-reduced-motion: reduce){ .ctoken{transition:none} #cdie svg{animation:none} }
  .rrefresh{font:700 10px var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--red);background:none;border:none;cursor:pointer;padding:0}

/* ---- GUT CIRCUIT ONLINE: waiting-screen chrome ------------------------------------------------
   The waiting screen uses the SAME .cboard shell + top status strip as boardScreen (and pass-and-play),
   so the board looks identical in every mode -- only the row BELOW the board differs: the centered
   "AMAR is up · playing MIDPOINT" turn line (.cturn) + the move-budget bar (.cclockrow), where the
   ROLL button sits on your own turn. (.cturn is its OWN centered class, not the flex .cstatus pips
   row, so the turn line reads centered rather than left-aligned.) */
.cturn{font:600 13px var(--mono);text-align:center;color:var(--ink);margin-top:2px}
.cclockrow{height:6px;background:var(--panel);border:1px solid var(--line);margin:8px auto 0;max-width:340px;width:100%}
.cclock{height:100%;background:var(--red);width:100%;transition:width .2s linear}

/* ---- HOW TO PLAY · GUT CIRCUIT rules sheet (2026-07-13) ---------------------------------------
   A pure overlay above the stage, so it can be opened mid-game without disturbing the turn.
   Scrolls INSIDE the sheet so the page body never scrolls horizontally or loses its top edge. */
.chow{position:fixed;inset:0;z-index:60;background:var(--enamel);display:flex;justify-content:center;
  align-items:center;align-items:safe center;overflow-y:auto;
  padding:max(14px, env(safe-area-inset-top)) max(14px, env(safe-area-inset-right)) max(14px, env(safe-area-inset-bottom)) max(14px, env(safe-area-inset-left))}
.chowsheet{background:var(--panel);border:2px solid var(--ink);box-shadow:5px 6px 0 var(--ink);
  width:100%;max-width:520px;max-height:calc(100svh - max(14px, env(safe-area-inset-top)) - max(14px, env(safe-area-inset-bottom)));display:flex;flex-direction:column}
.chowhead{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;
  padding:13px 16px;border-bottom:2px solid var(--ink);flex-shrink:0}
.chowhead h2{margin:3px 0 0;font-family:var(--disp);font-weight:800;text-transform:uppercase;
  font-size:27px;line-height:1;letter-spacing:.01em}
.chowx{background:none;border:0;width:auto;min-width:44px;min-height:44px;display:inline-flex;align-items:center;justify-content:center;padding:2px 4px;font-size:19px;line-height:1;
  color:var(--ink);cursor:pointer}
.chowbody{flex:1;overflow-y:auto;padding:14px 16px}
.chowlead{font-family:var(--mono);font-size:13px;line-height:1.62;margin:0}
.chowlead b,.chowsec b{font-weight:700}
.chowsec{border-top:1px solid var(--line);margin-top:13px;padding-top:12px}
.chowno{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;font-weight:700;color:#C63722}
.chowsec h3,.cinfosheet h3{margin:3px 0 6px;font-family:var(--disp);font-weight:800;text-transform:uppercase;
  font-size:18px;line-height:1.1;letter-spacing:.01em;color:var(--ink)}
.chowsec p{font-family:var(--mono);font-size:12.5px;line-height:1.62;margin:0 0 6px}
.chownote{color:var(--ink2)}
.chowlist{list-style:none;margin:7px 0 0;padding:0}
.chowlist li{display:flex;justify-content:space-between;align-items:baseline;gap:14px;
  padding:5px 0;border-bottom:1px dotted var(--line);font-family:var(--mono);font-size:12.5px}
.chowlist li:last-child{border-bottom:0}
.chowlist li b{white-space:nowrap}
.chowlist li span{text-align:right;color:var(--ink2)}
.chowfoot{padding:11px 16px;border-top:2px solid var(--ink);flex-shrink:0}
.chowlink{margin-top:8px}
.chowrow{display:flex;gap:8px;align-items:stretch}
.chowrow #croll{flex:1}
/* .controls button sets flex:1 (a type+class selector), so the compact "?" needs to out-specify it */
.chowrow .chowq{flex:0 0 60px;width:60px;padding:0;font-weight:800;font-size:17px}
.cinfosheet{max-width:340px;max-height:60vh}
.cchip{display:inline-block;width:10px;height:10px;margin-right:6px;border:1px solid #141F29;vertical-align:baseline}
