/* =====================================================================
   The boards themselves: every game's stage, and the panel chrome around it.

   Loaded by BOTH pages on purpose. The tablet draws a game with VIEWS.<id>
   (js/views/*.js); the phone watching over /api/live mounts the very same view
   into its arena, so a player holding a phone sees the board the room is
   looking at, not a description of it (Cory, 2026-09-17: "every game needs its
   remote to have the game graphics the same as on the full app"). One
   stylesheet is what keeps the two from drifting apart.

   Lives after app.css and before screens.css in the app; live.html loads it on
   its own, so nothing in here may assume the app's chrome exists.
   ===================================================================== */

.panel{position:relative; border-radius:22px; background:linear-gradient(180deg, rgba(36,56,120,.50), rgba(9,14,38,.82)); border:1px solid var(--edge); box-shadow:inset 0 1px 0 rgba(255,255,255,.08), 0 18px 50px rgba(0,0,0,.45)}
.panel::after{content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none; background:linear-gradient(135deg, rgba(255,255,255,.07), transparent 40%)}
.avatar{--pc:var(--gold); width:38px; height:38px; flex:none; border-radius:12px; display:grid; place-items:center; font-family:var(--num); font-size:17px; color:#08102a; background:linear-gradient(145deg, #fff 0%, var(--pc) 45%, color-mix(in srgb, var(--pc) 55%, #000) 100%); box-shadow:0 0 14px color-mix(in srgb, var(--pc) 55%, transparent), inset 0 -3px 0 rgba(0,0,0,.25)}
/* A bot in the lineup wears its own face, in the colour of its level — the same one the bot sheet shows. */
.avatar.bot{background:linear-gradient(160deg,#1a2452,#0a0f28); box-shadow:0 0 14px color-mix(in srgb, var(--pc) 45%, transparent); padding:2px}
.avatar.bot svg{width:100%; height:100%; display:block}
.x-main{display:grid; grid-template-columns:190px minmax(0,1fr); gap:12px; min-height:0; height:100%}
.rounds{padding:14px; display:flex; flex-direction:column; gap:10px; min-height:0}
.rp-head{display:flex; align-items:center; gap:10px}
.rp-name{font-weight:700; font-size:18px; line-height:1.1; min-width:0; overflow-wrap:anywhere}
.rp-stat{display:grid; grid-template-columns:1fr 1fr; gap:6px; text-align:center}
.rp-stat div{padding:6px 4px; border-radius:12px; background:rgba(120,160,255,.08)}
.rp-stat b{display:block; font-family:var(--num); font-weight:400; font-size:20px}
.rp-stat span{font-size:10px; letter-spacing:.2em; color:var(--muted); font-weight:700; text-transform:uppercase}
/* Spelled-out labels need the tracking back to fit; "MPR" meant nothing to anyone who does not play. */
.rp-stat span.wide{letter-spacing:.04em; line-height:1.25; display:block}
/* The last turn of a countdown game (Sniper): the number left goes warm so it is the thing you notice. */
.rp-stat .low{background:rgba(255,200,58,.16)}
.rp-stat .low b{color:#ffd27a}
/* flex:1 1 0 so the list is the box that scrolls: without it the list grew to its content and the panel's
   edge cut the bottom row in half (store screenshots, 2026-10-16); the scroll-to-bottom then does its job. */
.rlist{display:flex; flex-direction:column; gap:3px; overflow:auto; min-height:0; flex:1 1 0}
.rlist div{display:flex; justify-content:space-between; padding:4px 10px; border-radius:8px; font-weight:600; color:#b8c5ee}
.rlist div:nth-child(odd){background:rgba(120,160,255,.05)}
/* A round list scrolled down fades its top edge, so a row half under the panel above reads as "more above"
   rather than as a row cut in half (store screenshots at 440px, 2026-10-16). The class is set by one scroll
   listener for every list (js/engine.js). -webkit- first: the HiGole tablet is on Chrome 105. */
.rlist.faded{-webkit-mask-image:linear-gradient(to bottom, transparent 0, #000 22px); mask-image:linear-gradient(to bottom, transparent 0, #000 22px)}
.rlist div.cur{background:linear-gradient(90deg, rgba(255,45,85,.35), rgba(255,45,85,.08)); color:#fff}
.rlist div em{font-style:normal; color:var(--muted)}

/* ---------- race ---------- */
.race-wrap{display:grid; grid-template-rows:minmax(0,1fr) auto; gap:10px; min-height:0}
.mm-line{background-size:calc(100% / var(--steps, 21)) 100%}
.lanepop.miss{fill:#dfe6ff; stroke:#1a1030}
/* Each world tints the rail; nothing else about it changes. See the main .sign block below. */
.sign-world{--sg:#5fd0ff}
.sign-clock{--sg:#ffc83a}
.scene.theme-world{background:#061634}
.scene.theme-clock{background:#0a0620}
.w-route{fill:none; stroke:#ffffff; stroke-opacity:.55; stroke-width:4; stroke-dasharray:2 14; stroke-linecap:round}
.w-num{font-family:var(--num); font-size:20px; fill:#fff; text-anchor:middle; paint-order:stroke; stroke:#061634; stroke-width:6}
.w-lbl{font-family:var(--ui); font-weight:700; font-size:15px; fill:#bfe9ff; text-anchor:middle; paint-order:stroke; stroke:#061634; stroke-width:5; letter-spacing:.04em}
.w-pulse{fill:none; stroke:#ffd84a; stroke-width:4; opacity:0; transform-box:fill-box; transform-origin:center}
.w-stop.next .w-pulse{animation:wpulse 1.2s ease-out infinite}
.w-stop.next circle:nth-child(2){fill:#ffd84a; stroke:#fff}
@keyframes wpulse{from{opacity:1; transform:scale(.6)} to{opacity:0; transform:scale(1.8)}}
.w-home{animation:homeBob 2.4s ease-in-out infinite alternate}
@keyframes homeBob{to{transform:translateY(-4px)}}
.plane-num{font-family:var(--num); font-size:11px; fill:#fff; text-anchor:middle; dominant-baseline:central}
.plane .plane-body{filter:drop-shadow(0 6px 4px rgba(0,0,0,.35))}
.c-num{font-family:var(--num); font-size:42px; fill:#e8e4ff; text-anchor:middle; dominant-baseline:central; transition:fill .3s}
.c-num.next{fill:#ffd84a}
/* the pulse is a lit copy over the next number fading in and out (js race.js clock build) */
.c-lit{opacity:0; fill:#fff7d0; pointer-events:none; transition:none}
.c-num.next + .c-lit{animation:cnumLit 1s ease-in-out infinite alternate}
@keyframes cnumLit{to{opacity:1}}
.c-brand{font-family:var(--num); font-size:16px; letter-spacing:.3em; fill:#8f86c8; text-anchor:middle}
/* Picasso: ink numbers on painted patches, the next one in red; the painter's signature in the corner. */
.face-picasso .c-num{fill:#1a1410; font-family:Georgia, "Times New Roman", serif; font-weight:700}
.face-picasso .c-num.next{fill:#c8102e}
.face-picasso .c-lit{fill:#2b5c8a}
.c-sign{font-family:"Brush Script MT", "Segoe Script", cursive; font-size:34px; fill:#1a1410}
/* Mid-century: cream numbers in a clean geometric face on the walnut. */
.face-midcentury .c-num{fill:#fbf1dc; font-family:Futura, "Century Gothic", "Avenir Next", "Trebuchet MS", sans-serif; font-weight:500; font-size:40px}
.face-midcentury .c-brand{fill:#e9d3a0}
.face-midcentury .c-num.next{fill:#ffd84a} .face-midcentury .c-lit{fill:#fff7d0}
.c-chime{fill:none; stroke:#ffd84a; stroke-width:10; opacity:0; transform-box:fill-box; transform-origin:center}
.c-chime.ring{animation:chime 1.2s ease-out}
@keyframes chime{0%{opacity:1; transform:scale(1)} 100%{opacity:0; transform:scale(1.35)}}
.gear{transform-box:fill-box; transform-origin:center}
.gear.g1{animation:spin 60s linear infinite} .gear.g2{animation:spin 40s linear infinite reverse} .gear.g3{animation:spin 30s linear infinite}
.hand, .hand-num{transform-box:view-box}
.hand:not(.on){opacity:.8}
.hand.on .hand-tip{stroke:#fff; stroke-width:4}

/* ---------- tic tac toe ---------- */
/* The grid is a square as big as the room allows either way: a phone held upright is narrower than it is tall,
   and the finished board (no dartboard beside it) was drawing the grid off both sides (28 Oct 2026). */
.ttt-wrap{padding:14px; display:grid; grid-template-rows:minmax(0,1fr) auto; gap:10px; justify-items:center; min-height:0; min-width:0; container-type:inline-size}
/* Drawn on the card's own ground: a deep blue wash with the light coming from above, the hash across it,
   and no box round anything (Cory, 2026-09-17). */
.ttt-grid{position:relative; height:100%; height:min(100%, 100cqw); aspect-ratio:1; max-width:100%; display:grid; grid-template:repeat(3, minmax(0,1fr)) / repeat(3, minmax(0,1fr)); gap:0; min-height:0;
  padding:clamp(6px, 1.4vh, 14px); border-radius:26px;
  background:radial-gradient(ellipse 80% 70% at 50% 34%, #1d4dd8, #060a22 74%)}
.ttt-hash{position:absolute; inset:clamp(6px, 1.4vh, 14px); width:auto; height:auto; pointer-events:none; overflow:visible}
.ttt-hash path{fill:none; stroke:#bcd4ff; stroke-opacity:.55; stroke-width:5; stroke-linecap:round; vector-effect:non-scaling-stroke}
.ttt-cell{--oc:transparent; position:relative; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:min(1.5cqh, 6px);
  background:none; border:0; border-radius:18px; transition:box-shadow .3s, background .3s; min-height:0; min-width:0; overflow:hidden}
/* The one you can still take glows from inside instead of wearing a border. */
.ttt-cell.live{background:radial-gradient(ellipse 66% 60% at 50% 50%, rgba(255,216,74,.13), transparent 72%)}
/* SIZED BY THE CELL, NOT THE WINDOW (Cory, 2026-09-21: "on my phone, both landscape and portrait modes
   have the X's misaligned"). The number and the two rows of marks were each sized off the viewport, so on
   a phone the three of them together were taller than the box they live in and the overflow spilled out
   past the grid lines — marks sitting above the top of the board, crosses straddling the line below the
   middle row. The cell is a size container (bottom of this file), so everything in it is measured against
   the cell itself and it cannot outgrow it whichever way the phone is held. */
.ttt-n{font-family:var(--num); font-size:min(30cqh, 26cqw, 40px); line-height:1; color:rgba(226,236,255,.82); position:relative; z-index:1}
.ttt-own{position:absolute; inset:0; display:grid; place-items:center}
/* Claimed: no fill, no frame — the mark itself is the claim, exactly as on paper. */
.ttt-cell.owned{background:radial-gradient(ellipse 70% 64% at 50% 50%, color-mix(in srgb, var(--oc) 22%, transparent), transparent 74%)}
/* a claimed cell keeps its number small in the corner so it never sits under the big X or O */
.ttt-cell.owned .ttt-n{position:absolute; top:4%; left:7%; font-size:min(17cqh, 15cqw, 19px); opacity:.55}
.ttt-marks{display:flex; align-items:center; justify-content:center; gap:min(2.5cqw, 10px); height:24cqh; min-height:0; position:relative; z-index:1}
.ttt-mini{flex:none; width:min(21cqh, 19cqw, 40px); height:min(21cqh, 19cqw, 40px); overflow:visible; filter:drop-shadow(0 0 6px rgba(255,255,255,.18)); animation:tttMini .35s cubic-bezier(.2,1.6,.4,1)}
@keyframes tttMini{from{transform:scale(.2); opacity:0}}
.ttt-glyph{width:72%; height:72%; overflow:visible}
/* The mark's resting state is DRAWN. The dash trick used to live on the element, so anything that stopped
   the animation short — a re-render, a reduced-motion setting, a tab that was in the background — left an
   invisible X on a claimed square. The dashes now live inside the keyframes, so the animation is the
   entrance and the end state is simply a finished mark. */
.ttt-glyph .draw, .ttt-line .draw{fill:none; stroke-width:12; stroke-linecap:round; animation:draw .45s ease-out both; filter:drop-shadow(0 0 10px rgba(255,255,255,.5))}
.ttt-glyph .d2{animation-delay:.2s}
@keyframes draw{from{stroke-dasharray:460; stroke-dashoffset:460} to{stroke-dasharray:460; stroke-dashoffset:0}}
.ttt-line{position:absolute; inset:0; width:100%; height:100%; pointer-events:none; overflow:visible}
.ttt-line .draw{stroke-width:9; animation-duration:1.2s; animation-timing-function:cubic-bezier(.45,0,.25,1)}
.ttt-cell.pop{animation:cellPop .35s ease-out}
.ttt-cell.claim{animation:cellClaim .6s cubic-bezier(.2,1.5,.4,1)}
@keyframes cellPop{50%{transform:scale(.96)}}
@keyframes cellClaim{0%{transform:scale(.8)} 100%{transform:scale(1)}}
.ttt-legend{display:flex; gap:18px; font-weight:700}
.ttt-legend span{display:flex; align-items:center; gap:8px}
.ttt-legend b{font-family:var(--num); font-weight:400; font-size:22px; color:var(--pc)}

/* ---------- monster bash ---------- */
.mon-wrap{position:relative; display:grid; min-height:0}
/* ONE CLOCK FOR THE WHOLE FIGHT (Cory, 2026-09-22, on the combat rework). Every duration the arena uses is
   a property here, and js/views/monsters.js reads these same properties back with getComputedStyle before
   it schedules a single thing. Change a number and the motion and the timing move together — there is no
   second copy in the JavaScript to forget about. Reduced motion is the same block with small numbers. */
.mon-wrap{--t-inhale:200ms; --t-gap:120ms; --t-release:240ms; --t-fly:350ms; --t-charge:600ms; --t-orb:300ms;
  --t-orb-gap:140ms; --t-gulp:400ms; --t-cough:360ms; --t-flash:140ms; --t-hurt:660ms; --t-splash:420ms;
  --t-husk:800ms; --t-pop:1000ms; --t-settle:80ms; --t-finale-fly:420ms; --t-finale-gap:90ms;
  --vit:#3cf0a0; --gold:#ffd84a}
/* THE FOUR BREATHS, IN ONE PLACE (Cory, 2026-09-22: "Fixed element per slot for now"). The art emits no
   colour at all on anything the element owns — the glow in a throat, a shot's head, the splash, a husk's
   embers — so a monster's fire can never drift away from its fire. Here rather than in app.css because the
   phone's live screen loads board.css and views.css and nothing else (app/live.html). */
[data-el="fire"]{--el-core:#fff3b0; --el-hot:#ff7a1a; --el-edge:#ff2a00; --el-dark:#5a0e00}
[data-el="volt"]{--el-core:#eaf4ff; --el-hot:#56d0ff; --el-edge:#3b3fe8; --el-dark:#0b2fa8}
[data-el="acid"]{--el-core:#eafff3; --el-hot:#3cf0a0; --el-edge:#00c070; --el-dark:#05603a}
/* Frost was near-white on both the head and the halo, so an ice blast came out as a white starburst with
   no element in it — and it sat too close to lightning, the other blue. The head keeps its ice tint, the
   halo goes deep and cold, and lightning moves towards indigo so the two can never be confused. */
[data-el="frost"]{--el-core:#ffffff; --el-hot:#a8e4ff; --el-edge:#2b7fc4; --el-dark:#1c5a8a}
/* The arena's own floor, and it has to out-rank .scene's daylight blue further down this file — same
   specificity means the later rule wins, which is why a phone on its side showed a pale blue box with the
   monsters lost inside it (Cory, 2026-09-20). */
.scene.mon-scene{background:linear-gradient(180deg, #120a30, #080418)}
.mon-vs{font-family:var(--num); font-size:110px; text-anchor:middle; dominant-baseline:central; opacity:.9}
/* NO SPOTLIGHTS (Cory, 2026-09-24: "remove those spot lights also they're not good and they're just
   distracting"). They were three swaying cones across the back of the stage; they picked out bodies lying
   on the floor as happily as live ones, and they were competing for attention with the fight. The arena
   keeps its own lit backdrop, which is quieter and leaves the room to the monsters. */
/* THE TRANSITION LIVES HERE, NOT ON .mon.ko. Taking the class off again — an undo, a re-render from
   state after somebody's HP went back above zero — has to stand the body back UP over the same eight
   tenths of a second. A transition declared only on the .ko rule is gone the instant the class is, and the
   monster snaps upright in one frame. */
.mon-rig{transform-box:fill-box; transform-origin:50% 100%;
  transition:transform var(--t-husk, .8s) cubic-bezier(.5,0,.7,.4), filter var(--t-husk, .8s)}
.mon .maw-glow, .mon .maw-fire{transform-box:fill-box; transform-origin:50% 50%}
.mon .maw-glow{fill:var(--el-hot)} .mon .maw-fire{fill:var(--el-hot)}
/* BREATHING, SIXTEEN TIMES A BREATH (2026-10-19, after Count Up lagged the phone). The body is inside the
   arena drawing, and anything that moves inside a drawing redraws all of it - every monster, the floor, the
   sky - on every frame it moves; a breath that never stops was sixty redraws a second all game. The breath
   now holds each of sixteen eased poses (the same ease-in-out curve, written out) for an eighth of a second:
   about seven redraws a second, and the fight - which lives on the same drawing - is untouched. */
.mon .mon-body{transform-box:fill-box; transform-origin:50% 100%; animation:breathe 2.2s steps(1, end) infinite alternate}
.mon.turn .mon-body{animation-duration:.9s}
.mon .pupil{animation:look 4s ease-in-out infinite}
.mon .antenna{animation:blink 1.4s steps(2) infinite}
.mon-shadow{fill:rgba(0,0,0,.4)}
@keyframes breathe{0%{transform:scale(1.0000,1.0000)} 6.25%{transform:scale(1.0002,0.9998)} 12.5%{transform:scale(1.0009,0.9991)} 18.75%{transform:scale(1.0021,0.9979)} 25%{transform:scale(1.0039,0.9961)} 31.25%{transform:scale(1.0061,0.9939)} 37.5%{transform:scale(1.0088,0.9912)} 43.75%{transform:scale(1.0118,0.9882)} 50%{transform:scale(1.0150,0.9850)} 56.25%{transform:scale(1.0182,0.9818)} 62.5%{transform:scale(1.0212,0.9788)} 68.75%{transform:scale(1.0239,0.9761)} 75%{transform:scale(1.0261,0.9739)} 81.25%{transform:scale(1.0279,0.9721)} 87.5%{transform:scale(1.0291,0.9709)} 93.75%{transform:scale(1.0298,0.9702)} 100%{transform:scale(1.0300,0.9700)}}
@keyframes look{0%,40%{transform:translate(0,0)} 50%,90%{transform:translate(-8px,2px)}}
@keyframes blink{50%{fill:#ff6ad5}}
/* WHAT COMES OUT OF THE MOUTH (Cory, 2026-09-22: the melee swings were "the worst part of the game and it
   should be one of the best"; "maybe the monsters that shoot fireballs out of their mouths").

   Nobody reaches for anybody any more. A monster plants its feet, draws its breath in, and throws its
   element down the arena, so every frame is a wind-up, a thing in the air, or a landing — and none of it
   depends on two bodies meeting in the middle at the right moment, which is what made the swings look
   wrong. A double breathes twice and a triple three times, at every target the slice covers at once.

   The rig owns the squash and the knock; the body underneath keeps breathing on its own pivot, so being
   hit never cancels the idle and the two can overlap without either one being restarted. */
.mon .blink{animation:monBlink 4.6s steps(1) infinite; animation-delay:calc(var(--i, 0) * -.9s)}
@keyframes monBlink{0%,95.9%{opacity:0} 96%,98.9%{opacity:1} 99%,100%{opacity:0}}
.mon .maw-glow{animation:mawIdle 3.1s ease-in-out infinite alternate; animation-delay:calc(var(--i, 0) * -.7s)}
@keyframes mawIdle{from{opacity:.10} to{opacity:.28}}
/* Drawing it in: the body squashes down and back, the eyes narrow, the throat lights up. */
.mon.inhale .mon-rig{animation:monInhale var(--t-inhale) ease-out forwards}
@keyframes monInhale{0%{transform:none} 60%{transform:scale(1.2,.82) translateX(6px)} 100%{transform:scale(1.26,.8) translateX(10px)}}
.mon.inhale .pupil{animation:monNarrow var(--t-inhale) ease-out forwards}
@keyframes monNarrow{to{transform:scale(1,.55)}}
.mon.inhale .lid{animation:monLidDown var(--t-inhale) ease-out forwards}
@keyframes monLidDown{to{transform:translateY(22px)}}
/* And letting it go: a hard push forward off the back foot, mouth open for as long as it is coming out. */
.mon.release .mon-rig{animation:monRelease var(--t-release) cubic-bezier(.2,1.4,.4,1)}
@keyframes monRelease{
  0%{transform:scale(1.26,.8) translateX(10px)}
  22%{transform:translateX(-30px) scale(.82,1.3)}
  48%{transform:translateX(-10px) scale(1.08,.94)}
  74%{transform:translateX(4px) scale(.97,1.04)}
  100%{transform:none}
}
.mon.release .mouth-closed, .mon.cough .mouth-closed{opacity:0}
.mon.release .mouth-open, .mon.cough .mouth-open{opacity:1}
/* THE BULL (Cory, 2026-09-22: "bull and double bull also charge you up with health so in addition to the
   reign of terror, you should glow, energetic and confident!"). The health comes FIRST — chest out, eyes
   wide, gold all round it, the bar climbing — and the rain comes out of that, so the moment reads as being
   handed something and then using it, not as an attack that happens to heal. The grin stays shut for the
   charge; an open mouth would read as the attack starting early. */
.mon.charge .mon-rig{animation:monCharge var(--t-charge) cubic-bezier(.25,1.1,.4,1) forwards}
@keyframes monCharge{
  0%{transform:none; filter:none}
  20%{transform:translateY(-6px) scale(1.03,1.05); filter:drop-shadow(0 0 30px var(--gold)) brightness(1.35)}
  30%{transform:translateY(-10px) scale(1.06,1.10)}
  100%{transform:translateY(-6px) scale(1.04,1.04); filter:drop-shadow(0 0 30px var(--gold)) brightness(1.35)}
}
.mon.charge .pupil{animation:monShine var(--t-charge) ease-out forwards}
@keyframes monShine{to{transform:scale(1.2)}}
.mon.charge .maw-glow{animation:monThroat var(--t-charge) ease-out forwards}
@keyframes monThroat{to{opacity:1; transform:scale(1.5)}}
/* A HEAL IS A POWER-UP, AND A TRIPLE IS A BIG ONE (Cory, 2026-09-23: "on a triple health it's too subtle.
   A triple health should be a huge power up, muscles flexing even, then back to normal"). A single still
   swallows and gets on with it; two or three pump the whole body up, hold it there long enough to be seen,
   and let it back down. The hold is the part that sells it — without it a scale-up and a scale-down inside
   400ms is just a wobble. */
.mon.gulp .mon-rig{animation:monGulp var(--t-gulp) ease-out}
@keyframes monGulp{0%{transform:none} 50%{transform:translateY(-8px) scale(1.16,1.14); filter:drop-shadow(0 0 22px var(--vit)) brightness(1.25)} 100%{transform:none}}
.mon.gulp.flex .mon-rig{animation-name:monFlex; animation-duration:calc(var(--t-gulp) * 2.1)}
.mon.gulp.flex .pupil{animation-duration:calc(var(--t-gulp) * 2.1)}
@keyframes monFlex{
  0%{transform:none; filter:none}
  14%{transform:translateY(4px) scale(1.12,.86)}
  34%{transform:translateY(-22px) scale(1.34,1.26); filter:drop-shadow(0 0 40px var(--vit)) brightness(1.5) saturate(1.3)}
  52%{transform:translateY(-18px) scale(1.4,1.2); filter:drop-shadow(0 0 46px var(--vit)) brightness(1.55) saturate(1.35)}
  68%{transform:translateY(-20px) scale(1.3,1.28); filter:drop-shadow(0 0 40px var(--vit)) brightness(1.45) saturate(1.3)}
  86%{transform:translateY(-4px) scale(1.06,1.04); filter:drop-shadow(0 0 16px var(--vit)) brightness(1.12)}
  100%{transform:none; filter:none}
}
.mon.gulp.flex .pupil{animation:monShine var(--t-gulp) ease-out}
.mon.gulp .mouth-open{animation:monSwallow var(--t-gulp) linear}
@keyframes monSwallow{0%,49.9%{opacity:0} 50%,99.9%{opacity:1} 100%{opacity:0}}
.mon.gulp .mouth-closed{animation:monSwallowShut var(--t-gulp) linear}
@keyframes monSwallowShut{0%,49.9%{opacity:1} 50%,99.9%{opacity:0} 100%{opacity:1}}
/* A dart that hits nothing: the breath catches and comes out as three puffs of nothing at all. */
.mon.cough .mon-rig{animation:monCough var(--t-cough) ease-out}
@keyframes monCough{0%,100%{transform:none} 25%{transform:translateX(10px) scale(1.05,.95)} 45%{transform:translateX(-6px) scale(.97,1.03)} 70%{transform:translateX(3px)}}
.mon.cough .pupil{animation:monWince var(--t-cough) ease-out}
@keyframes monWince{0%,100%{transform:none} 30%{transform:scale(1,.4)}}
/* BEING HIT, IN THE DIRECTION IT CAME FROM. --kx is +1 or -1 in the victim's OWN (possibly flipped) frame
   and --ky is -1 when it came from above, so one set of keyframes covers every pairing on every stage
   instead of a left twin and a right twin that have to be kept in step by hand. */
.mon.hurt .mon-rig{animation:monHurt var(--t-hurt) cubic-bezier(.2,.85,.3,1)}
@keyframes monHurt{
  0%{transform:none; filter:none}
  7%{transform:translate(calc(var(--kx, -1) * 14px), calc(var(--ky, 0) * 8px)) scale(1.16,.84); filter:brightness(4) saturate(.2)}
  20%{transform:translate(calc(var(--kx, -1) * 104px), calc(var(--ky, 0) * 40px)) rotate(calc(var(--kx, -1) * 16deg)) scale(.88,1.1); filter:brightness(1.7) sepia(1) saturate(7) hue-rotate(-26deg)}
  42%{transform:translate(calc(var(--kx, -1) * 58px), calc(var(--ky, 0) * 18px)) rotate(calc(var(--kx, -1) * 10deg)) scale(1.04,.96); filter:brightness(1.2) sepia(.7) saturate(5) hue-rotate(-26deg)}
  62%{transform:translate(calc(var(--kx, -1) * -22px), 0) rotate(calc(var(--kx, -1) * -6deg)) scale(.98,1.03); filter:none}
  80%{transform:translate(calc(var(--kx, -1) * 12px), 0) rotate(calc(var(--kx, -1) * 3deg))}
  100%{transform:none; filter:none}
}
/* Nine landings at once on a 2019 tablet cannot afford nine sepia-and-hue-rotate passes, so a crowded wave
   drops to transforms and a single brightness frame. At that speed it reads the same. */
.mon.hurt.lite .mon-rig{animation-name:monHurtLite}
@keyframes monHurtLite{
  0%{transform:none; filter:none}
  7%{transform:translate(calc(var(--kx, -1) * 14px), calc(var(--ky, 0) * 8px)) scale(1.16,.84); filter:brightness(3)}
  20%{transform:translate(calc(var(--kx, -1) * 88px), calc(var(--ky, 0) * 34px)) rotate(calc(var(--kx, -1) * 14deg)) scale(.9,1.08); filter:none}
  48%{transform:translate(calc(var(--kx, -1) * 40px), 0) rotate(calc(var(--kx, -1) * 7deg))}
  74%{transform:translate(calc(var(--kx, -1) * -14px), 0) rotate(calc(var(--kx, -1) * -4deg))}
  100%{transform:none; filter:none}
}
/* Eyes blow wide on contact and then roll; the eyelid Blorp has comes down with them. */
.mon.hurt .pupil{animation:monWide var(--t-hurt) ease-in-out}
@keyframes monWide{0%{transform:none} 15%{transform:scale(1.35)} 45%{transform:translate(-16px,-12px) scale(1.1)} 72%{transform:translate(14px,10px)} 100%{transform:none}}
.mon.hurt .lid{animation:monSquint var(--t-hurt) ease-out}
@keyframes monSquint{0%,100%{transform:none} 25%{transform:translateY(36px)} 60%{transform:translateY(18px)}}
/* WHAT IS LEFT (Cory, 2026-09-22, choosing the husk over a puff of smoke: "sure"). A knocked-out monster
   stays in its spot wearing whatever killed it, so the arena keeps a record of the fight instead of
   tidying itself up. It tips AWAY from the shot that finished it — the same --kx the knock-back used. */
.mon.ko .mon-rig{transform:rotate(calc(var(--kx, -1) * 74deg)) translateY(46px) scale(.85); filter:grayscale(.85) brightness(.55)}
.mon.ko .mon-body, .mon.ko .pupil, .mon.ko .antenna, .mon.ko .blink{animation:none}
.mon.ko .maw-glow{animation:none; opacity:0}
.mon.ko .eye-x{opacity:1}
.mon.ko.ko-fire .mon-rig{filter:grayscale(1) brightness(.3) contrast(1.3)}
.mon.ko.ko-volt .mon-rig{filter:saturate(.25) brightness(1.15) contrast(1.35)}
.mon.ko.ko-volt .mon-body{animation:monShocked 1.1s steps(3) infinite}
@keyframes monShocked{0%,100%{transform:translateX(0)} 50%{transform:translateX(2px)}}
/* THE POSE IS THE SAME FOR ALL FOUR (Cory, 2026-09-23: "the character sideways, eyes X'd and them burning
   and smouldering a bit when they're dead"). The element used to change the pose as well as the colour, so
   an acid husk lay flat and a frost husk stayed on its feet — and a monster still standing does not read
   as out, whatever its eyes are doing. Everybody goes over sideways. The element is left to say what it
   did to them: charred, bleached, eaten, or frozen through. */
.mon.ko.ko-acid .mon-rig{filter:saturate(.75) brightness(.6) contrast(1.15)}
.mon.ko.ko-frost .mon-rig{filter:saturate(.45) brightness(1.2) contrast(.9)}
/* Upright: the arena is a column, so the bars stack instead of sitting in a row — but four stacked bars is
   half a phone, so past two players they go two abreast, the way the monsters do. */
/* TWO ABREAST, ALWAYS, WHEN THE STAGE IS UPRIGHT. A watching phone in portrait gives the arena a box
   about 384px across and 418 tall; two cards stacked down it took 126 of that — nearly a third — and left
   two monsters 133px each. Side by side they take 63 and the monsters get the rest. The cards themselves
   are 189px wide on the tablet's own portrait scoreboard and read fine, and these are wider than that. */
.mon-wrap.upright .mon-hud{grid-template-columns:repeat(2, minmax(0,1fr)); gap:6px}
.mon-wrap.upright .mon-hp{padding:6px 8px}
/* THE FINISH. Everything not in it goes and the lights come down, but nothing is taken off the stage:
   the winner stands over what is left of the last one and empties itself into it. */
.mon-wrap.final .mon-scene{filter:saturate(.5) brightness(.6); transition:filter .7s}
.mon .faded, .mon.faded{opacity:.18; filter:grayscale(1); transition:opacity .7s, filter .7s}
/* Only the winner at the end (js/views/monsters.js solo). */
.mon.gone, .mon-wrap.solo .mon-vs, .mon-wrap.solo .mon-ledge, .mon-wrap.solo #monHusks, .mon-wrap.solo .mon-shadow{opacity:0; transition:opacity .6s}
/* The finish does not take the loser away any more — it stands over the husk and empties everything it has
   into it. Nothing on this stage vanishes, in play or at the end of it. */
.mon-husk.smoulder{transform:scale(1.3); opacity:1; transition:transform .5s ease-out, opacity .5s}
.mon.ko.crushed .mon-rig{transform:rotate(calc(var(--kx, -1) * 74deg)) translateY(46px) scale(.85,.5)}
.mon-wrap.quake{animation:monQuake .5s linear}
@keyframes monQuake{0%,100%{transform:none} 15%{transform:translate(-9px,5px)} 35%{transform:translate(8px,-6px)} 55%{transform:translate(-6px,-3px)} 75%{transform:translate(4px,4px)}}
@media (prefers-reduced-motion:reduce){ .mon-wrap.quake, .mon-wrap.jolt{animation:none} }
/* Every blow shakes the arena a little; a critical or a knockout shakes the whole thing. */
.mon-wrap.jolt{animation:monJolt .2s linear}
@keyframes monJolt{0%,100%{transform:none} 30%{transform:translate(-5px,3px)} 65%{transform:translate(4px,-3px)}}
/* IN THE AIR. Nothing that flies carries a filter — the 2019 tablet drops frames on a filtered node moving
   across the stage — so the glow is a fat translucent halo behind the head instead of a drop-shadow. The
   art draws every shot pointing along +x and the view rotates it to its own tangent, so the same drawing
   flies correctly in both directions with no mirrored copy of it. */
/* THE HEAD CARRIES THE COLOUR, not the halo. Filling the head with --el-core (which is near-white for all
   four) and haloing it in the same pale colour gave a grey lollipop with no element in it — frost worst of
   all. The saturated edge colour goes on the halo, the hot mid on the head, and the only white left is a
   small core at the tip, which is what makes it look like it is burning rather than glowing. */
.mon-shot .shot-tail{stroke:var(--el-hot)}
.mon-shot .shot-halo{fill:var(--el-edge)}
.mon-shot .shot-glow{fill:var(--el-hot)}
.mon-shot .shot-head{fill:var(--el-hot); stroke:var(--el-core); stroke-width:2; stroke-linejoin:round}
.mon-shot .shot-head.zag{animation:zapFlick 80ms steps(2) infinite}
@keyframes zapFlick{50%{opacity:.6}}
.mon-shot .shot-trail{fill:var(--el-hot); opacity:.8}
.mon-shot.bull .shot-halo{fill:var(--gold); opacity:.55}
.mon-shot.bull .shot-head{stroke:var(--gold)}
/* The flare at the lip, one tick long. Its placement is a transform on the wrapper the view makes, so the
   scale below has a group of its own to live on (an SVG transform attribute and a CSS transform are the
   same slot — setting one wipes the other, which is the trap in fx.js:125). */
.mon-muzzle{transform-box:fill-box; transform-origin:50% 50%; animation:monMuzzle .18s ease-out forwards}
@keyframes monMuzzle{from{transform:scale(.4); opacity:.95} to{transform:scale(1.6); opacity:0}}
.mon-muzzle .mz-ray{stroke:var(--el-hot)}
.mon-muzzle .mz-core{fill:var(--el-core)}
/* THE LANDING. This is the beat the whole animation exists for, so it is the only place that gets eight
   moving pieces: a ring that snaps outwards and eight bits of the element thrown along the arrival. */
.mon-flash{fill:#fff; animation:monFlash var(--t-flash) ease-out forwards}
@keyframes monFlash{from{opacity:.72} to{opacity:0}}
.mon-splash .sp-ring{stroke:var(--el-core); animation:monShock var(--t-splash) ease-out forwards}
@keyframes monShock{from{r:18px; stroke-width:18px; opacity:.95} to{r:200px; stroke-width:2px; opacity:0}}
.mon-splash .sp{fill:var(--el-hot); stroke:var(--el-edge); stroke-width:2}
.mon-splash[data-el="volt"] .sp{fill:none; stroke:var(--el-hot)}
.mon-puff{transform-box:fill-box; transform-origin:50% 50%; animation:monPuff .18s ease-out forwards}
@keyframes monPuff{from{r:8px; opacity:.8} to{r:26px; opacity:0}}
.mon-puff.smoke{animation:monSmoke .3s ease-out forwards}
@keyframes monSmoke{from{r:8px; opacity:.6; transform:none} to{r:26px; opacity:0; transform:translate(var(--dx, 30px), -40px)}}
/* Charging up, and being healed: the same shapes in two colours, so both read as the same kind of good
   news at two volumes. Gold is the bull; green is an ordinary heal slice. */
.mon-orb .orb-halo, .mon-orb .orb-spark{fill:var(--vit)}
.mon-orb.bull .orb-halo, .mon-orb.bull .orb-spark{fill:var(--gold)}
.mon-aura .au-ring{stroke:var(--vit)} .mon-aura.bull .au-ring{stroke:var(--gold)}
.mon-aura .au-spark{fill:var(--vit)} .mon-aura.bull .au-spark{fill:var(--gold)}
.mon-aura .au-ring, .mon-aura .au-spark{transform-box:fill-box; transform-origin:50% 50%}
.mon-aura .au-ring{animation:auRing var(--t-charge) ease-out forwards}
@keyframes auRing{from{transform:scale(.3); opacity:.9} to{transform:scale(1.15); opacity:0}}
.mon-aura .au-spark{animation:auSpark var(--t-charge) ease-out forwards}
@keyframes auSpark{0%{transform:translateY(0); opacity:0} 30%{opacity:1} 100%{transform:translateY(-220px); opacity:0}}
.mon-aura .au-spark:nth-of-type(even){animation-delay:80ms}
.mon-drawin{fill:var(--el-hot)}
/* AND WHAT IS LEFT OF THE LOSER. The garnish sits in its own layer beside the bodies, never inside one: a
   body's squash pivots off its own fill box, and an extra child would move that box. */
.mon-husk *{transform-box:fill-box; transform-origin:50% 50%}
.mon-husk{transform-box:fill-box; transform-origin:50% 50%; animation:huskIn .4s var(--t-husk) both}
@keyframes huskIn{from{opacity:0} to{opacity:1}}
/* Embers, low flames and smoke on every husk, whatever knocked it over. The delay on each one is its own
   so the patch never pulses as a single object. */
.mon-husk .hk-ember{fill:#ff8a1f; animation:hkEmber 1.6s ease-in-out infinite alternate}
@keyframes hkEmber{from{opacity:.3} to{opacity:.95}}
.mon-husk .hk-flame{fill:#ff7a1a; opacity:.8; animation:hkFlame 1.1s ease-in-out infinite alternate; animation-delay:var(--d, 0s)}
@keyframes hkFlame{from{transform:scaleY(.7) scaleX(1.1); opacity:.5} to{transform:scaleY(1.15) scaleX(.9); opacity:.95}}
.mon-husk .hk-smoke{fill:#6a6276; animation:hkSmoke 3.4s ease-out infinite; animation-delay:var(--d, 0s)}
@keyframes hkSmoke{0%{transform:translateY(6px) scale(.5); opacity:0} 25%{opacity:.34} 100%{transform:translateY(-52px) scale(1.5); opacity:0}}
.husk-fire .hk-ember{fill:var(--el-hot)}
.husk-volt .hk-spark{stroke:var(--el-hot); animation:hkSpark .9s steps(2) infinite}
@keyframes hkSpark{0%,60%{opacity:0} 61%,80%{opacity:1} 81%,100%{opacity:0}}
.husk-acid .hk-pool{fill:var(--el-edge); opacity:.55}
.husk-acid .hk-drip{fill:var(--el-hot); animation:hkDrip 2.2s ease-in infinite}
@keyframes hkDrip{0%{transform:translateY(-10px); opacity:0} 20%{opacity:1} 100%{transform:translateY(18px); opacity:0}}
.husk-frost .hk-ice{fill:var(--el-hot); opacity:.85}
.husk-frost .hk-ring{stroke:var(--el-edge); opacity:.6}
/* THE NUMBERS ARE SIZED OFF HOW BIG A STAGE UNIT ACTUALLY CAME OUT. The arena on a phone is a fifth of the
   size it is on a TV, and a flat 40-unit number was eight pixels there. The view measures the drawing and
   writes --pop; everything below is a multiple of it, so a triple stacks three readable numbers. */
#monPops .lanepop{font-size:var(--pop, 40px)}
#monPops .lanepop.dmg{fill:#ff5c7a; stroke:#2a0010}
#monPops .lanepop.dmg2{font-size:calc(var(--pop, 40px) * 1.15)}
#monPops .lanepop.dmg3{font-size:calc(var(--pop, 40px) * 1.3)}
#monPops .lanepop.crit{fill:#ffd84a; stroke:#4a2600; font-size:calc(var(--pop, 40px) * 1.3)}
#monPops .lanepop.heal{fill:#7dffc8; stroke:#003a22}
#monPops .lanepop.ko{fill:#ffffff; stroke:#6a0010; font-size:calc(var(--pop, 40px) * 1.4)}
#monPops .lanepop.miss{fill:#dfe6ff; stroke:#1a1030; font-size:calc(var(--pop, 40px) * .85)}
.mon-hud{position:absolute; left:12px; right:12px; top:12px; display:grid; grid-template-columns:1fr 1fr; gap:14px; pointer-events:none}
/* ONE DIRECTION, EVERY PLAYER (Cory, 2026-09-20: "the power meters should both go the same direction. For
   all players"). A two-player game used to mirror the second card, so the pair faced each other the way a
   fighting game lays them out. On a phone, stacked one above the other, it reads as a fault: two bars, one
   draining left and one draining right, with nothing on screen saying why. They all fill from the left. */
.mon-hp{padding:10px 12px; border-radius:16px; background:rgba(8,4,24,.72); border:1px solid color-mix(in srgb, var(--pc) 45%, transparent); transition:box-shadow .3s; min-width:0}
.mon-hp.on{box-shadow:0 0 0 2px var(--pc), 0 0 24px color-mix(in srgb, var(--pc) 45%, transparent)}
/* ONE CARD PER MONSTER, AND ONLY ONE (Cory, 2026-09-24). It carries everything the row underneath used to
   repeat: who they are, what they have scored, what health is left, and — for everybody except whoever is
   throwing — the three numbers that hurt them. The thrower has no block of its own, so that space is
   simply empty on their card rather than showing something they cannot use. */
.mon-hp-top{display:flex; align-items:center; gap:8px; margin-bottom:6px; min-width:0}
/* TWO ROWS, EACH SAYING ONE KIND OF THING (Cory, 2026-09-25). The top row is what you need to DO — the
   three numbers that hurt this one, and what they have scored. The bottom row is what they have LEFT: the
   bar, and the number at the end of it. The health number used to sit up top next to the points, which
   read as two scores side by side; it belongs on the end of its own bar. */
/* NUMBERS HARD LEFT, POINTS IN THE MIDDLE, AND NO SEAT BADGE (Cory, 2026-09-25: "player numbers don't
   show up in landscape mode, but that's okay... it's obvious who the player is because the information is
   right above the player — let's put the hit numbers to the very far left... I just don't want to get
   confused with the number below the health number").

   The badge had already been dropped on a short screen and nothing was lost, so it goes everywhere: the
   card sits directly over its monster and is outlined in that player's colour. What it was spending — a
   26px square and a gap — goes to the numbers, which are the thing you actually read.

   The points are CENTRED rather than right-aligned, which is the whole reason he raised it: flush right
   puts "14 PTS" directly over the health number on the end of the bar, two numbers on the same edge
   meaning different things. An empty track after it does the centring, so the middle of the card is the
   middle of the card; when the numbers need more room than that leaves, they keep it and the points give
   way rather than anything overflowing. */
.mon-hp-top .mon-nums{flex:1 1 0; justify-content:flex-start}
.mon-hp-top::after{content:''; flex:1 1 0; min-width:0}
.mon-hp-top u{text-decoration:none; font-family:var(--num); font-size:16px; color:var(--pc);
  opacity:.95; white-space:nowrap; flex:0 0 auto}
/* THE HEALTH NUMBER SITS ON THE END OF THE HEALTH BAR (Cory, 2026-09-25), where the numbers that hurt an
   opponent already sit. It was the tallest thing in the row above, so moving it down takes that row's
   height with it — which is the point: the height comes off the card and goes to the monsters. */
.mon-row strong{font-family:var(--num); font-weight:400; font-size:21px; line-height:1; flex:0 0 auto; margin-left:auto}
.mon-hp.ko .mon-row strong{color:var(--muted)}
/* THE TOTAL READS FROM ACROSS THE ROOM (Cory, 31 Oct 2026: "the total number of points is very tiny"). The number is
   one and a half times the PTS beside it, on every screen: each size rule below sets the PTS and the number follows. */
.mon-hp-top u b{font-weight:400; font-size:1.3em; line-height:1}
/* A TV has the room for more; a phone or a laptop with four cards does not (the three numbers come first). */
@media (min-width:1600px) and (min-height:900px){ .mon-hp-top u b{font-size:1.6em} }
.mon-hp-top u small{font-size:1em}
/* Three or four cards below TV size: the three numbers and a two-digit total fill the row, so the word goes and the number
   stays (it was running off the card at 1280 wide and on a phone on its side). */
@media (max-width:1599px), (max-height:899px){ .mon-hud.n3 .mon-hp-top u small, .mon-hud.n4 .mon-hp-top u small{display:none} }
@media (min-width:601px) and (min-height:561px){
  .mon-hp-top u{font-size:clamp(16px, 2vh, 22px)}
}
/* WHO IS WHO, AT A GLANCE (Cory, 31 Oct 2026, from across the room: "There needs to be names more prominent above the
   characters so we can tell who is who"). The card sits over its monster; the name under its bar says whose it is. */
.mon-name{font-family:var(--num); font-size:clamp(20px, 3.6vh, 40px); line-height:1.1; color:var(--pc); margin-top:6px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; text-transform:uppercase; letter-spacing:.02em;
  text-shadow:0 2px 0 rgba(0,0,0,.6)}
.mon-hud.n4 .mon-name, .mon-hud.n3 .mon-name{font-size:clamp(18px, 2.9vh, 32px)}
/* ONE TICK PER HIT POINT (Cory, 2026-09-24: "let's remove the smooth power bar and instead make it ticks
   of health"). A glance now says "four left" instead of "about a fifth", and a dart that takes three of
   them is visibly worth three. The ticks go out from the TOP DOWN a few milliseconds apart — --k counts
   backwards from the end for exactly that — so a big hit reads as a run of lights going off rather than
   a bar quietly shrinking. */
.mon-bar{display:flex; align-items:stretch; gap:2px; height:14px; min-width:0}
.mon-bar i{flex:1 1 0; min-width:0; border-radius:2px; background:rgba(255,255,255,.09);
  transition:background .2s ease, box-shadow .2s ease; transition-delay:calc(var(--k, 0) * 11ms)}
.mon-bar i.on{background:var(--pc); box-shadow:0 0 5px color-mix(in srgb, var(--pc) 55%, transparent)}
/* THE INVISIBLE REWIND. A dart arrives with the damage already done to the state, so the view puts the bar
   back to what it was a moment ago and then drains it as the breath lands. That first write must not be
   seen travelling — it is not a thing that happened — so it goes in with no transition at all. */
.mon-bar.snap i{transition:none !important}
.mon-hp.low .mon-bar i.on{background:#ff2d55; box-shadow:0 0 6px rgba(255,45,85,.7); animation:lowhp .6s ease-in-out infinite alternate}
@keyframes lowhp{to{opacity:.6}}
/* WHAT TO THROW AT THEM, WRITTEN ON THEM (Cory, 2026-09-23: "put the numbers that have to be hit to hurt
   the opponent, by the opponent somewhere"). The board underneath is already painted in everybody's
   colours, but working out whose is whose means looking down at the board and back up again. The three
   numbers that hurt this monster belong on this monster's card, and its points sit on the other end of the
   same row. */
.mon-row{display:flex; align-items:center; gap:7px; min-width:0}
.mon-row .mon-bar{flex:1 1 auto; min-width:0}
.mon-nums{display:flex; align-items:center; gap:4px; flex:0 0 auto}
.mon-nums i{font-family:var(--num); font-style:normal; font-size:18px; line-height:1; padding:3px 8px; border-radius:7px;
  background:color-mix(in srgb, var(--pc) 26%, rgba(255,255,255,.07)); color:#fff;
  border:1px solid color-mix(in srgb, var(--pc) 55%, transparent)}
.mon-hp.ko .mon-nums i{display:none}
/* FOUR ACROSS IS THE TIGHT ONE, AND IT IS TIGHT ON A BIG SCREEN TOO. A phone was always measured for; a
   laptop never was, because a laptop looks roomy — but the cards sit over the arena panel, not the window,
   and four of them share about 700px. At full size the three chips and the points came to 192px inside a
   174px card and spilled over the edge onto the neighbour. (The seat badge used to make it 226px; dropping
   it helped and did not fix it.) Phones set these sizes themselves further down, so this asks for a screen
   that is neither narrow nor short and leaves those rules alone. */
@media (min-width:601px) and (min-height:561px){
  .mon-hud.n4 .mon-nums{gap:3px}
  .mon-hud.n4 .mon-nums i{font-size:15px; padding:0 6px; line-height:21px; border-radius:6px}
  .mon-hud.n4 .mon-hp-top{gap:6px}
  .mon-hud.n4 .mon-hp-top u{font-size:clamp(13px, 1.6vh, 17px)}
}
/* MOVED HERE FROM screens.css (2026-09-22). The phone's live screen loads board.css and views.css and
   nothing else, so a knocked-out card stayed at full strength there and the stripes pill had no border.
   Everything the arena needs has to be in this file. Declared after the .mon-hud base rule above on
   purpose: same specificity, and this is the one that should win. */
.mon-hud{grid-template-columns:repeat(var(--n, 2), minmax(0,1fr))}
.mon-hp.ko{opacity:.45}
/* STILL AND QUIET, FOR ANYBODY WHO ASKED FOR IT. Same clock, small numbers: the results still land — the
   flash, the number, the bar, the husk — and nothing travels, spins or shakes to deliver them. */
@media (prefers-reduced-motion:reduce){
  .mon-wrap{--t-inhale:0ms; --t-gap:0ms; --t-release:10ms; --t-fly:10ms; --t-charge:10ms; --t-orb:10ms;
    --t-orb-gap:0ms; --t-gulp:10ms; --t-cough:10ms; --t-flash:10ms; --t-hurt:10ms; --t-splash:10ms;
    --t-husk:0ms; --t-settle:0ms}
  .mon-wrap.quake, .mon-wrap.jolt, .mon .mon-body, .mon .pupil, .mon .antenna, .mon .blink, .mon .maw-glow,
  .mon-husk *, .mon.ko.ko-volt .mon-body{animation:none}
  .mon-rig{transition:none}
  .mon-shot, .mon-splash, .mon-orb, .mon-puff, .mon-muzzle, .mon-aura, .mon-drawin{display:none}
  #monPops .lanepop.go{animation:lanepopCalm .9s ease-out forwards}
  @keyframes lanepopCalm{0%{opacity:0} 15%,75%{opacity:1} 100%{opacity:0}}
}

/* ---------- two line ---------- */
.tl-wrap{padding:14px; display:grid; grid-template-columns:minmax(0,1fr) auto; gap:14px; align-items:center; min-height:0; background:radial-gradient(ellipse at 40% 40%, rgba(255,190,40,.18), transparent 65%), linear-gradient(180deg, rgba(36,56,120,.50), rgba(9,14,38,.82))}
.tl-grid{position:relative; height:100%; max-height:100%; aspect-ratio:1; max-width:100%; justify-self:center; display:grid; grid-template:repeat(5, minmax(0,1fr)) / repeat(5, minmax(0,1fr)); gap:7px; min-height:0}
.tl-box{--oc:transparent; position:relative; display:grid; place-items:center; border-radius:12px; min-height:0; overflow:hidden;
  background:linear-gradient(160deg, #ffffff 0%, #dfe5f2 45%, #aab4cc 100%); box-shadow:inset 0 -4px 0 rgba(0,0,0,.18), inset 0 2px 0 rgba(255,255,255,.9), 0 4px 10px rgba(0,0,0,.35)}
.tl-box .tl-n{font-family:var(--num); font-size:clamp(18px, 5.2vh, 44px); line-height:1; color:#15203d; position:relative; z-index:1}
.tl-box.star{background:linear-gradient(160deg, #fffaf0 0%, #ffe8a8 50%, #e8b440 100%)}
.tl-box.star .tl-n{color:#b86a00; text-shadow:0 0 12px rgba(255,200,60,.8)}
.tl-box.owned{background:linear-gradient(160deg, color-mix(in srgb, var(--oc) 55%, #fff) 0%, var(--oc) 50%, color-mix(in srgb, var(--oc) 60%, #000) 100%); box-shadow:inset 0 -4px 0 rgba(0,0,0,.25), inset 0 2px 0 rgba(255,255,255,.55), 0 0 16px color-mix(in srgb, var(--oc) 55%, transparent)}
.tl-box.owned .tl-n{color:#fff; text-shadow:0 2px 0 rgba(0,0,0,.35)}
.tl-crack{position:absolute; inset:0; opacity:0; background:linear-gradient(115deg, transparent 44%, rgba(10,10,20,.75) 45%, rgba(10,10,20,.75) 48%, transparent 49%), linear-gradient(35deg, transparent 58%, rgba(10,10,20,.6) 59%, rgba(10,10,20,.6) 61%, transparent 62%)}
.tl-box.cracked .tl-crack{opacity:1}
.tl-box.flip{animation:tlFlip .5s ease-out}
.tl-box.hit{animation:tlHit .4s linear}
.tl-box.bump{animation:bump .4s cubic-bezier(.2,1.6,.4,1)}
@keyframes tlFlip{0%{transform:perspective(400px) rotateY(0)} 50%{transform:perspective(400px) rotateY(90deg) scale(1.08)} 100%{transform:perspective(400px) rotateY(0)}}
@keyframes tlHit{20%{transform:translateX(-6px) rotate(-4deg)} 40%{transform:translateX(5px) rotate(3deg)} 60%{transform:translateX(-3px)} 80%{transform:translateX(2px)}}
.tl-lines{position:absolute; inset:0; width:100%; height:100%; pointer-events:none; overflow:visible; z-index:2}
.tl-line{fill:none; stroke-width:12; stroke-linecap:round; opacity:.9; filter:drop-shadow(0 0 8px rgba(255,255,255,.7)); stroke-dasharray:720; stroke-dashoffset:720; animation:draw .6s ease-out forwards}
.tl-line.win{stroke-width:16; animation:draw .6s ease-out forwards, tlWin .6s .6s ease-in-out infinite alternate}
@keyframes tlWin{to{opacity:.55}}
.tl-side{display:flex; flex-direction:column; gap:10px; width:clamp(140px, 16vw, 200px)}
.tl-lc{display:grid; grid-template-columns:auto minmax(0,1fr); align-items:center; gap:6px 8px; padding:10px 12px; border-radius:16px; background:rgba(4,7,22,.45); border:1px solid color-mix(in srgb, var(--pc) 40%, transparent); transition:box-shadow .3s}
.tl-lc.on{box-shadow:0 0 0 2px var(--pc), 0 0 22px color-mix(in srgb, var(--pc) 45%, transparent)}
.tl-lc .avatar{width:28px; height:28px; font-size:13px; border-radius:9px}
.tl-lc b{white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.tl-pips{display:flex; gap:6px}
.tl-pips i{width:36px; height:12px; border-radius:6px; background:rgba(255,255,255,.14)}
.tl-pips i.lit{background:var(--pc); box-shadow:0 0 10px var(--pc)}
.tl-lc small{font-size:11px; font-weight:700; letter-spacing:.2em; color:var(--muted)}
.tl-rule{font-size:13px; font-weight:600; color:var(--muted); text-align:center}
.tl-rule b{color:#ffd766}
@media (orientation:portrait){ .tl-wrap{grid-template-columns:1fr; grid-template-rows:minmax(0,1fr) auto} .tl-side{flex-direction:row; width:auto; align-items:center} .tl-lc{flex:1} .tl-rule{display:none} }
.tl-pips, .tl-lc small{grid-column:1/-1}
.tl-lc b{text-align:left}
.mon-hud.n4 .mon-hp-top strong{font-size:22px}
.mon-hud.n4 .mon-hp{padding:8px 10px}

/* setup: pick-one options (01 start rule) and the records round note */
.ochoice{display:flex; flex-direction:column; gap:6px}
.rpill.small button{min-height:42px; font-family:var(--ui); font-weight:700; font-size:15px; letter-spacing:.02em}
.rec-note{margin-top:2px; text-align:center; font-size:13px; font-weight:600; color:#ffd27a}

/* ---------- connect 4 ---------- */
.c4-wrap{padding:12px; display:grid; grid-template-rows:minmax(0,1fr) auto; gap:8px; justify-items:center; min-height:0; background:radial-gradient(ellipse at 50% 30%, rgba(58,123,255,.22), transparent 70%), linear-gradient(180deg, rgba(36,56,120,.50), rgba(9,14,38,.82))}
.c4-svg{width:100%; height:100%; min-height:0; overflow:visible}
.c4-badge circle{fill:#0b1433; stroke:var(--cc, #fff); stroke-width:5; filter:drop-shadow(0 0 10px color-mix(in srgb, var(--cc, #fff) 70%, transparent)); transition:stroke .3s, opacity .3s}
.c4-badge text{font-family:var(--num); font-size:36px; fill:#fff}
.c4-badge.full{opacity:.3}
.c4-badge.full circle{stroke:#5a6a9a; filter:none}
.c4-frame{filter:drop-shadow(0 10px 24px rgba(0,0,0,.5))}
.c4-rims circle{fill:none; stroke:rgba(4,10,40,.45); stroke-width:5}
.c4-ring{fill:none; stroke:#fff; stroke-width:7; filter:drop-shadow(0 0 10px #fff); animation:c4Pulse 1s ease-in-out infinite alternate}
.c4-line{stroke:#fff; stroke-width:10; stroke-linecap:round; opacity:.85; stroke-dasharray:700; stroke-dashoffset:700; animation:draw .6s ease-out forwards}
@keyframes c4Pulse{to{stroke-width:3; opacity:.6}}
.c4-legend{display:flex; gap:18px; font-weight:700; font-size:15px}
.c4-legend span{display:inline-flex; align-items:center; gap:8px; padding:4px 14px 4px 6px; border-radius:999px; border:1px solid transparent; opacity:.7}
.c4-legend span.on{opacity:1; border-color:rgba(255,255,255,.35); background:rgba(255,255,255,.06)}
.c4-legend i{width:22px; height:22px; border-radius:50%; box-shadow:inset 0 -3px 0 rgba(0,0,0,.25)}

/* ---------- hunter: glass vials ---------- */
.hv-stage{position:relative; display:flex; flex-direction:column; align-items:stretch; gap:8px; padding:12px 16px 10px; overflow:hidden; min-height:0;
  background:#0a0333}
/* MIAMI ABOVE THE HORIZON, TRON BELOW IT (Cory, 2026-09-20: "they should look like miami vice vs tron").
   It replaced a wall of slowly turning gears. Everything moves on CSS alone and none of it sees a dart. */
.hv-scene{position:absolute; inset:0; width:100%; height:100%; pointer-events:none; overflow:hidden}
/* The floor runs the same way the 01 games' does: one journey, started at sixteen different points - each
   line a strip one unit tall that the journey moves down and thickens (js hunter.js hvScene). */
.hv-scene .hv-h{position:absolute; left:-30px; top:-.4px; width:820px; height:.8px; background:#39d5ff; opacity:0;
  animation:hvRun 4.2s cubic-bezier(.55,0,.85,.45) infinite; will-change:transform, opacity}
@keyframes hvRun{
  0%{transform:translateY(0) scaleY(1); opacity:0}
  9%{opacity:.7}
  100%{transform:translateY(300px) scaleY(5); opacity:.75}
}
/* Two cycles, crossing at their own speeds, each dragging its light behind it. */
.hv-scene .hv-trail{position:absolute; border-radius:2.5px; filter:drop-shadow(0 0 8px currentColor)}
.hv-scene .hv-trail.a{left:-460px; width:460px; height:5px; color:#39d5ff;
  background:linear-gradient(90deg, rgba(57,213,255,0) 0%, rgba(57,213,255,.9) 82%, #fff 100%); animation:hvRide 6.5s linear infinite}
.hv-scene .hv-trail.b{left:-380px; width:380px; height:7px; border-radius:3.5px; color:#ff2f86;
  background:linear-gradient(90deg, rgba(255,47,134,0) 0%, rgba(255,47,134,.9) 82%, #fff 100%); animation:hvRideB 9s linear infinite}
@keyframes hvRide{from{transform:translateX(0)} to{transform:translateX(1500px)}}
@keyframes hvRideB{from{transform:translateX(1420px)} to{transform:translateX(-400px)}}
/* The sun sinking through its own bands - a unit at a time, so its small drawing is redrawn a few times a
   second, not sixty - the water under it, the sky over it. */
.hv-scene .hv-slits{animation:hvSlits 8s steps(32) infinite}
@keyframes hvSlits{to{transform:translateY(-32px)}}
.hv-scene .hv-sunbox{animation:hvSun 6s ease-in-out infinite alternate}
@keyframes hvSun{to{filter:brightness(1.2)}}
/* The horizon breathes: the line fades up, and a lit copy with the glow fades in over it. */
.hv-scene .hv-hzglow, .hv-scene .hv-hz{position:absolute; left:-30px; width:820px}
.hv-scene .hv-hzglow{height:12px; background:#39d5ff; opacity:.22}
.hv-scene .hv-hz{height:2.4px; background:#eafcff; animation:hvHz 3.6s ease-in-out infinite alternate}
.hv-scene .hv-hz.lit{filter:drop-shadow(0 0 9px #39d5ff); animation-name:hvHzLit}
@keyframes hvHz{from{opacity:.5} to{opacity:1}}
@keyframes hvHzLit{from{opacity:0} to{opacity:1}}
.hv-scene .hv-dusk{position:absolute; left:0; top:0; width:760px; height:600px; background:#08021c; opacity:.34}
.hv-scene .hv-star{fill:#fff; animation:hvStar 5s ease-in-out infinite; animation-delay:calc(var(--d, 0s) * -1)}
@keyframes hvStar{0%,100%{opacity:.2} 50%{opacity:.85}}
/* The city is a silhouette; only its windows are awake, and not all of them at once. */
.hv-scene .hv-tower{fill:#170a34}
.hv-scene .hv-win{fill:#ffd9a8; animation:hvWin 9s steps(1) infinite; animation-delay:var(--d, 0s)}
@keyframes hvWin{0%,44%{opacity:.75} 45%,100%{opacity:.12}}
/* Palms lean in the wind off the water, each on its own beat, under one pink glow. */
.hv-scene .hv-palms{fill:#07011c; stroke:#ff6aa8; stroke-width:1.1; stroke-opacity:.55; paint-order:stroke; filter:drop-shadow(0 0 5px rgba(255,106,168,.4))}
.hv-scene .hv-palm{transform-origin:50% 100%; animation:hvSway 7s ease-in-out infinite alternate; animation-delay:var(--lag, 0s)}
@keyframes hvSway{from{transform:rotate(-1.6deg)} to{transform:rotate(1.6deg)}}
@media (prefers-reduced-motion:reduce){
  .hv-scene .hv-h, .hv-scene .hv-trail, .hv-scene .hv-slits, .hv-scene .hv-sunbox,
  .hv-scene .hv-hz, .hv-scene .hv-star, .hv-scene .hv-win, .hv-scene .hv-palm{animation:none}
  .hv-scene .hv-h{opacity:.55}
  .hv-scene .hv-hz.lit{opacity:0}
}
.hv-goal{position:relative; align-self:center; padding:3px 16px; border-radius:999px; font-weight:700; font-size:12px; letter-spacing:.3em; color:#e9cf98; background:rgba(10,8,4,.55); border:1px solid rgba(232,192,112,.4)}
.hv-goal b{font-family:var(--num); font-weight:400; font-size:16px; letter-spacing:.04em; color:#fff}
.hv-row{position:relative; flex:1; min-height:0; display:flex; justify-content:center; align-items:stretch; gap:clamp(10px, 3vw, 44px)}
.hv{flex:0 1 120px; min-width:0; min-height:0; display:flex; flex-direction:column; align-items:center; gap:6px}
/* FEWER PLAYERS, FATTER TUBES (Cory, 2026-09-20). Two of them have the whole stage to stand on, and a pair
   of drinking straws in the middle of it was not using any of it. */
.hv-row.n1 .hv{flex:0 1 300px} .hv-row.n2 .hv{flex:0 1 250px} .hv-row.n3 .hv{flex:0 1 195px} .hv-row.n4 .hv{flex:0 1 158px}
.hv-score{font-family:var(--num); font-size:clamp(20px, 5vh, 46px); line-height:1; color:#fff; text-shadow:0 0 18px color-mix(in srgb, var(--pc) 70%, transparent), 0 3px 0 rgba(0,0,0,.5)}
.hv-tube{position:relative; flex:1; min-height:60px; width:min(100%, 84px)}
.hv-row.n1 .hv-tube{width:min(100%, 230px)} .hv-row.n2 .hv-tube{width:min(100%, 186px)}
.hv-row.n3 .hv-tube{width:min(100%, 148px)} .hv-row.n4 .hv-tube{width:min(100%, 118px)}
.hv-tube::before{content:""; position:absolute; left:50%; top:-10px; width:10px; height:12px; margin-left:-5px; border-radius:2px; background:repeating-linear-gradient(180deg, #d8b878 0 2px, #6b5020 2px 4px)}
.hv-cap, .hv-base{position:absolute; left:-7px; right:-7px; height:20px; border-radius:6px; background:linear-gradient(90deg, #4a3510, #e8c878 32%, #fff0c0 42%, #9a7430 68%, #3c2a0c); box-shadow:0 3px 8px rgba(0,0,0,.5); z-index:2}
.hv-cap{top:0; display:grid; place-items:center}
.hv-cap span{font-family:var(--num); font-size:11px; color:#3a2600; opacity:.8}
.hv-base{bottom:0}
.hv-glass{position:absolute; top:16px; bottom:16px; left:0; right:0; overflow:hidden; border-radius:6px; border:2px solid rgba(220,235,255,.32);
  background:linear-gradient(90deg, rgba(255,255,255,.12), rgba(255,255,255,.02) 30%, rgba(255,255,255,.05) 70%, rgba(255,255,255,.14)); transition:border-color .3s, box-shadow .3s}
.hv-glass::after{content:""; position:absolute; top:8px; bottom:8px; left:16%; width:10%; border-radius:6px; background:linear-gradient(180deg, rgba(255,255,255,.34), rgba(255,255,255,.08)); pointer-events:none; z-index:1}
.hv-ticks{position:absolute; inset:0; background:repeating-linear-gradient(0deg, transparent 0 calc(10% - 2px), rgba(255,255,255,.18) calc(10% - 2px) 10%); -webkit-mask:linear-gradient(90deg, transparent 70%, #000 72%); mask:linear-gradient(90deg, transparent 70%, #000 72%); pointer-events:none}
.hv-liquid{position:absolute; left:0; right:0; bottom:0; height:0; transition:height .75s cubic-bezier(.3,1.25,.45,1);
  background:linear-gradient(90deg, color-mix(in srgb, var(--pc) 55%, #000), var(--pc) 38%, color-mix(in srgb, var(--pc) 55%, #fff) 52%, var(--pc) 66%, color-mix(in srgb, var(--pc) 55%, #000));
  box-shadow:0 0 26px color-mix(in srgb, var(--pc) 55%, transparent), inset 0 -20px 30px rgba(0,0,0,.25)}
.hv-liquid::before{content:""; position:absolute; left:0; right:0; top:-4px; height:8px; border-radius:50%; background:color-mix(in srgb, var(--pc) 45%, #fff); opacity:.85}
.hv-liquid i{position:absolute; bottom:4px; width:6px; height:6px; border-radius:50%; background:rgba(255,255,255,.45); animation:hvBubble 2.4s ease-in infinite}
.hv-liquid i:nth-child(1){left:30%} .hv-liquid i:nth-child(2){left:58%; animation-delay:.8s; width:4px; height:4px} .hv-liquid i:nth-child(3){left:44%; animation-delay:1.6s}
@keyframes hvBubble{0%{transform:translateY(0); opacity:0} 15%{opacity:.8} 100%{transform:translateY(-120px); opacity:0}}
.hv.drain .hv-liquid{transition:height 1.2s cubic-bezier(.6,0,.9,.6)}
.hv.full .hv-liquid{animation:hvFull .8s ease-in-out infinite alternate}
@keyframes hvFull{to{box-shadow:0 0 50px var(--pc), inset 0 -20px 30px rgba(0,0,0,.2)}}
.hv.on .hv-glass{border-color:color-mix(in srgb, var(--pc) 60%, #fff); box-shadow:0 0 24px color-mix(in srgb, var(--pc) 55%, transparent)}
/* Each plate wears its player's colour (Cory, 2026-09-18), so an empty vial still says whose it is. The thrower's
   is lit and glows; when the vial is too narrow for the name, the plate shows the initial. */
.hv{container-type:inline-size}
.hv-plate{display:flex; align-items:center; justify-content:center; gap:6px; max-width:calc(100% + 18px); padding:4px 10px 4px 4px; border-radius:999px; font-weight:800; font-size:14px; white-space:nowrap; overflow:hidden; color:#fff;
  background:linear-gradient(180deg, color-mix(in srgb, var(--pc) 45%, #0b1230), color-mix(in srgb, var(--pc) 25%, #060a1c)); border:1.5px solid color-mix(in srgb, var(--pc) 70%, transparent); box-shadow:0 3px 8px rgba(0,0,0,.45)}
.hv-plate b{flex:none; display:grid; place-items:center; width:1.6em; height:1.6em; border-radius:50%; background:var(--pc); color:#fff; font-size:.85em; text-shadow:0 1px 1px rgba(0,0,0,.45)}
.hv-plate span{min-width:0; overflow:hidden; text-overflow:ellipsis}
.hv.on .hv-plate{background:linear-gradient(180deg, color-mix(in srgb, var(--pc) 55%, #fff), var(--pc) 55%, color-mix(in srgb, var(--pc) 60%, #000)); border-color:color-mix(in srgb, var(--pc) 40%, #fff); box-shadow:0 0 18px color-mix(in srgb, var(--pc) 60%, transparent)}
.hv.on .hv-plate b{background:#fff; color:color-mix(in srgb, var(--pc) 70%, #000); text-shadow:none}
@container (max-width:58px){ .hv-row .hv .hv-plate{padding:3px} .hv-plate span{display:none} }
.hv-mark{position:absolute; left:50%; bottom:calc(16px + (100% - 32px) * var(--lvl, 0)); transform:translate(-50%, 50%); z-index:3; padding:2px 8px; border-radius:999px; font-weight:800; font-size:12px; white-space:nowrap; color:#08102a; background:#7ff0c0; box-shadow:0 0 10px rgba(127,240,192,.5); animation:hvMark 1.1s ease-in-out infinite alternate}
@keyframes hvMark{to{box-shadow:0 0 22px rgba(127,240,192,1)}}
.hv-mark[hidden]{display:none}
.hv-boom{position:absolute; left:50%; top:35%; z-index:4; font-family:var(--num); font-size:clamp(16px, 3vh, 26px); color:#fff; white-space:nowrap; opacity:0; pointer-events:none; text-shadow:0 0 12px #ff2d55, 0 2px 0 #000}
.hv-boom.go{animation:hvBoom 1.3s ease-out}
/* THE VIAL GOING (Cory, 2026-09-20). A flash inside the glass, a shockwave off it, the tube kicked, and
   nine shards thrown off it that tumble away and fade. The drain that follows is the old animation; this is
   only the moment it breaks. */
.hv.blown .hv-glass{animation:hvBlow .5s ease-out}
@keyframes hvBlow{
  0%{filter:brightness(4) saturate(0); transform:scale(1.14, .92)}
  22%{filter:brightness(2.2); transform:scale(.92, 1.08)}
  55%{transform:scale(1.05, .97)}
  100%{filter:none; transform:none}
}
.hv.blown .hv-tube{animation:hvKick .42s cubic-bezier(.3,1.5,.4,1)}
@keyframes hvKick{0%{transform:none} 25%{transform:translateY(7px) scale(1.06, .93)} 60%{transform:translateY(-4px) scale(.97,1.03)} 100%{transform:none}}
.hv.blown .hv-tube::after{content:""; position:absolute; left:50%; top:50%; width:16px; height:16px; margin:-8px 0 0 -8px;
  border-radius:50%; border:3px solid #fff; animation:hvShock .55s ease-out forwards; pointer-events:none; z-index:5}
@keyframes hvShock{from{transform:scale(.4); opacity:.95} to{transform:scale(9); opacity:0; border-width:1px}}
.hv-stage.quake{animation:monQuake .45s linear}
.hv-shards{position:absolute; inset:0; z-index:6; pointer-events:none}
.hv-shards i{position:absolute; left:50%; top:46%; width:14px; height:20px; margin:-10px 0 0 -7px;
  background:linear-gradient(150deg, rgba(255,255,255,.95), rgba(180,205,245,.6) 55%, rgba(120,150,200,.25));
  clip-path:polygon(50% 0, 100% 62%, 62% 100%, 8% 74%);
  animation:hvShard .9s cubic-bezier(.2,.7,.4,1) forwards}
@keyframes hvShard{
  0%{transform:rotate(0) translate(0,0) scale(var(--s,1)); opacity:1}
  100%{transform:rotate(var(--r,240deg)) translate(calc(cos(var(--a,0deg)) * var(--d,70px)), calc(sin(var(--a,0deg)) * var(--d,70px) + 60px)) scale(var(--s,1)); opacity:0}
}
@media (prefers-reduced-motion:reduce){
  .hv.blown .hv-glass, .hv.blown .hv-tube, .hv.blown .hv-tube::after, .hv-shards i{animation:none}
  .hv-shards{display:none}
}
@keyframes hvBoom{0%{opacity:0; transform:translate(-50%, 0) scale(.3) rotate(-10deg)} 20%{opacity:1; transform:translate(-50%, 0) scale(1.35) rotate(-6deg)} 70%{opacity:1; transform:translate(-50%, -20px) scale(1) rotate(-6deg)} 100%{opacity:0; transform:translate(-50%, -40px) scale(1)}}
.hv-row.dense .hv-plate{font-size:11px; padding:3px 7px 3px 3px}
.hv-row.dense .hv-score{font-size:clamp(16px, 3.6vh, 30px)}
.hv-stage .checkout{position:relative; align-self:center}

/* ---------- cricket: numbers down the middle, a capsule of marks per player, on a starfield ---------- */
.ck-stage{position:relative; padding:10px 16px 12px; overflow:hidden; min-height:0; display:flex; background:#04061a}
.ck-sky{position:absolute; inset:0; pointer-events:none;
  background:
    radial-gradient(1.5px 1.5px at 12% 18%, #fff, transparent), radial-gradient(1px 1px at 27% 64%, #cfe0ff, transparent), radial-gradient(1.5px 1.5px at 41% 32%, #fff, transparent),
    radial-gradient(1px 1px at 58% 12%, #fff, transparent), radial-gradient(2px 2px at 73% 48%, #dfe8ff, transparent), radial-gradient(1px 1px at 86% 22%, #fff, transparent),
    radial-gradient(1px 1px at 91% 78%, #fff, transparent), radial-gradient(1.5px 1.5px at 18% 86%, #fff, transparent), radial-gradient(1px 1px at 66% 90%, #cfe0ff, transparent),
    radial-gradient(1px 1px at 35% 8%, #fff, transparent), radial-gradient(1px 1px at 8% 50%, #fff, transparent), radial-gradient(1.5px 1.5px at 50% 72%, #fff, transparent),
    radial-gradient(ellipse 60% 45% at 70% 35%, rgba(90,60,200,.35), transparent 70%),
    radial-gradient(ellipse 50% 40% at 25% 70%, rgba(30,110,220,.28), transparent 70%),
    linear-gradient(180deg, #070a24, #03040f)}
.ck-planet{position:absolute; width:38%; aspect-ratio:1; left:-12%; bottom:-22%; border-radius:50%; pointer-events:none;
  background:radial-gradient(circle at 62% 30%, #7fb4ff 0%, #2f5bd0 22%, #16246e 48%, #070b2a 72%); box-shadow:0 0 60px rgba(80,140,255,.35); opacity:.7}
.ck-grid{position:relative; flex:1; min-height:0; display:grid; --caph:clamp(58px, 11vh, 92px);
  grid-template-rows:var(--caph) repeat(7, minmax(0, 1fr)); column-gap:clamp(6px, 1.4vw, 18px); max-width:720px; margin:0 auto; width:100%}
/* THE WHOLE COLUMN IS THE PLAYER'S COLOUR (Cory, 2026-09-17: "still difficult to tell whose columns are
   whose — can we colour in the entire column with a lighter shade"). A coloured outline around a dark
   column was nearly nothing to go on with eight of them side by side: the tint now runs the full height,
   strongest at the top where the name is, and the whole column is dimmed rather than de-coloured when it
   is not your turn, so the colour survives being stepped back. It is the same hex the player's card and
   avatar use — one colour per person, everywhere on screen. */
.ck-col{grid-row:1 / -1; justify-self:center; width:min(100%, 110px); container-type:inline-size; border-radius:22px; border:3px solid color-mix(in srgb, var(--pc) 75%, transparent);
  background:linear-gradient(180deg, color-mix(in srgb, var(--pc) 44%, rgba(6,10,28,.84)) 0%,
    color-mix(in srgb, var(--pc) 32%, rgba(6,10,28,.86)) 45%, color-mix(in srgb, var(--pc) 28%, rgba(6,10,28,.88)) 100%);
  opacity:.78; transition:box-shadow .3s, border-color .3s, opacity .3s, transform .3s}
/* The one being thrown right now: full colour and lifted, while the rest step back. Colour alone was not
   enough to find yourself among four columns, so the name is on the cap and only one cap is ever lit. */
.ck-col.on{border-color:var(--pc); opacity:1; filter:none; transform:translateY(-3px);
  box-shadow:0 0 26px color-mix(in srgb, var(--pc) 55%, transparent), inset 0 0 22px color-mix(in srgb, var(--pc) 22%, transparent)}
.ck-cap{position:relative; height:var(--caph); display:grid; grid-template-rows:auto auto; place-content:center; justify-items:center; gap:2px; padding:0 2px}
/* THE HEAD IS AS WIDE AS THE COLUMN (Cory, 2026-09-17: "bot heads on top are way too small, I couldn't see
   them — they should fill out the same diameter as the column itself"). It is sized off the COLUMN's own
   width (the column is a container), not the viewport, so it grows to whatever room that player's column
   has and is capped by the cap's height so it can never push the name out. The glyph inside is a share of
   the circle, so it grows with it — a robot face reads at this size, a 14px emoji did not. */
.ck-face{position:relative; --av:min(84cqw, calc(var(--caph) * .62)); width:var(--av); height:var(--av)}
.ck-init{width:100%; height:100%; border-radius:50%; display:grid; place-items:center;
  font:700 calc(var(--av) * .52)/1 var(--ui, inherit); color:#04061e; background:var(--pc); box-shadow:0 0 10px color-mix(in srgb, var(--pc) 60%, transparent)}
.ck-init.bot{font-size:calc(var(--av) * .72); text-shadow:none}
/* The name is sized off the COLUMN (cqw, the column is a container), not the screen: with four players on a
   phone a column is ~44px and a 12px name was cut to "Le..." (store review, 2026-10-16). At 21cqw a 44px
   column gets 9px, which fits six letters; wide columns still cap at 12. */
.ck-nm{max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:clamp(7px, 21cqw, 12px); font-weight:700;
  letter-spacing:0; color:rgba(255,255,255,.72); text-align:center}
.ck-col.on .ck-nm{color:#fff; text-shadow:0 0 10px color-mix(in srgb, var(--pc) 70%, transparent)}
/* The thrower's head glows harder and softer. It pulsed its box-shadow, which repaints the head every frame
   of every turn (2026-10-19, the backdrop lag); now the stronger glow is a ring of its own fading in and out. */
.ck-col.on .ck-face::after{content:""; position:absolute; inset:0; border-radius:50%; pointer-events:none; opacity:0;
  box-shadow:0 0 18px color-mix(in srgb, var(--pc) 90%, transparent); animation:ckTurn 1.8s ease-in-out infinite}
@keyframes ckTurn{50%{opacity:1}}
/* The padlock rides on the corner of the head like a badge, and is sized off the head — it used to sit in
   the corner of the cap at a fixed size, which was fine beside a 24px disc and invisible beside the big one
   (Cory, 2026-09-17). Closing all seven is the one thing this mark exists to say, so when it happens the
   badge fills gold with a dark lock in it, which reads across a room; until then it is a dark disc with a
   faint open shackle that nobody has to look at. */
.ck-lockwrap{position:absolute; right:calc(var(--av) * -.06); bottom:calc(var(--av) * -.06);
  width:calc(var(--av) * .46); height:calc(var(--av) * .46); display:grid; place-items:center; border-radius:50%;
  background:rgba(4,6,26,.9); color:rgba(255,255,255,.26); box-shadow:0 0 0 2px rgba(4,6,26,.9);
  transition:color .3s, background .3s, box-shadow .3s}
.ck-lock{width:74%; height:auto}
.ck-lock .shackle{transform:translateY(-3px); transition:transform .3s}
.ck-lockwrap.locked{color:#08102a; background:#ffd84a; box-shadow:0 0 0 2px rgba(4,6,26,.9), 0 0 14px rgba(255,216,74,.85)}
.ck-lockwrap.locked .shackle{transform:none}
.ck-num{display:grid; place-items:center; font-family:var(--num); font-size:clamp(22px, 6vh, 56px); line-height:1; color:#fff; font-style:italic; text-shadow:0 3px 0 rgba(0,0,0,.6), 0 0 18px rgba(140,180,255,.35); transition:opacity .3s, color .3s; position:relative}
.ck-num.bull{color:#ffd84a; font-size:clamp(20px, 5.2vh, 48px)}
.ck-num.score{color:#ffe690; text-shadow:0 3px 0 rgba(0,0,0,.6), 0 0 22px rgba(255,200,58,.9)}
.ck-num.dead{opacity:.28}
.ck-num.dead::after{content:""; position:absolute; left:18%; right:18%; top:50%; height:4px; border-radius:2px; background:#fff}
.ck-num.hit{animation:ckHit .5s ease-out}
@keyframes ckHit{30%{transform:scale(1.25)}}
.ck-cell{position:relative; display:grid; place-items:center; min-height:0; color:#fff; transition:opacity .3s}
.ck-cell.dead{opacity:.35}
/* A mark was sized off the ROW's height with its width left to follow, so in a tall narrow column — eight
   players on a phone — it came out wider than the column it belonged to and sat over its neighbours
   (Cory, 2026-09-17). Bounded on both sides now; the glyph letterboxes inside whatever box it is given, so
   it can never distort and can never overflow. */
/* SIZED OFF THE CELL, IN CONTAINER UNITS (Cory, 2026-09-28, iPhone: "the bottom X is hanging off the bottom
   of the tube... I think it's just misaligned"). `height:74%` never resolved against the 25px row a phone
   gives these cells - the glyph came out 38px in a 25px cell, every mark sat a row and a half low, the 18 read
   as the 17 and the Bull's mark hung under the capsule. The cell is a size container now and the glyph is a
   square of the smaller of its two sides, so it can only ever sit inside its own row. */
.ck-cell{container-type:size}
/* AS BIG AS THE COLUMN (Cory, 2026-09-28, of a 52px mark in a 110px column: "always make the X's and 0's
   fill out most of the column width. Do the same for Cut Throat" - same view, same rule). No pixel cap:
   the mark is most of the column's width, held down only by the row's height. */
.ck-g{width:min(90cqw, 96cqh); height:min(90cqw, 96cqh); overflow:visible; filter:drop-shadow(0 0 6px rgba(255,255,255,.35))}
.ck-cell .ck-g3{color:#fff; filter:drop-shadow(0 0 8px var(--pc))}
.ck-cell.pop .ck-g{animation:ckPop .45s cubic-bezier(.2,1.6,.4,1)}
@keyframes ckPop{from{transform:scale(1.8) rotate(-12deg); opacity:.3}}
/* Crowded board: the columns come in tight against the numbers, and the numbers take the room that frees
   up (Cory: "bring those columns right up into the numbers, and then we can widen the numbers out as far
   as we can"). There was a finger's width of nothing on both sides of every number. */
.ck-grid{container-type:inline-size}
.ck-grid.many{column-gap:clamp(2px, .8vw, 8px)}
/* Sized off the BOARD, capped so two digits always sit inside their own column: as big as the room allows
   on a tablet, as big as it can be on a phone, never over the marks either side. */
.ck-grid.many .ck-num{font-size:min(clamp(24px, 7vh, 56px), 12cqw)}
.ck-grid.many .ck-num.bull{font-size:min(clamp(20px, 6vh, 48px), 9.5cqw)}
.ck-grid.many .ck-col{border-radius:16px; border-width:2px}
/* Five or more and there is no width for a name, so the letter carries it — and the column being thrown
   is still the only bright one. */
.ck-grid.many .ck-nm{display:none}
/* With the name gone the cap is the head's alone, so it takes nearly the whole width of the column. */
.ck-grid.many{--caph:clamp(54px, 10vh, 84px)}
.ck-grid.many .ck-face{--av:min(86cqw, calc(var(--caph) * .82))}
.ck-rlist > div{display:flex; align-items:center; justify-content:space-between}
.ck-marks{display:inline-flex; gap:4px; align-items:center}
.ck-marks > *{width:18px; height:18px; display:inline-grid; place-items:center}
.ck-mini{color:#fff}
.ck-marks i.ck-none::before{content:""; width:8px; height:2px; border-radius:1px; background:rgba(255,255,255,.3)}
@media (orientation:portrait){
  .ck-num{font-size:clamp(16px, 2.3vh, 32px)} .ck-num.bull{font-size:clamp(15px, 2.1vh, 28px)}
  .ck-grid, .ck-grid.many{--caph:clamp(40px, 5.6vh, 56px)}
}
/* A phone on its side (Cory, 2026-09-28: "in the landscape one everything's kind of squished"): the board
   has ~230px to stand in. The cap and the stage's padding give back what they can to the seven rows, and
   the numbers are sized to the row so they never overlap the one above. */
@media (max-height:500px){
  .ck-stage{padding:6px 12px 8px}
  .ck-grid, .ck-grid.many{--caph:clamp(38px, 10vh, 48px)}
  .ck-num{font-size:min(clamp(20px, 6.2vh, 56px), 100cqh)}
  .ck-num.bull{font-size:min(clamp(18px, 5.4vh, 48px), 90cqh)}
  .ck-num{container-type:size}
}
.scene > canvas.globe{position:absolute; inset:0; width:100%; height:100%; touch-action:none; cursor:grab}
/* Fly closer or pull back, mid-game, without leaving the board. */
.globe-zoom{position:absolute; right:10px; bottom:10px; z-index:3; display:flex; flex-direction:column; gap:8px}
.globe-zoom .gz{width:40px; height:40px; border-radius:999px; display:grid; place-items:center; padding:0;
  font-family:var(--num); font-size:21px; line-height:1; color:#eaf0ff; cursor:pointer;
  background:rgba(6,10,28,.62); border:1px solid rgba(140,180,255,.34); box-shadow:0 6px 18px rgba(0,0,0,.4)}
@media (hover:hover){.globe-zoom .gz:hover{background:rgba(12,20,52,.82); border-color:rgba(160,200,255,.6)}}
.globe-zoom .gz:active{transform:scale(.94)}
body.notouch .globe-zoom{display:none}   /* nobody can press them on a display; the phone has the zoom */
/* NOW ARRIVED IN ... across the foot of the globe on every landing (views/race.js world.arrive). In the flyer's colour,
   sized off the panel so the TV and the phone both read it. */
.globe-arrive{--pc:#ffd84a; position:absolute; left:50%; bottom:6%; z-index:4; transform:translateX(-50%); pointer-events:none;
  display:flex; flex-direction:column; align-items:center; gap:.2em; padding:.45em 1.4em .55em; max-width:92%; border-radius:22px;
  font-size:clamp(13px, 2.9vh, 32px); text-align:center; white-space:nowrap;
  background:linear-gradient(180deg, rgba(6,14,38,.94), rgba(6,14,38,.86)); border:3px solid var(--pc);
  box-shadow:0 14px 50px rgba(0,0,0,.6), 0 0 40px color-mix(in srgb, var(--pc) 55%, transparent)}
.globe-arrive .ga-eye{font-weight:800; letter-spacing:.3em; font-size:.8em; color:var(--pc)}
.globe-arrive .ga-city{font-family:var(--num); font-weight:400; font-size:2.7em; line-height:1; letter-spacing:.04em; color:#fff;
  text-shadow:0 4px 0 rgba(0,0,0,.4), 0 0 30px color-mix(in srgb, var(--pc) 75%, transparent); overflow:hidden; text-overflow:ellipsis; max-width:100%}
.globe-arrive.go{animation:gaIn 2.8s cubic-bezier(.2,1.3,.35,1) both}
.globe-arrive.go .ga-city{animation:gaCity 2.8s cubic-bezier(.2,1.4,.35,1) both}
@keyframes gaIn{0%{opacity:0; transform:translateX(-50%) translateY(40px) scale(.7)} 14%{opacity:1; transform:translateX(-50%) scale(1.06)}
  22%,86%{opacity:1; transform:translateX(-50%) scale(1)} 100%{opacity:0; transform:translateX(-50%) translateY(16px) scale(.96)}}
@keyframes gaCity{0%,6%{transform:scale(.4)} 20%{transform:scale(1.18)} 30%,100%{transform:scale(1)}}
.globe-arrive[hidden]{display:none}
@media (max-width:700px), (max-height:500px){
  .globe-arrive{font-size:11px; bottom:5%; padding:.4em 1.1em .5em; border-width:2px; border-radius:14px}
  .globe-arrive .ga-eye{letter-spacing:.18em}
  .globe-arrive .ga-city{font-size:2.3em}
}

/* ---------- last one standing ---------- */
/* EVERY SPARE PIXEL GOES TO THE PLAYERS (Cory, 2026-09-20: "why are the characters so tiny. We need these
   to fill out... fill out the entire box with characters"). The banner is one slim line, the rule that used
   to sit under it is gone, and the score pills below the panel are hidden for this game alone — every
   character already carries its own total on its plate, so the row was the same numbers twice. */
#scr-game[data-view="lastone"] .pcards{display:none}
.lo-stage{position:relative; overflow:hidden; min-height:0; display:flex; flex-direction:column; gap:6px; padding:8px 10px 6px;
  background:radial-gradient(ellipse 70% 55% at 50% -5%, rgba(255,230,160,.22), transparent 70%), linear-gradient(180deg, #14101e, #07060c)}
.lo-light{position:absolute; left:50%; top:-10%; width:70%; height:120%; transform:translateX(-50%); pointer-events:none;
  background:conic-gradient(from 160deg at 50% 0%, transparent 0deg, rgba(255,236,180,.10) 20deg, rgba(255,236,180,.16) 40deg, rgba(255,236,180,.10) 60deg, transparent 80deg); animation:loSway 7s ease-in-out infinite alternate}
@keyframes loSway{from{transform:translateX(-54%) rotate(-3deg)} to{transform:translateX(-46%) rotate(3deg)}}
.lo-banner{position:relative; display:flex; align-items:center; gap:10px; justify-content:center; text-align:left; flex:none}
.lo-banner b{display:block; font-family:var(--num); font-weight:400; font-size:clamp(17px, 2.8vh, 26px); letter-spacing:.04em; color:#fff; line-height:1.1}
.lo-bull{position:relative; width:clamp(26px, 3.6vh, 38px); aspect-ratio:1; flex:none}
/* On a phone the banner was sized by height and wrapped to two lines, and it ran into the player tiles
   (Cory, 2026-09-18: "tons of overlap on last one standing"). One line, smaller, and the tiles keep their room. */
@media (max-width:600px), (max-height:560px){
  .lo-stage{gap:5px; padding:7px 9px 5px}
  .lo-banner{gap:8px}
  .lo-banner b{font-size:17px; line-height:1.1}
  .lo-bull{width:24px}
}
.lo-bull i{position:absolute; inset:0; border-radius:50%; background:#0f4596; box-shadow:0 0 18px rgba(255,216,74,.4)}
.lo-bull i:nth-child(2){inset:22%; background:#2ee6a0} .lo-bull i:nth-child(3){inset:40%; background:#ff2d55; box-shadow:0 0 12px #ff2d55}
/* THE TRAPDOOR STAGE (Cory, 2026-09-19). Every state is a class on the player's group: cur is spotlit and
   looking down, safe wears the lock, risk goes orange, out drops through the floor, won rises on the podium.
   The SVG scales to the panel, so the same stage works on a phone and across a tablet. */
.lo-svg{flex:1; min-height:0; width:100%}
.lo-p{--pc:#ffc83a}
.lo-p .lo-beam{opacity:0; transition:opacity .35s}
.lo-p.cur .lo-beam{opacity:1}
.lo-name{font-family:var(--ui); font-weight:800; font-size:22px; fill:#dfe9ff; letter-spacing:.1em; text-anchor:middle}
.lo-score{font-family:var(--num); font-size:62px; fill:#fff; text-anchor:middle}
.lo-tag{font-family:var(--num); font-size:30px; letter-spacing:.06em; fill:#8e9bc4; text-anchor:middle}
.lo-p.cur .lo-tag{fill:var(--pc)} .lo-p.safe .lo-tag{fill:#2ee6a0} .lo-p.risk .lo-tag{fill:#ff8a3d} .lo-p.out .lo-tag{fill:#ff6a86}
.lo-p.won .lo-tag{fill:#ffd84a}
/* The lock: it is not there until a bull, and then it slams. */
.lo-lock{opacity:0; transform-box:fill-box; transform-origin:50% 50%}
.lo-p.safe .lo-lock{opacity:1}
.lo-p.locking .lo-lock{animation:loLock .42s cubic-bezier(.3,1.7,.5,1)}
@keyframes loLock{0%{opacity:0; transform:translateY(-120px) scale(1.5)} 60%{opacity:1; transform:translateY(0) scale(.92)} 100%{opacity:1; transform:none}}
.lo-p.locking .lo-frame{animation:loThud .3s ease-out}
@keyframes loThud{40%{transform:translateY(5px)}}
/* Whose floor is still open: the frame glows orange the moment somebody else is safe. */
.lo-p.risk .lo-leaf rect:first-child{fill:#5a3a1e; stroke:#ff8a3d}
.lo-p.safe .lo-leaf rect:first-child{stroke:#2ee6a0}
.lo-p.cur .lo-leaf rect:first-child{stroke:var(--pc)}
/* The doors, hinged at the outside edges. */
.lo-leaf{transform-box:fill-box; transition:transform .42s cubic-bezier(.4,0,.2,1)}
.lo-leaf.l{transform-origin:0% 50%} .lo-leaf.r{transform-origin:100% 50%}
.lo-p.dropping .lo-leaf.l{transform:rotate(84deg)} .lo-p.dropping .lo-leaf.r{transform:rotate(-84deg)}
/* Hanging, then gone. Three ways down, by seat (js/views/lastone.js LO_FALLS). */
.lo-char{transform-box:fill-box; transform-origin:50% 100%; transition:transform .3s}
.lo-p.dropping .lo-char{animation:loHang .42s ease-out}
@keyframes loHang{40%{transform:translateY(-14px)} 100%{transform:translateY(4px)}}
/* STRAIGHT DOWN, SPINNING (Cory, 2026-09-20). They drop through the hole they were standing on — no sideways
   drift, because a trapdoor does not throw you sideways — and tumble out of control on the way. Each seat
   spins its own way and its own speed so a row of them going down never looks copied. */
.lo-p.falling .lo-char{transform-origin:50% 45%; animation:loFall .8s cubic-bezier(.45,0,.95,.55) forwards}
.lo-p.cling.falling .lo-char{animation-name:loFallCling}
.lo-p.board.falling .lo-char{animation-name:loFallBoard}
.lo-p.wave.falling .lo-char{animation-name:loFallWave}
@keyframes loFall{30%{transform:translateY(120px) rotate(150deg)} 100%{transform:translateY(900px) rotate(760deg); opacity:0}}
@keyframes loFallCling{25%{transform:translateY(90px) rotate(-130deg)} 100%{transform:translateY(900px) rotate(-680deg); opacity:0}}
@keyframes loFallBoard{20%{transform:translateY(60px) rotate(60deg)} 100%{transform:translateY(920px) rotate(1080deg); opacity:0}}
@keyframes loFallWave{25%{transform:translateY(100px) rotate(200deg)} 100%{transform:translateY(900px) rotate(900deg); opacity:0}}
/* The yell that goes with it, fading away down the hole. */
.lo-yell{font-family:var(--num); font-size:26px; fill:#fff; text-anchor:middle; paint-order:stroke; stroke:#141032; stroke-width:4px; opacity:0}
.lo-p.falling .lo-yell{animation:loYell .8s ease-out forwards}
@keyframes loYell{0%{opacity:0; transform:translateY(0) scale(.7)} 18%{opacity:1; transform:translateY(-16px) scale(1.1)} 100%{opacity:0; transform:translateY(120px) scale(.6)}}
.lo-p.dropped .lo-char{opacity:0}
.lo-p.out .lo-leaf.l{transform:rotate(84deg)} .lo-p.out .lo-leaf.r{transform:rotate(-84deg)}
/* Gone only once the fall has finished: until then the animation owns the character. */
.lo-p.out.dropped .lo-char{opacity:0}
.lo-p.out .lo-plate{opacity:.5}
/* The puff they leave behind. */
.lo-smoke circle{transform-box:fill-box; transform-origin:50% 50%}
.lo-p.falling .lo-smoke circle{animation:loPuff .85s ease-out var(--d) both}
@keyframes loPuff{0%{opacity:0; transform:scale(.3)} 25%{opacity:.75} 100%{opacity:0; transform:scale(1.5) translateY(-16px)}}
/* Faces and arms: nervous on your turn, worried when somebody else is safe, arms up when you are locked in.
   Every one of these is a visibility swap between drawn poses — no CSS `d:path()`, which Safari ignores, so
   an iPhone sees the same animation as the tablet (Cory, 2026-09-20). */
.lo-char .arms.up, .lo-char .mouth.worried, .lo-char .mouth.cheer{opacity:0}
.lo-p.risk .lo-char .mouth.calm{opacity:0} .lo-p.risk .lo-char .mouth.worried{opacity:1}
.lo-p.safe .lo-char .arms.down{opacity:0} .lo-p.safe .lo-char .arms.up{opacity:1}
.lo-p.safe .lo-char .mouth.calm{opacity:0} .lo-p.safe .lo-char .mouth.cheer{opacity:1}
.lo-p.won .lo-char .arms.down{opacity:0} .lo-p.won .lo-char .arms.up{opacity:1}
.lo-p.won .lo-char .mouth.calm{opacity:0} .lo-p.won .lo-char .mouth.cheer{opacity:1}
.lo-p.cur .lo-char .pupil{transform:translateY(5px)}
.lo-p.risk .lo-char .brow.lb{transform:translateY(-4px) rotate(-12deg)}
.lo-p.risk .lo-char .brow.rb{transform:translateY(-4px) rotate(12deg)}
.lo-p.risk .lo-char{animation:loShiver 1.1s ease-in-out infinite}
@keyframes loShiver{50%{transform:translateX(3px)}}
.lo-p.safe .lo-char{animation:loCheer .5s ease-in-out infinite alternate}
@keyframes loCheer{to{transform:translateY(-10px)}}
/* The winner hops, arms up, for as long as the screen is theirs (Cory, 2026-09-20). */
.lo-p.won .lo-char{animation:loHop .46s cubic-bezier(.3,.9,.4,1) infinite}
@keyframes loHop{0%{transform:translateY(0) scaleY(1)} 18%{transform:translateY(2px) scaleY(.92)} 55%{transform:translateY(-34px) scaleY(1.06)} 100%{transform:translateY(0) scaleY(1)}}
.lo-char .brow{transform-box:fill-box; transform-origin:50% 50%}
/* The winner rises, and only the winner wears the crown. */
.lo-crown{opacity:0; transform-box:fill-box; transform-origin:50% 100%}
.lo-p.won .lo-crown{opacity:1; animation:loCrown .6s cubic-bezier(.2,1.6,.4,1)}
@keyframes loCrown{from{opacity:0; transform:translateY(-40px) scale(.4)}}
.lo-podium{fill:#2a2450; opacity:0; transition:opacity .4s}
.lo-rise{transition:transform .7s cubic-bezier(.2,1.2,.4,1)}
.lo-p.won .lo-rise{transform:translateY(-58px)}
.lo-p.won .lo-podium{opacity:1}
.lo-p.won .lo-beam{opacity:1}
/* Points scored on the way to a bull, under the door where they cover nothing. */
/* The darts scored on the way to a bull, written on the trapdoor leaves under the player. */
.lo-dart{font-family:var(--num); font-size:22px; fill:#ffd766; text-anchor:middle; paint-order:stroke; stroke:#141032; stroke-width:3px}
.lo-pop{font-family:var(--num); font-size:34px; fill:#ffd766; text-anchor:middle; opacity:0}
.lo-pop.big{font-size:52px; fill:#ff8a3d}
.lo-pop.go{animation:loPop .9s cubic-bezier(.2,1.3,.4,1)}
@keyframes loPop{0%{opacity:0; transform:translateY(-14px) scale(.6)} 25%{opacity:1; transform:translateY(0) scale(1.1)} 70%{opacity:1} 100%{opacity:0; transform:translateY(26px)}}
@media (prefers-reduced-motion:reduce){ .lo-p *, .lo-pop{animation:none !important} }
/* the round summary */
/* Over everything, board included, and sized for a room (Cory, 2026-09-28). */
.lo-sum{position:fixed; inset:0; z-index:60; display:grid; place-items:center; padding:18px; background:rgba(4,3,10,.74); cursor:pointer}
.lo-sum[hidden]{display:none}
.lo-sum.go .lo-sum-card{animation:sheetIn .35s cubic-bezier(.2,1.3,.4,1)}
.lo-sum-card{width:min(1100px, 94vw); max-height:100%; overflow:auto; padding:clamp(18px, 3vh, 32px) clamp(20px, 3vw, 40px); border-radius:28px; text-align:center;
  background:linear-gradient(180deg, #221a36, #0c0916); border:1px solid rgba(255,230,160,.3); box-shadow:0 30px 90px rgba(0,0,0,.7)}
.lo-sum-card h3{margin:6px 0 16px; font-family:var(--num); font-weight:400; font-size:clamp(30px, 7vh, 68px); line-height:1; color:#fff}
.lo-sum-card .eyebrow{color:#ffe7a3; font-size:clamp(12px, 1.9vh, 18px)}
.lo-sum-cols{display:grid; grid-template-columns:1fr 1fr; gap:clamp(14px, 3vw, 36px); text-align:left}
.lo-sum-h{font-weight:800; font-size:clamp(12px, 1.8vh, 16px); letter-spacing:.2em; margin-bottom:8px}
.lo-sum-h.in{color:#2ee6a0} .lo-sum-h.out{color:#ff8fa3}
.lo-sum-row{display:flex; align-items:center; gap:12px; padding:8px 16px 8px 8px; margin-bottom:8px; border-radius:999px; background:rgba(255,255,255,.07); font-size:clamp(16px, 2.7vh, 26px)}
.lo-sum-row .avatar{width:1.7em; height:1.7em; font-size:.9em}
.lo-sum-row b{flex:1; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.lo-sum-row span:last-child{font-weight:700; font-size:.8em; color:#c8cde8; white-space:nowrap}
.lo-sum-skip{font-size:clamp(11px, 1.7vh, 15px)}
@media (max-width:640px){ .lo-sum-cols{grid-template-columns:1fr} }
.lo-sum-row .avatar{width:26px; height:26px; font-size:12px; border-radius:50%}
#loSumOut .lo-sum-row{opacity:.6}
#loSumOut .lo-sum-row.fresh{opacity:1; background:rgba(255,45,85,.16)}
#loSumOut .lo-sum-row.fresh span:last-child{color:#ff8fa3}
.lo-sum-none{margin:4px 0; color:#8f8aa8; font-size:14px}
.lo-sum-skip{margin-top:10px; font-size:12px; font-weight:700; letter-spacing:.2em; text-transform:uppercase; color:#fff; opacity:.45}

/* ---------- time bomb ---------- */
.tb-stage{position:relative; overflow:hidden; min-height:0; display:flex; flex-direction:column; padding:0; background:#08070c}
/* The drawing is rebuilt whenever the thrower changes (a bomb each), so it lives in its own box. It keeps
   most of the panel whatever happens to the strip underneath it: a wrapped line of rules must never leave
   the bomb a sliver at the top (Cory, 2026-09-20). */
.tb-wrap{flex:1 1 auto; min-height:62%; display:flex}
.tb-svg{width:100%; flex:1; min-height:0}
/* EVERY WORD ON THE BOMB IS BIGGER THAN IT LOOKS HERE (Cory, 2026-09-20: "I couldn't read the words like
   TNT etc. it needs to be a lot larger"). The drawing is 896 units across and a phone shows it about 350px
   wide, so a size written here lands on the glass at about four tenths of it: 13px was five. Everything a
   thrower has to read off the bomb is sized for that, not for the number looking sensible in the file. */
.tb-owner{font-family:var(--ui); font-weight:800; font-size:30px; letter-spacing:.16em; fill:#fff; opacity:.5; text-anchor:middle}
.tb-tnt{font-family:var(--num); font-size:36px; fill:#fff; opacity:.9; text-anchor:middle; dominant-baseline:central; letter-spacing:.14em}
.tb-led, .tb-led-ghost{font-family:'Courier New', monospace; font-weight:700; font-size:72px; text-anchor:middle; dominant-baseline:central; letter-spacing:6px}
.tb-led-ghost{fill:#3a0a0c; opacity:.55}
.tb-led{fill:#ff2a2a; filter:drop-shadow(0 0 6px #ff2a2a) drop-shadow(0 0 16px rgba(255,40,40,.6))}
/* On the glass with the number, lit the same way (Cory, 2026-09-21). */
.tb-led-lbl{font-family:var(--num); font-weight:400; font-size:21px; letter-spacing:.1em; text-anchor:middle;
  fill:#ff2a2a; filter:drop-shadow(0 0 5px rgba(255,40,40,.8))}
.tb-light{fill:#ff2a2a; animation:tbBlink 1.2s steps(2) infinite}
@keyframes tbBlink{50%{fill:#3a0a0c}}
/* WHITE UNTIL IT IS KNOWN, then the colour IS the answer (Cory, 2026-09-21). */
.tb-wire{fill:none; stroke:#f4f4f4; stroke-width:9; stroke-linecap:round; filter:drop-shadow(0 3px 2px rgba(0,0,0,.5));
  transition:stroke .45s ease, stroke-dasharray .2s}
.tb-wire.live{stroke:#2ee66a} .tb-wire.dud{stroke:#ff3b3b} .tb-wire.done{stroke:#4d5b72}
.tb-wire.cut{stroke-dasharray:44 12 100; opacity:.85}
.tb-tag rect{fill:#161821; stroke-width:4}
.tb-tag-n{font-family:var(--num); font-size:52px; fill:#fff; text-anchor:middle; dominant-baseline:central}
/* WHAT THE WIRE IS, BIG ENOUGH TO SEE (Cory, 2026-09-22: "the tnt is waaaay too small. We need to be able
   to see that"). It was 25px against a 52px number, which at the size the panel shrinks to on a phone is a
   footnote — and it is the single most important word on the screen. */
.tb-tag-s{font-family:var(--ui); font-weight:900; font-size:36px; letter-spacing:0; fill:#a8b0c6; text-anchor:middle; dominant-baseline:central}
/* A TAG SAYS THE SAME THING ITS WIRE DOES (Cory, 2026-09-21: "the wires that light up TIMER should also
   turn green instead of red/pink"). Green is the one to cut, red is the one that kills you, and a wire
   already cut goes quiet in slate — it is finished, and it should not be competing with the live ones. */
.tb-tag rect{stroke:#39405a}
.tb-tag.live rect{fill:#0d3a1e; stroke:#2ee66a} .tb-tag.live .tb-tag-n{fill:#d8ffe8} .tb-tag.live .tb-tag-s{fill:#2ee66a}
.tb-tag.dud rect{fill:#3a0d12; stroke:#ff3b3b} .tb-tag.dud .tb-tag-n{fill:#ffd6d6} .tb-tag.dud .tb-tag-s{fill:#ff5f5f}
.tb-tag.done rect{fill:#161b28; stroke:#4d5b72} .tb-tag.done .tb-tag-n{fill:#7d89a3} .tb-tag.done .tb-tag-s{fill:#8fa0bd}
.tb-tag.scanned{animation:tbScan 1.5s ease-out}
@keyframes tbScan{20%{transform-box:fill-box; filter:drop-shadow(0 0 22px #39d5ff) brightness(1.8)} 60%{filter:drop-shadow(0 0 14px #39d5ff) brightness(1.4)}}
.tb-hud-lbl{font-family:var(--ui); font-weight:700; font-size:16px; letter-spacing:.12em; fill:#c8cde8; text-anchor:middle}
.tb-hud-n{font-family:var(--num); font-size:34px; fill:#fff; text-anchor:middle}
/* Its own strip under the picture, never over it: laid over the bottom it covered the wire numbers, which
   are the thing a thrower is aiming by (Cory, 2026-09-18). The drawing shrinks to fit above it. */
/* The rules, in pieces rather than paragraphs: a rule between each move, and the whole thing left to flow
   so it always fits the width it is given. */
/* Readable across a room (Cory, 2026-09-28: "plenty of dark space there to make the wording bigger and
   actually readable"). The panel had the height to spare; the line was sized for a phone and left there. */
.tb-help{position:relative; flex:none; padding:4px 10px calc(4px + env(safe-area-inset-bottom)); text-align:center; line-height:1.3;
  font-weight:600; font-size:14.5px; line-height:1.4; color:#aab4d8; background:rgba(8,7,12,.92); border-top:1px solid rgba(255,255,255,.06)}
.tb-help b{color:#e6ecff; font-weight:800}
/* The separator is a rule, not a bullet: it cuts the moves apart without adding a character to read. */
.tb-help i{display:inline-block; width:1px; height:.95em; margin:0 9px -.1em; background:rgba(255,255,255,.22)}
.tb-help em{font-style:normal; color:#ffb9a8}
.tb-help em b{color:#ff8a6a}
/* Two lines, whatever the panel is: the bomb is the thing being looked at and the strip underneath is a
   reminder, so it gives its room back rather than pushing the drawing into a letterbox. */
.tb-help{font-size:11.5px}
.tb-help i{margin:0 7px -.1em}
@media (max-width:820px){ .tb-help{font-size:11px} .tb-help i{margin:0 6px -.1em} }
/* instability: the lower the clock, the worse it gets */
.tb-bundle{transform-box:fill-box; transform-origin:50% 90%}
.tb-stage.uneasy .tb-bundle{animation:tbWobble 1.6s ease-in-out infinite}
.tb-stage.danger .tb-bundle{animation:tbWobble .55s ease-in-out infinite}
.tb-stage.danger .tb-light{animation-duration:.45s}
.tb-stage.critical .tb-bundle{animation:tbShake .12s linear infinite}
.tb-stage.critical .tb-light{animation-duration:.2s}
.tb-stage.critical{box-shadow:inset 0 0 90px rgba(255,30,30,.45); animation:tbAlarm .8s ease-in-out infinite}
/* HALF THE CLOCK (js/views/timebomb.js, last.kind === 'tnt'). Cutting a TNT wire no longer ends the game, so
   it does not get the fireball — it gets the light of one, and the whole panel takes the hit. */
.tb-stage.jolt{animation:tbJolt .5s cubic-bezier(.2,.9,.3,1)}
@keyframes tbJolt{
  0%{transform:translate(0,0); filter:brightness(2.6) saturate(1.6)}
  8%{transform:translate(-9px,5px)} 18%{transform:translate(8px,-4px)} 28%{transform:translate(-6px,-3px)}
  40%{transform:translate(4px,3px); filter:brightness(1.35) saturate(1.2)}
  60%{transform:translate(-3px,1px)} 100%{transform:translate(0,0); filter:none}}
@media (prefers-reduced-motion:reduce){ .tb-stage.jolt{animation:none} }
@keyframes tbWobble{25%{transform:rotate(-1.2deg)} 75%{transform:rotate(1.2deg)}}
@keyframes tbShake{25%{transform:translate(-3px,1px) rotate(-.8deg)} 50%{transform:translate(2px,-2px)} 75%{transform:translate(3px,1px) rotate(.8deg)}}
@keyframes tbAlarm{50%{box-shadow:inset 0 0 140px rgba(255,30,30,.7)}}
.tb-stage.defused .tb-led{fill:#2ee66a; filter:drop-shadow(0 0 10px #2ee66a)}
.tb-stage.defused .tb-light{fill:#2ee66a; animation:none}
.tb-stage.defused .tb-bundle{animation:none; filter:saturate(.6)}
.tb-flash{position:absolute; inset:0; background:#fff; opacity:0; pointer-events:none}
.tb-flash.go{animation:tbFlash 1.1s ease-out}
@keyframes tbFlash{0%{opacity:.95} 12%{opacity:.7} 100%{opacity:0}}
/* THE EXPLOSION (js/views/timebomb.js tbBoomSVG). Hard-edged lumps in four bands, a shockwave along the
   ground, casing thrown out of it, and a cloud that climbs on its own stem — drawn, not blurred. Nothing
   here runs until .go is on the svg, and it takes itself back down again afterwards. */
.tb-boom{position:absolute; inset:0; width:100%; height:100%; opacity:0; pointer-events:none}
.tb-boom.go{opacity:1}
.tb-boom g > *{opacity:0}
.tbx-glow{transform-box:fill-box; transform-origin:50% 50%}
.tb-boom.go .tbx-glow{animation:tbxGlow 2.6s ease-out forwards}
@keyframes tbxGlow{0%{opacity:0; transform:scale(.2)} 8%{opacity:1; transform:scale(1)} 100%{opacity:0; transform:scale(1.5)}}
/* the fire: four bands, each lump on its own beat so the edge crawls */
.tbx-rim circle, .tbx-body circle, .tbx-heart circle, .tbx-core circle{transform-box:fill-box; transform-origin:50% 50%}
.tbx-rim circle{fill:#a8210c} .tbx-body circle{fill:#ff5f00} .tbx-heart circle{fill:#ffb020} .tbx-core circle{fill:#fff4cd}
.tb-boom.go .tbx-rim circle{animation:tbxFire 2.1s cubic-bezier(.12,.8,.3,1) both; animation-delay:var(--d, 0s)}
.tb-boom.go .tbx-body circle{animation:tbxFire 1.9s cubic-bezier(.12,.8,.3,1) both; animation-delay:calc(var(--d, 0s) + .04s)}
.tb-boom.go .tbx-heart circle{animation:tbxFire 1.6s cubic-bezier(.12,.8,.3,1) both; animation-delay:calc(var(--d, 0s) + .06s)}
.tb-boom.go .tbx-core circle{animation:tbxCore 1.15s cubic-bezier(.1,.9,.3,1) both; animation-delay:var(--d, 0s)}
@keyframes tbxFire{0%{opacity:0; transform:scale(.12)} 10%{opacity:1} 34%{transform:scale(1.06) translateY(-14px)}
  70%{opacity:.95; transform:scale(1) translateY(-40px)} 100%{opacity:0; transform:scale(.86) translateY(-96px)}}
@keyframes tbxCore{0%{opacity:0; transform:scale(.1)} 8%{opacity:1; transform:scale(1.15)} 60%{opacity:.9; transform:scale(.9)} 100%{opacity:0; transform:scale(.5) translateY(-40px)}}
/* the ground shock */
.tbx-shock ellipse{fill:none; stroke:#ffd9a0; stroke-width:7; transform-box:fill-box; transform-origin:50% 50%}
.tbx-shock ellipse.b{stroke:#fff; stroke-width:4}
.tb-boom.go .tbx-shock ellipse{animation:tbxShock 1.1s cubic-bezier(.1,.8,.3,1) both}
.tb-boom.go .tbx-shock ellipse.b{animation-delay:.12s}
@keyframes tbxShock{0%{opacity:0; transform:scale(.2)} 12%{opacity:.9} 100%{opacity:0; transform:scale(4.2)}}
/* casing, thrown and tumbling */
.tbx-chunk{fill:#1d1a19; stroke:#000; stroke-width:2; transform-box:fill-box; transform-origin:50% 50%}
.tb-boom.go .tbx-chunk{animation:tbxChunk 1.9s cubic-bezier(.15,.7,.5,1) both; animation-delay:var(--d, 0s)}
@keyframes tbxChunk{0%{opacity:0; transform:translate(0,0) rotate(0) scale(.6)} 8%{opacity:1}
  100%{opacity:0; transform:translate(var(--dx), calc(var(--dy) * -1 + 260px)) rotate(var(--rot)) scale(1)}}
/* the cloud: a stem that grows and a cap that rolls up off it */
.tbx-stem{transform-box:fill-box; transform-origin:50% 100%}
.tb-boom.go .tbx-stem{animation:tbxStem 3.2s cubic-bezier(.2,.75,.3,1) both; animation-delay:.34s}
/* The cloud ends by clearing: a stem that stopped at three quarters opacity stood on the stage as a grey
   slab for the rest of the game (Cory, 2026-09-28: "a tombstone or something, remove it"). */
@keyframes tbxStem{0%{opacity:0; transform:scaleY(.05)} 20%{opacity:.9} 60%{opacity:.7; transform:scaleY(1)} 100%{opacity:0; transform:scaleY(1.18)}}
.tbx-cap circle{fill:#5f564f; transform-box:fill-box; transform-origin:50% 50%}
.tbx-cap.hot circle{fill:#ff7a1f; mix-blend-mode:screen}
.tb-boom.go .tbx-cap circle{animation:tbxCap 3.2s cubic-bezier(.2,.7,.3,1) both; animation-delay:calc(.42s + var(--d, 0s))}
.tb-boom.go .tbx-cap.hot circle{animation:tbxCapHot 2.2s ease-out both; animation-delay:calc(.42s + var(--d, 0s))}
@keyframes tbxCap{0%{opacity:0; transform:translateY(110px) scale(.2)} 18%{opacity:1}
  60%{opacity:.9; transform:translateY(-10px) scale(1)} 100%{opacity:0; transform:translateY(-90px) scale(1.24)}}
@keyframes tbxCapHot{0%{opacity:0; transform:translateY(110px) scale(.2)} 20%{opacity:.9} 100%{opacity:0; transform:translateY(-20px) scale(1)}}
/* embers, going up long after the fire has gone */
.tbx-ember{fill:#ffce6a; transform-box:fill-box; transform-origin:50% 50%}
.tb-boom.go .tbx-ember{animation:tbxEmber 2.4s ease-out both; animation-delay:var(--d, 0s)}
@keyframes tbxEmber{0%{opacity:0; transform:translate(0,0) scale(.4)} 10%{opacity:1; transform:scale(1)}
  100%{opacity:0; transform:translate(var(--dx), var(--dy)) scale(.3)}}
@media (prefers-reduced-motion:reduce){ .tb-boom.go *{animation-duration:.01s !important} }
/* Defused: everything lets go — the shaking stops, the bomb sags, the lights go green. */
.tb-stage.fizzle .tb-bundle{animation:tbSag 1.6s cubic-bezier(.3,1.4,.4,1) forwards; filter:saturate(.55)}
.tb-stage.fizzle .tb-wire{opacity:.55; transition:opacity .8s}
.tb-stage.fizzle .tb-led, .tb-stage.fizzle .tb-light{fill:#2ee66a; filter:drop-shadow(0 0 12px #2ee66a); animation:none}
@keyframes tbSag{60%{transform:translateY(10px) rotate(1.6deg)} 100%{transform:translateY(14px) rotate(1.2deg)}}

/* Sound sheet: which announcer, and how it is delivered. */
.snd-pick{display:flex; flex-direction:column; gap:8px}
.snd-voices{display:flex; flex-wrap:wrap; gap:8px}
.snd-v{display:inline-flex; flex-direction:column; align-items:flex-start; gap:1px; padding:8px 14px; border-radius:14px; font-weight:700; font-size:14.5px;
  color:#cfe0ff; background:rgba(120,160,255,.1); border:1px solid rgba(120,160,255,.3); text-align:left}
.snd-v small{font-size:10.5px; letter-spacing:.1em; color:var(--muted)}
.snd-v.on{color:#1a1000; background:linear-gradient(180deg,#fff1b0,#ffc83a 65%,#e09000); border-color:#fff1b0}
.snd-v.on small{color:#5a3a00}
/* The website's preview window (js/main.js #preview=1): the ways in are visibly out of reach. */
body.preview .prow.add, body.preview .qa-chip, body.preview .start{filter:saturate(.7) brightness(.85)}
/* A preview is a shop window: the tiles show their picture and their name, nothing else. */
body.preview .card p, body.preview .card .pcount{display:none}
body.preview .card .meta{padding:10px 14px 12px}

/* Board settings (the gear in the header): who plays here, and removing one of them for good. */
.set-sheet{max-width:560px}
.set-body{display:flex; flex-direction:column; gap:18px; max-height:64vh; overflow:auto; padding-right:4px}
.set-sec{display:flex; flex-direction:column; gap:8px}
.set-sec.danger .eyebrow{color:#ff8098}
.set-list{display:flex; flex-direction:column; gap:8px}
.set-inline{display:flex; flex-wrap:wrap; align-items:center; gap:8px}
.set-inline input{flex:1; min-width:140px; min-height:46px; padding:8px 16px; border-radius:14px; font-size:17px; font-weight:700;
  color:var(--text); background:rgba(4,7,22,.55); border:1px solid rgba(120,160,255,.3)}
.set-note{margin:0; color:var(--muted); font-size:12.5px; line-height:1.5}
.set-note.warn{color:#ffb9c6}
.set-note b{color:var(--text)}
.set-dim{color:var(--muted); font-size:13px; font-weight:600}
.set-code{font-family:var(--num); font-size:32px; letter-spacing:.22em; color:#ffe07a; padding:6px 14px; border-radius:14px;
  background:rgba(255,200,58,.12); border:1px solid rgba(255,200,58,.4)}

.set-row{display:flex; align-items:center; gap:12px; padding:10px 12px; border-radius:16px;
  background:rgba(120,160,255,.07); border:1px solid rgba(120,160,255,.16)}
.set-row .avatar{--pc:#6f86c8; flex:none}
.set-who{min-width:0; flex:1}
.set-who b{display:block; font-size:17px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.set-who small{display:block; color:var(--muted); font-size:12.5px; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.set-row.danger-row{background:rgba(255,45,85,.12); border-color:rgba(255,45,85,.45)}
.set-house{align-self:center; color:var(--muted); font-size:12px; font-weight:700; letter-spacing:.08em; text-transform:uppercase}
.pill.small{min-height:40px; padding:6px 16px; font-size:14px}
.set-row.gone-row{opacity:.6; border-style:dashed}
/* The PIN on the gear, now that the settings sheet is where the record book is managed (2026-09-20). */
.set-lock #setPin, .set-lock #setCode, #setNewPin{flex:1; min-width:120px; min-height:46px; padding:8px 16px; border-radius:14px; font-family:var(--num);
  font-size:24px; letter-spacing:.3em; text-align:center; color:var(--text); background:rgba(4,7,22,.55); border:1px solid rgba(120,160,255,.3)}
/* A set of record boards opens up to the games behind it, the way the admin page used to list them. */
.set-row[data-open]{cursor:pointer}
.set-chev{flex:none; width:18px; text-align:center; color:var(--muted); font-size:13px}
.set-row.set-open{background:rgba(120,160,255,.14); border-color:rgba(120,160,255,.35)}
.set-sublist{display:flex; flex-direction:column; gap:6px; margin:6px 0 2px; padding-left:12px; border-left:2px solid rgba(120,160,255,.25)}
.set-row.set-sub{padding:7px 10px; background:rgba(120,160,255,.05)}
.set-row.set-sub .set-who b{font-size:15px}


/* ---------- the scoring panel and the race scene, both of them a board rather than the app around one ---------- */
.scorep{display:flex; flex-direction:column; align-items:center; justify-content:center; gap:10px; padding:14px; overflow:hidden; min-height:0; container-type:size}
/* Sized by height as before, but never wider than its card: --chars (set in numeral.js) is the number of
   digits showing, and a Russo One digit is about .68em wide. A phone upright cut 501 to "50" without it. */
.bigscore{position:relative; font-family:var(--num); font-size:min(clamp(80px, 24vh, 230px), calc(92cqw / (var(--chars, 3) * .68)), 52cqh); line-height:1; text-align:center; transform-origin:center}
.bigscore .ext{position:absolute; left:0; right:0; top:0; font-weight:400; color:#4a2600; text-shadow:0 2px 0 #7a4600,0 4px 0 #6a3c00,0 6px 0 #5a3200,0 8px 0 #4a2800,0 10px 0 #3a1f00,0 22px 40px rgba(0,0,0,.7)}
.bigscore .face{position:relative; background:linear-gradient(180deg,#fffdf0 0%,#ffe690 26%,#ffbe1a 50%,#fff3c0 52%,#f0a000 76%,#ffd766 100%); -webkit-background-clip:text; background-clip:text; color:transparent; filter:drop-shadow(0 0 22px rgba(255,190,40,.35))}
.bigscore.bump{animation:bump .45s cubic-bezier(.2,1.6,.4,1)}
/* The pieces of a busted number (js/views/numeral.js shatter). Each one is a whole copy of the numeral cut
   down to a wedge by clip-path, so the extrude, the chrome and the game's own colours all come with it. */
.bigscore .bs-shard{position:absolute; inset:0; will-change:transform, opacity}
.bigscore.shattering{overflow:visible}
.bigscore.shattering > .ext, .bigscore.shattering > .face{opacity:0}
/* FOG ON THE WATER (Cory, 2026-09-21: "make the scoring digits much larger but slightly transparent and
   look like cloud lettering as if it's fog"). Two layers of the same number: a heavily blurred copy that
   breathes underneath, and the number itself cut out of a gradient that never reaches full white, so the
   sea and whatever is in it show through. Nothing here is a filter on the scene — the boat stays sharp. */
.scorep.bermuda .bigscore{font-size:min(clamp(100px, 32vh, 320px), calc(96cqw / (var(--chars, 3) * .64)), 64cqh)}
.scorep.bermuda .bigscore .ext{color:#dbeeff; text-shadow:none; filter:blur(14px); opacity:.38;
  animation:bmFog 8s ease-in-out infinite alternate}
/* THE FOG BREATHES WITHOUT BEING REDRAWN (2026-10-19, after Count Up lagged the phone). It breathed by
   animating its blur, which re-blurred a panel-sized number every frame, and the face breathed by animating
   its letter-spacing, which laid the page out again every frame. Now the tight fog fades out as a looser
   copy of it (::before) fades in, and the face swells by a transform; the blurs never change. */
@keyframes bmFog{from{opacity:.38} to{opacity:0}}
.scorep.bermuda .bigscore::before{content:attr(data-v); position:absolute; left:0; right:0; top:0; color:#dbeeff;
  filter:blur(24px); opacity:0; animation:bmFogWide 8s ease-in-out infinite alternate}
@keyframes bmFogWide{from{opacity:0} to{opacity:.62}}
.scorep.bermuda .bigscore.shattering::before{opacity:0; animation:none}
.scorep.bermuda .bigscore .face{background:linear-gradient(180deg, rgba(255,255,255,.88) 0%, rgba(226,243,255,.66) 42%,
    rgba(186,219,255,.42) 74%, rgba(160,200,245,.3) 100%);
  -webkit-background-clip:text; background-clip:text; color:transparent; position:relative; display:inline-block;
  filter:drop-shadow(0 0 30px rgba(190,226,255,.45)); animation:bmFogFace 11s ease-in-out infinite alternate}
@keyframes bmFogFace{from{opacity:.82; transform:none} to{opacity:.98; transform:scaleX(1.02)}}
/* CHROME, AND MUCH BIGGER (Cory, 2026-09-20: "let's make the numbers (301) look like the 80's chrome, make
   them much larger"). The same gradient the card's numeral is cut from — white to the cool colour, a hard
   line across the middle, then the hot one down into purple and out to lavender — with a neon edge burning
   behind it. The size caps all go up: on that backdrop the number is the poster, not a readout. */
.scorep.retro .bigscore{font-size:min(clamp(104px, 34vh, 340px), calc(97cqw / (var(--chars, 3) * .62)), 68cqh)}
/* GLOWS, NOT FILTERS (2026-10-19, after Count Up lagged the phone). The number changes on every frame of its
   count after a dart, and a filter is redrawn each time; on a phone that is a blur the size of the panel,
   twenty-five times a dart. The neon edge's glow is text shadows now, and the dark lip under the chrome -
   which was a drop-shadow on the face - is a dark copy of the number between the edge and the face
   (::before, js numeral.js setScore keeps data-v), so it sits over the neon edge exactly as before. No
   color-mix: the old tablet's Chrome drops a whole text-shadow over it, so the cool glow comes from js. */
.scorep.retro .bigscore .ext{color:transparent; -webkit-text-stroke:.068em var(--hot); paint-order:stroke; opacity:.9;
  text-shadow:0 0 1.5px var(--hot), 0 0 24px var(--hot), 0 0 40px var(--hot), 0 0 60px var(--hot), 0 0 34px var(--cool-glow, transparent)}
.scorep.retro .bigscore .face{background:linear-gradient(180deg,#fff 0%,#eafcff 16%,var(--cool) 40%,#fff 49.5%,
    var(--hot) 50.5%, #7a1f8c 78%, #e7d6ff 100%);
  -webkit-background-clip:text; background-clip:text; color:transparent; filter:none; position:relative; z-index:2}
.scorep.retro .bigscore::before{content:attr(data-v); position:absolute; left:0; right:0; top:3px; z-index:1; color:rgba(0,0,0,.45); text-align:center}
.scorep.retro .bigscore.shattering::before{opacity:0}
/* A PLASTIC TELEVISION, NOT A PANE OF GLASS (Cory, 2026-09-20: "can we also frame it in a plastic 80's TV
   instead of the square?"). The set IS the panel: a moulded body with the light catching its top edge, the
   tube sunk into it behind glass that is rounder than any panel would be, a grille moulded into the chin,
   and the little red lamp that told you it was on. */
.scorep.retro{--bez:clamp(11px, 3vh, 26px); --chin:clamp(20px, 5.4vh, 46px);
  padding:var(--bez); padding-bottom:var(--chin); border:none; border-radius:clamp(18px, 3.4vh, 30px);
  background:linear-gradient(158deg, #5c5c68 0%, #3e3e49 20%, #292930 58%, #17171d 100%);
  box-shadow:inset 0 2px 0 rgba(255,255,255,.26), inset 0 -8px 16px rgba(0,0,0,.55), 0 20px 44px rgba(0,0,0,.55)}
/* The tube: deeper corners than the cabinet, and sunk into it. */
.scorep.retro .bd{inset:var(--bez); bottom:var(--chin);
  border-radius:clamp(14px, 6%, 40px) / clamp(18px, 8%, 52px);
  box-shadow:inset 0 0 46px rgba(0,0,0,.8), inset 0 0 0 3px rgba(0,0,0,.66), 0 0 0 2px rgba(255,255,255,.07)}
/* The chin: a moulded grille on the left, the power lamp on the right. */
.scorep.retro::before{content:""; position:absolute; left:clamp(16px, 3vh, 26px); bottom:clamp(7px, 1.4vh, 13px);
  width:clamp(40px, 9%, 84px); height:clamp(5px, 1vh, 9px); border-radius:2px; opacity:.5;
  background:repeating-linear-gradient(90deg, rgba(0,0,0,.6) 0 2px, rgba(255,255,255,.12) 2px 4px)}
.scorep.retro::after{content:""; position:absolute; right:clamp(16px, 3vh, 26px); bottom:clamp(8px, 1.6vh, 15px);
  width:clamp(6px, 1.1vh, 10px); aspect-ratio:1; border-radius:50%; background:#ff5f3a;
  box-shadow:0 0 10px rgba(255,95,58,.9), inset 0 1px 1px rgba(255,255,255,.5)}
/* A cabinet measured in screen height knows nothing about how small this panel is, and on a phone it ate
   half the tube. The set gets slimmer where the panel is. */
@media (max-width:600px), (max-height:560px){
  .scorep.retro{--bez:9px; --chin:17px; border-radius:16px}
  .scorep.retro::before{width:34px; height:4px; left:12px; bottom:6px}
  .scorep.retro::after{width:6px; right:12px; bottom:6px}
}
.scorep.retro .eyebrow{color:#ffe7ff; text-shadow:0 2px 8px rgba(0,0,0,.95), 0 0 16px var(--hot)}
.scorep.retro .checkout .lbl{color:#ffe7ff}
@keyframes bump{0%{transform:scale(.86)} 100%{transform:scale(1)}}
.checkout{display:flex; align-items:center; gap:8px; min-height:34px}
.checkout .ds{display:flex; align-items:center; justify-content:center; flex-wrap:wrap; gap:8px}
.checkout .lbl{font-size:11px; letter-spacing:.22em; font-weight:700; color:#7ff0c0}
.checkout .d{padding:4px 12px; border-radius:999px; font-weight:700; background:rgba(46,230,160,.12); border:1px solid rgba(46,230,160,.45); color:#c8ffe8}
/* THE ROUTE OUT, UNDER THE NUMBER (Cory, 2026-09-20: "right now it's off center and you can barely see it.
   Make it high contrast"). Stacked, so the darts are centred under the score instead of being pushed off
   to one side by the word in front of them, and dark-backed at full white so they read against the grid. */
.checkout.co{flex-direction:column; gap:5px}
.checkout.co .ds{gap:clamp(5px, 1.2cqw, 10px)}
.checkout.co .lbl{font-size:clamp(10px, 1.5vh, 13px); color:#bfe9ff; letter-spacing:.26em; text-shadow:0 2px 6px rgba(0,0,0,.9)}
/* Sized by the CARD, not the window: three darts of a 3-dart checkout have to fit across a panel that is
   165px wide on a phone, and a height-based clamp put them through the side of the television. */
.checkout.co .d{font-family:var(--num); font-size:clamp(12px, min(2.9vh, 7.2cqw), 30px); line-height:1; padding:.34em .62em; border-radius:999px;
  background:rgba(4,12,26,.92); border:2px solid #39d5ff; color:#fff; letter-spacing:.04em;
  box-shadow:0 0 18px rgba(57,213,255,.5), inset 0 0 12px rgba(57,213,255,.18); text-shadow:0 0 10px rgba(57,213,255,.75)}
/* BERMUDA'S TARGET, UNDER THE BOAT (Cory, 28 Oct 2026, round 4: "make that target doubles bigger down below. Put it
   down below the boat, because right now it's covering the boat. Make that bigger and kind of pulse, so you know
   what it is"). The panel's bottom edge, clear of the scene, the chip twice the size and breathing. */
.scorep.bermuda .checkout.co{position:absolute; left:0; right:0; bottom:clamp(10px, 3.5cqh, 30px); z-index:3; gap:8px}
.scorep.bermuda .checkout.co .lbl{font-size:clamp(12px, 2.2vh, 20px); letter-spacing:.3em}
/* Count Up: the big number is the running TOTAL, said large enough to tell it from the darts' own points (Cory, 29 Oct 2026). */
.scorep.pulse .checkout.co .lbl{font-size:clamp(16px, 3.2vh, 34px); letter-spacing:.32em; color:#fff}
.scorep.bermuda .checkout.co .d{font-size:clamp(20px, min(5.6vh, 13cqw), 60px); padding:.3em .85em; border-width:3px;
  animation:tgtPulse 1.1s ease-in-out infinite alternate}
/* ---------- HORSE RACE ---------- */
.scene{position:relative; border-radius:22px; overflow:hidden; border:1px solid var(--edge); box-shadow:0 18px 50px rgba(0,0,0,.5); min-height:0; background:#8fc3ff}
.scene > svg{position:absolute; inset:0; width:100%; height:100%}
/* THE WORLD GOES THE OTHER WAY (Cory, 2026-09-17: "the sky is going the wrong way, it should create the
   sense of movement"). The horses run right, so everything behind them has to travel LEFT — clouds drifting
   the other way read as the whole field going backwards. */
.cloud{animation:cloud linear infinite}
@keyframes cloud{from{transform:translateX(1460px)} to{transform:translateX(-260px)}}
/* Parallax is driven from race.js (RACE_THEMES.horse.loop), not CSS: one frame loop moves every layer by
   its own depth, eases the whole world between idle and full gallop, and shifts far layers with the camera
   less than near ones. CSS keeps only the things that never change speed abruptly. */
@media (prefers-reduced-motion:reduce){ .cloud{animation:none} }
/* The course in its own units (js race.js horse build): the camera, every layer the frame loop slides, each
   lane's dashes, the streaks and the horses are layers of their own, so moving them redraws nothing. */
.hr-cam, .hr-units .hlayer, .hr-units .hlane, .hr-units .hspeed > svg, .hr-units .horse{will-change:transform}
.hr-units .hlane-clip{overflow:hidden}
.hspeed svg{overflow:visible}
.crowdrow{animation:cheer .5s ease-in-out infinite alternate}
.crowdrow:nth-child(2){animation-delay:-.17s} .crowdrow:nth-child(3){animation-delay:-.33s} .crowdrow:nth-child(4){animation-delay:-.08s}
.hspeed line{stroke:#fff; stroke-linecap:round}
@keyframes cheer{to{transform:translateY(-3px)}}
.flag{transform-box:fill-box; transform-origin:0 50%; animation:flag 1.2s ease-in-out infinite alternate}
@keyframes flag{from{transform:skewY(6deg) scaleX(.9)} to{transform:skewY(-6deg) scaleX(1.05)}}
/* THE GALLOP (2026-09-18). One stride is .72s at rest; while a horse runs, race.js turns the SAME animations
   up with playbackRate instead of swapping to a shorter duration. Swapping the duration makes the browser
   recompute where every animation is, and the whole scene jumped a few frames each time a horse set off —
   the stutter Cory saw on doubles and triples. Fronts fold back at the knee, hinds fold at the hock, the
   hinds lead by half a stride, and the far legs trail their near partner a touch. */
.horse .bob{transform-box:fill-box; transform-origin:50% 100%; animation:bob .72s linear infinite}
.horse .lg, .horse .lg .shin{transform-box:fill-box; animation:.72s linear infinite}
.horse .lg{transform-origin:50% 3%}
.horse .lg .shin{transform-origin:50% 7%}
.horse .lg.f{animation-name:gallopF} .horse .lg.f .shin{animation-name:shinF}
.horse .lg.h{animation-name:gallopH} .horse .lg.h .shin{animation-name:shinH}
.horse .lg.far, .horse .lg.far .shin{animation-delay:-.08s}
/* BLOWING (Cory, 2026-09-21: "missing any dart should make the horse throwing off sweat and steam from the
   head area"). Steam off the muzzle, sweat off the neck and shoulder, both gone inside two seconds. */
/* THE VICTORY LAP (js/views/race.js victory). Over the track, under nothing: the board is finished and
   the win bar is holding, so this is what there is to look at. Five and a half seconds, then it takes
   itself away and leaves the finished board behind it. */
.hr-vic{position:absolute; inset:0; z-index:5; pointer-events:none; overflow:hidden;
  animation:hrVicIn .5s ease both}
.hr-vic.out{animation:hrVicOut .8s ease forwards}
@keyframes hrVicIn{from{opacity:0} to{opacity:1}}
@keyframes hrVicOut{to{opacity:0}}
.hr-vic svg{position:absolute; inset:0; width:100%; height:100%}
.hr-vdim{fill:#05060f; opacity:0; animation:hrVdim 5.4s ease both}
@keyframes hrVdim{0%{opacity:0} 14%{opacity:.62} 76%{opacity:.62} 100%{opacity:0}}
.hr-vglow{opacity:0; transform-box:fill-box; transform-origin:50% 50%; animation:hrVglow 5.4s ease both}
@keyframes hrVglow{0%{opacity:0; transform:scale(.4)} 22%{opacity:1; transform:scale(1)}
  70%{opacity:.9; transform:scale(1.1)} 100%{opacity:0; transform:scale(1.4)}}
/* Out of the track, into your face, up on the hind legs once, and away down the rail. */
.hr-hero{transform-box:fill-box; transform-origin:50% 100%; animation:hrHero 5.4s cubic-bezier(.3,.7,.4,1) both}
@keyframes hrHero{
  0%{transform:translate(-260px, 90px) scale(.34); opacity:0}
  9%{opacity:1}
  22%{transform:translate(180px, 4px) scale(1.95)}
  30%{transform:translate(200px, -6px) scale(2.05) rotate(-15deg)}
  40%{transform:translate(190px, 4px) scale(2) rotate(0)}
  52%{transform:translate(210px, -4px) scale(2.04) rotate(-9deg)}
  62%{transform:translate(190px, 4px) scale(2) rotate(0)}
  100%{transform:translate(1900px, 30px) scale(1.5); opacity:1}}
.hr-vdust{fill:#c8a882; opacity:0; transform-box:fill-box; transform-origin:50% 50%;
  animation:hrVdust 1.1s ease-out infinite; animation-delay:var(--d, 0s)}
@keyframes hrVdust{0%{opacity:.5; transform:translate(0,0) scale(.5)} 100%{opacity:0; transform:translate(-190px,-40px) scale(1.5)}}
.hr-vlines path{opacity:0; animation:hrVline 5.4s linear both; animation-delay:var(--d, 0s)}
@keyframes hrVline{0%,58%{opacity:0; transform:translateX(0)} 66%{opacity:.5} 100%{opacity:0; transform:translateX(1500px)}}
@media (prefers-reduced-motion:reduce){ .hr-hero, .hr-vdust, .hr-vlines path{animation-duration:.01s} }
.hr-lather{pointer-events:none}
.hr-steam{fill:#ffffff; opacity:0; transform-box:fill-box; transform-origin:50% 50%;
  animation:hrSteam 1.5s ease-out both; animation-delay:var(--d, 0s)}
@keyframes hrSteam{0%{opacity:0; transform:translate(0,0) scale(.35)}
  18%{opacity:.5} 100%{opacity:0; transform:translate(78px,-70px) scale(2.2)}}
.hr-sweat{fill:#bfe6ff; opacity:0; transform-box:fill-box; transform-origin:50% 0%;
  animation:hrSweat 1.1s cubic-bezier(.4,0,.8,1) both; animation-delay:var(--d, 0s)}
@keyframes hrSweat{0%{opacity:0; transform:translate(0,0) scale(.6)}
  15%{opacity:.95} 100%{opacity:0; transform:translate(-26px,96px) scale(1.1)}}
.horse .tail{transform-box:fill-box; transform-origin:100% 10%; animation:tail .72s ease-in-out infinite}
.horse .mane{transform-box:fill-box; transform-origin:100% 0%; animation:tail .72s ease-in-out infinite reverse}
@keyframes gallopF{0%{transform:rotate(-36deg)} 30%{transform:rotate(-6deg)} 55%{transform:rotate(26deg)} 78%{transform:rotate(6deg)} 100%{transform:rotate(-36deg)}}
@keyframes shinF{0%{transform:rotate(6deg)} 30%{transform:rotate(0deg)} 55%{transform:rotate(8deg)} 72%{transform:rotate(80deg)} 88%{transform:rotate(44deg)} 100%{transform:rotate(6deg)}}
@keyframes gallopH{0%{transform:rotate(30deg)} 25%{transform:rotate(6deg)} 52%{transform:rotate(-30deg)} 78%{transform:rotate(-2deg)} 100%{transform:rotate(30deg)}}
@keyframes shinH{0%{transform:rotate(-10deg)} 20%{transform:rotate(48deg)} 42%{transform:rotate(26deg)} 55%{transform:rotate(0deg)} 100%{transform:rotate(-10deg)}}
@keyframes bob{0%{transform:translateY(0) rotate(0deg)} 25%{transform:translateY(-8px) rotate(-2deg)} 50%{transform:translateY(-2px) rotate(1.5deg)} 75%{transform:translateY(-6px) rotate(-.5deg)} 100%{transform:translateY(0) rotate(0deg)}}
@keyframes tail{0%,100%{transform:rotate(-6deg)} 50%{transform:rotate(9deg)}}
.hshadow{fill:rgba(40,20,5,.28)}
.hnum{font-family:var(--num); font-size:15px; text-anchor:middle; dominant-baseline:central}
.lanepop{font-family:var(--num); font-size:34px; text-anchor:middle; paint-order:stroke; stroke:#3a1d05; stroke-width:6; fill:#ffe27a; opacity:0}
.lanepop.go{animation:lanepop 1s ease-out forwards}
@keyframes lanepop{0%{opacity:0; transform:translateY(10px)} 20%{opacity:1; transform:translateY(-6px)} 75%{opacity:1} 100%{opacity:0; transform:translateY(-34px)}}
/* The sign is sized off the BOARD, not the window. On a tablet those are near enough the same thing; on a
   phone watching over /api/live the board is a third of the screen and the sign was eating half of it
   (Cory, 2026-09-17: "the position box is way too big on mobile"). Declared after the px/vh rules on
   purpose — a browser without container queries drops the cqw line and keeps what it had. */
.scene{container-type:inline-size}
/* WHOSE THROW, AND WHAT THEY ARE ON — as a rail across the top of the scene, not a plate dropped onto it.

   It used to be a wooden sign parked over the map: a box in the corner, covering the part of the world the
   plane was flying across, and on a phone it took half the board (Cory, 2026-09-17: "I'm not a fan of that
   box... something a little more elegant, a little less blocky and out of place. Maybe a line across
   instead of a box positioned somewhere").

   So it spans the full width and reads on one line — WHO, then the number, then where it is. There is no box
   and no backing: the words sit on the scene with their own shadow. A hairline in the world's own colour,
   faded at both ends, is the line.
   Nothing under it is hidden that matters — the sky is at the top of every one of these scenes. */
.sign{position:absolute; left:0; right:0; top:0; z-index:3; display:flex; align-items:baseline;
  gap:clamp(6px, 2cqw, 14px); padding:clamp(6px, 1.8cqw, 12px) clamp(10px, 3cqw, 20px) clamp(12px, 3.4cqw, 22px);
  --sg:#ffd28a; pointer-events:none}
.sign::after{content:""; position:absolute; left:6%; right:6%; bottom:clamp(7px, 2cqw, 13px); height:1px;
  background:linear-gradient(90deg, transparent, color-mix(in srgb, var(--sg) 75%, transparent) 18%, color-mix(in srgb, var(--sg) 75%, transparent) 82%, transparent)}
/* No dark fade behind the rail, in any game (Cory, 2026-09-19: "remove the dark gradient on top of the
   animation window... apply this to every game"). It dimmed the top of every scene; the words carry their
   own shadow instead. */
/* No hairline on the clock (2026-09-18): the dial sits in the middle of its box, so on a phone it ran straight through it. */
.sign-clock::after{display:none}
/* Horse Race's sky is light, and white words vanished into its clouds once the fade went: dark ink there. */
/* Around the World drops the "CORY · NEXT STOP" label (Cory, 2026-09-19): the jet and the number already say it. */
.sign.sign-world .plate small, .sign-world::after{display:none}
.sign.sign-horse .plate small, .sign.sign-horse .plate em{color:#10244d; text-shadow:0 1px 0 rgba(255,255,255,.7), 0 0 8px rgba(255,255,255,.6)}
.sign .ropes{display:none}
.sign .plate{position:relative; z-index:1; display:flex; align-items:baseline; gap:clamp(6px, 2cqw, 14px);
  min-width:0; flex:1; padding:0; background:none; border:0; border-radius:0; box-shadow:none; text-align:left}
/* Who is up. Wide-tracked and quiet: it is the label, not the news. */
.sign .plate small{flex:none; font-weight:800; text-transform:uppercase; color:color-mix(in srgb, var(--sg) 62%, #eaf0ff);
  letter-spacing:clamp(.08em, .5cqw, .2em); font-size:clamp(8px, 2.6cqw, 13px); text-shadow:0 1px 3px rgba(0,0,0,.95), 0 0 10px rgba(0,0,0,.7)}
/* The number they are on. The one thing anybody looks at, so it keeps the display font and the gold. */
.sign .plate b{flex:none; margin-left:auto; font-family:var(--num); font-weight:400; line-height:1;
  font-size:clamp(17px, 6cqw, 40px); background:linear-gradient(180deg,#fffbe0,#ffd23a 58%,#f0a400);
  -webkit-background-clip:text; background-clip:text; color:transparent; filter:drop-shadow(0 2px 4px rgba(0,0,0,.8)) drop-shadow(0 0 8px rgba(0,0,0,.5))}
/* Where that is — the city, the target's name. Trimmed rather than wrapped: the rail stays one line. */
.sign .plate em{flex:0 1 auto; min-width:0; font-style:normal; font-weight:700; text-transform:uppercase; color:#eaf0ff;
  letter-spacing:clamp(.04em, .35cqw, .14em); font-size:clamp(9px, 3cqw, 16px); text-shadow:0 1px 3px rgba(0,0,0,.95), 0 0 10px rgba(0,0,0,.7);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.sign em:empty{display:none}
.sign.flip .plate b{animation:signflip .5s ease-in-out}
/* Once the game is over the winner's name is across the bottom of the screen at four times this size, so
   the in-scene sign is saying it twice. The scene keeps the plane and the nameplate on it; the result is
   the banner's job. */
.view.over .sign{display:none}
@keyframes signflip{0%{transform:rotateX(0)} 50%{transform:rotateX(90deg)} 100%{transform:rotateX(0)}}
.rhud{position:absolute; left:12px; top:12px; padding:8px 14px; border-radius:14px; background:rgba(20,12,4,.55); color:#fff; font-weight:700; letter-spacing:.1em; font-size:13px}
.rhud b{font-family:var(--num); font-weight:400; font-size:24px; letter-spacing:0; margin-left:6px}
.rdarts{position:absolute; right:12px; top:12px; display:flex; gap:6px}
.minimap{position:relative; height:50px; padding:0 40px 0 22px; border-radius:999px; background:linear-gradient(180deg, rgba(36,56,120,.5), rgba(9,14,38,.82)); border:1px solid var(--edge)}
.mm-in{position:relative; height:100%}
.mm-line{position:absolute; left:0; right:0; top:50%; height:4px; margin-top:-2px; border-radius:2px; background:repeating-linear-gradient(90deg, rgba(255,230,190,.5) 0 2px, transparent 2px 100%); background-size:calc(100% / 21) 100%; background-color:rgba(255,230,190,.18)}
.mm-flag{position:absolute; right:-34px; top:50%; transform:translateY(-50%); font-family:var(--num); font-size:11px; color:#fff; background:#ff2d55; padding:2px 6px; border-radius:6px}
.mm-dot{--pc:#fff; position:absolute; top:50%; width:22px; height:22px; margin:-11px 0 0 -11px; border-radius:50%; display:grid; place-items:center; font-family:var(--num); font-size:12px; color:#140a02; background:var(--pc); box-shadow:0 0 0 2px rgba(20,10,2,.8), 0 0 12px var(--pc); transition:left .7s cubic-bezier(.4,0,.2,1), transform .3s}
.mm-dot.on{transform:scale(1.3) translateY(-2px); z-index:2}
.rdarts i{width:34px; height:34px; border-radius:50%; background:rgba(20,12,4,.55); display:grid; place-items:center}
.rdarts i svg{width:20px; height:20px}
.rdarts i.used{opacity:.3}
.rcards .pcard .sc{font-size:clamp(20px, 3.4vh, 30px)}
.rcards .prog{position:absolute; left:0; bottom:0; height:4px; background:var(--pc); box-shadow:0 0 10px var(--pc); transition:width .6s}


/* SCENE UNITS (js/fx.js sceneFit / scenePart): a div whose pixels are a drawing's own units, scaled and centred
   as preserveAspectRatio="xMidYMid slice" would place it; every piece in it sits at its own coordinates. */
.su{position:absolute; left:0; top:0; width:0; height:0; transform-origin:0 0; pointer-events:none}
.su div, .su svg{position:absolute; left:0; top:0}
.su svg{overflow:visible}

/* ---------- backdrops: the world a number game is played in (js/views/numeral.js) ---------- */
.scorep.scened{overflow:hidden}
.scorep.scened > .bigscore, .scorep.scened > .checkout{position:relative; z-index:1}
.bd{position:absolute; inset:0; overflow:hidden; border-radius:inherit; pointer-events:none}
.bd .bd-svg{width:100%; height:100%; display:block}

/* THE 01 GAMES: THE POSTER, MOVING (Cory, 2026-09-20: "these title images already look super 80's and
   awesome, but the game pales in comparison... make the background match the title image and have it
   animated, with light moving around"). Every line of the floor runs the same journey — out of the horizon,
   accelerating, off the bottom — and they are only started at different points in it, which is why a
   twentieth of them is enough to make a road that never stops arriving. */
/* Each floor line is a strip one unit tall (js BACKDROPS.retro): the journey moves it down and thickens it. */
.bd-retro .rt-floor{overflow:hidden}
.bd-retro .rt-h{position:absolute; left:-40px; top:-.5px; width:1280px; height:1px; background:var(--cool); opacity:0;
  animation:rtRun 7.6s cubic-bezier(.55,0,.85,.45) infinite; will-change:transform, opacity}
@keyframes rtRun{
  0%{transform:translateY(0) scaleY(1); opacity:0}
  8%{opacity:.85}
  100%{transform:translateY(345px) scaleY(5); opacity:.9}
}
/* The sun's bands drift up through it, the way they did on every one of those covers - a unit at a time, so
   its little drawing is redrawn five times a second, not sixty (the bands are cut out of it; no element can
   slide a hole). Its glow brightens and dims on the element round it. */
.bd-retro .rt-slits{animation:rtSlits 7s steps(38) infinite}
@keyframes rtSlits{to{transform:translateY(-38px)}}
.bd-retro .rt-sunbox{animation:rtSun 5s ease-in-out infinite alternate}
@keyframes rtSun{to{filter:brightness(1.18)}}
/* Two slow lamps wandering the sky: the light moving around. Their gradients and blend are the drawing's;
   they turn and grow about the drawing's corner, as they did inside it. */
.bd-retro .rt-lamp{position:absolute; border-radius:50%; opacity:.42; mix-blend-mode:screen; will-change:transform}
.bd-retro .rt-lamp.a{left:-20px; top:-170px; width:640px; height:640px; transform-origin:20px 170px;
  background:radial-gradient(closest-side, var(--cool), transparent); animation:rtLampA 19s ease-in-out infinite alternate}
.bd-retro .rt-lamp.b{left:600px; top:-180px; width:600px; height:600px; transform-origin:-600px 180px; opacity:.4;
  background:radial-gradient(closest-side, var(--hot), transparent); animation:rtLampB 23s ease-in-out infinite alternate}
@keyframes rtLampA{from{transform:translate(-90px,-20px) scale(.9)} to{transform:translate(380px,60px) scale(1.15)}}
@keyframes rtLampB{from{transform:translate(120px,40px) scale(1.1)} to{transform:translate(-280px,-30px) scale(.88)}}
/* A band of light crossing the floor, and the horizon breathing under it: the glow is a lit copy fading in. */
.bd-retro .rt-sweep{position:absolute; left:-600px; top:0; width:600px; height:100%;
  background:linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,.16), rgba(255,255,255,0)); animation:rtSweep 18s linear infinite}
@keyframes rtSweep{from{transform:translateX(0)} to{transform:translateX(1800px)}}
.bd-retro .rt-hzglow, .bd-retro .rt-hzline{position:absolute; left:-40px; width:1280px}
.bd-retro .rt-hzglow{height:14px; background:var(--cool); opacity:.25}
.bd-retro .rt-hzline{height:3px; background:#fff; animation:rtHz 3.4s ease-in-out infinite alternate}
.bd-retro .rt-hzline.lit{filter:drop-shadow(0 0 10px var(--cool)); animation-name:rtHzLit}
@keyframes rtHz{from{opacity:.55} to{opacity:1}}
@keyframes rtHzLit{from{opacity:0} to{opacity:1}}
.bd-retro .rt-star{fill:#fff; animation:rtStar 4.4s ease-in-out infinite; animation-delay:calc(var(--d, 0s) * -1)}
@keyframes rtStar{0%,100%{opacity:.25} 50%{opacity:.9}}
.bd-retro .rt-tri{animation:rtTri 6s ease-in-out infinite alternate}
@keyframes rtTri{to{opacity:.5}}
@media (prefers-reduced-motion:reduce){
  .bd-retro .rt-h, .bd-retro .rt-slits, .bd-retro .rt-sunbox, .bd-retro .rt-lamp,
  .bd-retro .rt-sweep, .bd-retro .rt-hzline, .bd-retro .rt-star, .bd-retro .rt-tri{animation:none}
  .bd-retro .rt-h{opacity:.6}
  .bd-retro .rt-hzline.lit{opacity:0}
}

/* THE MACHINED WINDOW (Cory, 2026-09-26: "make the scoring window like the top score window, very
   industrial, very super hero, very transformers. Put the score in there and make it spin up cool").

   Built out of stacked elements rather than a picture, so it grows with the panel and can move: a gold
   plate with its corners cut, a bevel inside it, a dark lit slot for the number, a grid on the glass, two
   brackets top and bottom, four rivets, and a bar of light that crosses the glass every time the number
   changes. The number itself spins up over three quarters of a second instead of the usual nudge. */
.scorep.framed{position:relative; overflow:visible}
.scorep.framed > .bigscore, .scorep.framed > .checkout{position:relative; z-index:2}
.mechf{position:absolute; inset:4% 3%; z-index:0; pointer-events:none}
.mechf i{position:absolute; display:block}
.mf-plate{inset:0; background:linear-gradient(160deg,#6a5a2a 0%,#f0d478 14%,#fff6d0 26%,#d8a828 46%,#8a6a1a 62%,#f2dc90 82%,#6a5220 100%);
  clip-path:polygon(9% 0, 91% 0, 100% 16%, 100% 84%, 91% 100%, 9% 100%, 0 84%, 0 16%);
  box-shadow:0 18px 30px rgba(0,0,0,.55)}
.mf-bevel{inset:5px; background:linear-gradient(160deg,#3a2f12,#8a7028 40%,#2a2210);
  clip-path:polygon(9% 0, 91% 0, 100% 16%, 100% 84%, 91% 100%, 9% 100%, 0 84%, 0 16%)}
.mf-win{inset:14px; background:radial-gradient(ellipse at 50% 30%, #16233f 0%, #0a1020 55%, #050810 100%);
  clip-path:polygon(9% 0, 91% 0, 100% 16%, 100% 84%, 91% 100%, 9% 100%, 0 84%, 0 16%);
  box-shadow:inset 0 0 40px rgba(60,140,255,.25)}
/* the grid on the glass, and the two brackets that hold the plate */
.mf-grid{inset:14px; opacity:.25; clip-path:polygon(9% 0, 91% 0, 100% 16%, 100% 84%, 91% 100%, 9% 100%, 0 84%, 0 16%);
  background:repeating-linear-gradient(90deg, rgba(120,190,255,.35) 0 1px, transparent 1px 34px),
    repeating-linear-gradient(0deg, rgba(120,190,255,.28) 0 1px, transparent 1px 34px)}
.mf-bar{left:16%; right:16%; height:10px; border-radius:5px;
  background:linear-gradient(180deg,#f6e6a8,#c69a20 55%,#6a5014); box-shadow:0 2px 0 rgba(0,0,0,.45)}
.mf-bar.top{top:-5px} .mf-bar.bot{bottom:-5px}
.mf-riv{width:13px; height:13px; border-radius:50%;
  background:radial-gradient(circle at 34% 30%, #fff8d8, #d8b23c 55%, #6a5014); box-shadow:inset 0 -1px 2px rgba(0,0,0,.5)}
.mf-riv.a{left:12px; top:12px} .mf-riv.b{right:12px; top:12px}
.mf-riv.c{left:12px; bottom:12px} .mf-riv.d{right:12px; bottom:12px}
/* and the light that runs across it when the score moves */
.mf-sweep{inset:14px; clip-path:polygon(9% 0, 91% 0, 100% 16%, 100% 84%, 91% 100%, 9% 100%, 0 84%, 0 16%);
  background:linear-gradient(105deg, transparent 38%, rgba(140,210,255,.5) 48%, rgba(255,255,255,.75) 52%, rgba(140,210,255,.5) 56%, transparent 66%);
  opacity:0}
.mf-sweep.mf-go{animation:mfGo .76s cubic-bezier(.3,.7,.3,1)}
@keyframes mfGo{0%{opacity:0; transform:translateX(-58%)} 18%{opacity:1} 100%{opacity:0; transform:translateX(58%)}}
@media (prefers-reduced-motion:reduce){ .mf-sweep.mf-go{animation:none} }
/* the number in the slot: lit from inside, in the plate's own gold */
.scorep.frame-mech .bigscore{font-size:min(clamp(92px, 28vh, 300px), calc(72cqw / (var(--chars, 3) * .62)), 52cqh)}
.scorep.frame-mech .bigscore .ext{color:#1a1206; text-shadow:0 2px 0 #241a08,0 4px 0 #1c1406,0 6px 0 #140e04,0 16px 30px rgba(0,0,0,.8)}
.scorep.frame-mech .bigscore .face{background-image:linear-gradient(180deg,#fffbe8 0%,#ffe79a 24%,#f0b52a 48%,#fff3c8 52%,#c98a12 78%,#ffdf88 100%);
  filter:drop-shadow(0 0 30px rgba(255,200,80,.55))}
.scorep.frame-mech .checkout .lbl{color:#9fd0ff}

/* THE NUMBER IS THE SHOT (Cory, 2026-09-26: "the point count needs to be much larger, make it look more
   robinhood-like"). Every other game reads its number off a panel; here it is carved over a battlefield, so
   it takes the room a battlefield gives it — about a third again the size of the standard face — and wears
   gold leaf over a dark wood shadow rather than the app's polished chrome. */
.scorep.archery .bigscore{font-size:min(clamp(110px, 36vh, 360px), calc(98cqw / (var(--chars, 3) * .6)), 72cqh)}
.scorep.archery .bigscore .ext{color:#2a1608; text-shadow:0 3px 0 #3a2410,0 6px 0 #33200e,0 9px 0 #2a1a0b,0 12px 0 #1f1308,0 16px 0 #150d05,0 26px 44px rgba(0,0,0,.75)}
/* background-IMAGE, not the shorthand: `background:` resets background-clip, which is what cuts the number
   out of the gradient. Set it the other way and the whole panel fills with a gold slab. */
.scorep.archery .bigscore .face{background-image:linear-gradient(180deg,#fffbe8 0%,#ffdf8a 22%,#f0a92a 48%,#fff1c0 52%,#c87a12 78%,#ffd977 100%);
  filter:drop-shadow(0 0 26px rgba(255,180,60,.4))}

/* ARCHERY: THE HILL (js/views/numeral.js BACKDROPS.archery). Flat colour, one shadow tone, a warm rim off
   the low sun. Everything that moves is on a long slow loop — clouds, grass, the run cycle — so a screen
   left up all evening never asks for anything, and only a thrown dart makes anything happen fast. */
.bd-field .ar-dk{fill:#1c2436} .bd-field .ar-mid{fill:#2f3b56} .bd-field .ar-rim{fill:#ffb15e; opacity:.85}
.bd-field .ar-skin{fill:#e8b98c} .bd-field .ar-plume{fill:#c8444a}
/* Steel, wood and hide: a blade catches the low sun, a haft does not, and a bowstring is one hair wide. */
.bd-field .ar-steel{fill:#dfe7f2} .bd-field .ar-sword{fill:#dfe7f2}
.bd-field .ar-grip{fill:#6b4a2a} .bd-field .ar-boot{fill:#12192a}
.bd-field .ar-bow{fill:none; stroke:#8a5a2a; stroke-width:5; stroke-linecap:round}
.bd-field .ar-string{stroke:#e9dcc4; stroke-width:1.6; fill:none}
.bd-field .ar-shaft2{stroke:#e9dcc4; stroke-width:3; fill:none; stroke-linecap:round}
.bd-field .ar-sh{fill:#3a4763} .bd-field .ar-shb{fill:#c8a24a}
.bd-field .ar-shadow{fill:#12261c; opacity:.45}
.bd-field .ar-shaft, .bd-field .ar-kshaft{stroke:#e9dcc4; stroke-width:3; fill:none}
.bd-field .ar-fl, .bd-field .ar-kfl{fill:#c8444a}
.bd-field .ar-kdk{fill:#101a16}
.bd-field .ar-stuck{opacity:.8}
.bd-field .ar-grass path{stroke:#16301f; stroke-width:5; fill:none; stroke-linecap:round}
.bd-field .ar-blade{transform-origin:50% 100%; animation:arGrass 4.6s ease-in-out infinite alternate; animation-delay:var(--d, 0s)}
@keyframes arGrass{from{transform:rotate(-3deg)} to{transform:rotate(4deg)}}
.bd-field .ar-clouds{opacity:.5} .bd-field .ar-cloud{fill:#f6c9a2; animation:arCloud 90s linear infinite}
@keyframes arCloud{from{transform:translateX(-160px)} to{transform:translateX(1360px)}}
/* the run cycle: a bob, a lean into the hill and the sword arm working */
.bd-field .ar-body{transform-box:fill-box; transform-origin:50% 100%; animation:arRun .58s ease-in-out infinite; animation-delay:var(--d, 0s)}
@keyframes arRun{0%,100%{transform:translateY(0) rotate(-2deg)} 50%{transform:translateY(-7px) rotate(2deg)}}
.bd-field .ar-shield{transform-box:fill-box; transform-origin:100% 50%; animation:arShield 1.16s ease-in-out infinite; animation-delay:var(--d, 0s)}
@keyframes arShield{0%,100%{transform:translateX(0)} 50%{transform:translateX(-5px)}}
/* down: he goes over backwards and stays down until the next wave */
.bd-field .ar-run.hit{animation:arFall .7s cubic-bezier(.3,.9,.4,1) forwards}
.bd-field .ar-run.hit .ar-body, .bd-field .ar-run.hit .ar-shield{animation:none}
@keyframes arFall{to{transform:translate(var(--fx, 0), 26px) rotate(-74deg) scale(.96); opacity:.35}}
/* an arrow of ours, away down the slope */
.bd-field .ar-shot{animation:arShot .7s cubic-bezier(.2,.6,.5,1) forwards}
@keyframes arShot{0%{transform:translate(140px, 560px) scale(.7); opacity:0}
  12%{opacity:1} 100%{transform:translate(calc(140px + var(--tx)), calc(560px + var(--ty))) scale(1); opacity:0}}
@media (prefers-reduced-motion:reduce){ .bd-field *{animation:none !important} }

/* BERMUDA: a dark sea, three swells rolling at their own speeds, a boat with three men on it, and
   everything under and over the water that wants them. The slow things stay slow — a serpent you are not
   sure you saw is the triangle, a serpent that leaps is a cartoon — and only a thrown dart is allowed to
   make anything move fast. */
/* The deep: minute-long crossings, right to left, well behind the near swell. */
.bm-deep{animation:bmDeep var(--d, 60s) linear infinite}
@keyframes bmDeep{from{transform:translateX(1420px)} to{transform:translateX(-420px)}}
.bm-deep-body{animation:bmDeepRoll 9s ease-in-out infinite alternate}
@keyframes bmDeepRoll{from{transform:translateY(0)} to{transform:translateY(7px)}}
/* The boat rides the swell on the outer group; everything that happens TO it lands on the one inside, so a
   jolt never stops it bobbing. */
.bm-boat{animation:bmBob 6.5s ease-in-out infinite}
/* THE WATER UNDER HER IS WATER (Cory, 2026-09-26: "stop tilting the light colored water below the ship
   like it was part of the ship"). The wake was inside the group that rocks, so every roll of the hull
   tipped the sea with it. It sits outside the boat now and only breathes. */
.bm-wake{animation:bmWake 7.5s ease-in-out infinite alternate}
@keyframes bmWake{from{opacity:.1; transform:scaleX(.97)} to{opacity:.17; transform:scaleX(1.03)}}
@keyframes bmBob{0%,100%{transform:translateY(0) rotate(-1.6deg)} 50%{transform:translateY(-7px) rotate(1.8deg)}}
.bm-win{fill:#ffca6a; opacity:.85; animation:bmWin 5s ease-in-out infinite}
@keyframes bmWin{40%{opacity:.85} 46%{opacity:.3} 52%{opacity:.85}}
.bm-lamp-glow{animation:bmLampG 3.4s ease-in-out infinite alternate}
@keyframes bmLampG{from{opacity:.55} to{opacity:1}}
/* The crew: braced, pikes out, never quite in step with each other. */
.bm-man{animation:bmBrace 3.4s ease-in-out infinite; animation-delay:var(--lag, 0s)}
@keyframes bmBrace{0%,100%{transform:rotate(-2deg)} 50%{transform:rotate(2.4deg)}}
.bm-arm{transform-box:fill-box; transform-origin:0% 100%}
.bm-boat.swing .bm-arm{animation:bmSwing .5s ease-in-out}
@keyframes bmSwing{0%{transform:rotate(0)} 35%{transform:rotate(-38deg)} 70%{transform:rotate(26deg)} 100%{transform:rotate(0)}}
/* Damage carries through the turn: the mast leans, then the sail goes. */
.bm-boat.hurt1 .bm-rig{transform:rotate(-7deg); transform-box:fill-box; transform-origin:30% 100%; transition:transform .5s}
.bm-boat.hurt2 .bm-rig{transform:rotate(-15deg)}
.bm-boat.hurt2 .bm-sail{opacity:.35}
/* Answering a dart: held off, bitten, or taken. */
.bm-jolt{transform-box:fill-box; transform-origin:50% 90%}
.bm-jolt.fend{animation:bmRock .85s cubic-bezier(.3,1.4,.4,1)}
@keyframes bmRock{0%{transform:rotate(0)} 25%{transform:rotate(5deg)} 55%{transform:rotate(-3.5deg)} 80%{transform:rotate(1.5deg)} 100%{transform:rotate(0)}}
.bm-jolt.bitten{animation:bmBitten 1.05s cubic-bezier(.25,1.2,.4,1)}
@keyframes bmBitten{0%{transform:rotate(0) translate(0,0)} 18%{transform:rotate(-13deg) translate(-12px,6px)}
  50%{transform:rotate(9deg) translate(8px,-3px)} 75%{transform:rotate(-4deg)} 100%{transform:rotate(0) translate(0,0)}}
/* IN HALF. The two hulls were drawn apart from the start; each turns about the edge they share and goes
   down its own way, and the men go with the deck they were standing on. */
.bm-half{transform-box:fill-box; transition:transform .1s}
.bm-boat.snapped .bm-half.stern, .bm-boat.zapped .bm-half.stern{transform-origin:100% 40%; animation:bmSinkL 2.6s cubic-bezier(.4,0,.7,1) forwards}
.bm-boat.snapped .bm-half.bow, .bm-boat.zapped .bm-half.bow{transform-origin:0% 40%; animation:bmSinkR 2.6s cubic-bezier(.4,0,.7,1) forwards}
@keyframes bmSinkL{0%{transform:rotate(0) translate(0,0); opacity:1} 16%{transform:rotate(-16deg) translate(-16px,-8px)}
  100%{transform:rotate(-42deg) translate(-54px,120px); opacity:0}}
@keyframes bmSinkR{0%{transform:rotate(0) translate(0,0); opacity:1} 16%{transform:rotate(19deg) translate(18px,-10px)}
  100%{transform:rotate(48deg) translate(62px,126px); opacity:0}}
.bm-boat.snapped .bm-man, .bm-boat.zapped .bm-man{animation:bmTumble 1.6s ease-in forwards}
@keyframes bmTumble{to{transform:rotate(200deg) translate(14px, 64px); opacity:0}}
.bm-boat.zapped .bm-half{filter:drop-shadow(0 0 14px rgba(150,255,220,.85))}
/* And another boat rows in out of the dark, because there is another round to play. */
.bm-boat.arriving{animation:bmBob 6.5s ease-in-out infinite, bmRowIn 2.1s ease-out}
@keyframes bmRowIn{from{transform:translateX(-420px); opacity:0} 40%{opacity:1} to{transform:translateX(0); opacity:1}}
/* Each serpent rides its own long cycle: mostly under, up for a few seconds, down again. */
.bm-wave{transform-box:view-box; animation:bmWave linear infinite}
.bm-wave.w1{animation-duration:19s} .bm-wave.w2{animation-duration:13s} .bm-wave.w3{animation-duration:9s}
@keyframes bmWave{to{transform:translateX(200px)}}
/* Each one rides its own long cycle: mostly under, up for a few seconds, down again. */
.bm-fig{animation:bmRise var(--d, 18s) ease-in-out infinite}
@keyframes bmRise{
  0%, 58%{transform:translateY(74px); opacity:0}
  66%{opacity:.9}
  72%, 84%{transform:translateY(0); opacity:1}
  94%, 100%{transform:translateY(74px); opacity:0}
}
.bm-fig .bm-eye{fill:#ffd06a; opacity:.9; animation:bmEye 4.4s ease-in-out infinite}
@keyframes bmEye{45%{opacity:.9} 50%{opacity:.15} 55%{opacity:.9}}
/* A dart on target sends one at the boat and it is beaten off; a miss lets it get its head in. --to is
   which side of the boat it lives on, so both of them lean the right way. */
.bm-fig.lunge{animation:bmLunge 1.5s cubic-bezier(.2,.9,.3,1)}
@keyframes bmLunge{0%{transform:translateY(70px); opacity:0} 22%{transform:translateY(-14px) rotate(calc(var(--to, 1) * 7deg)); opacity:1}
  46%{transform:translateY(-22px) rotate(calc(var(--to, 1) * 15deg))} 70%{transform:translateY(4px) rotate(calc(var(--to, 1) * -6deg)); opacity:1}
  100%{transform:translateY(74px); opacity:0}}
.bm-fig.strike{animation:bmStrike 1.4s cubic-bezier(.3,1.2,.4,1)}
@keyframes bmStrike{0%{transform:translateY(74px); opacity:0} 18%{transform:translateY(-58px) rotate(calc(var(--to, 1) * 20deg)); opacity:1}
  40%{transform:translateY(-70px) rotate(calc(var(--to, 1) * 26deg))} 62%{transform:translateY(-18px) rotate(calc(var(--to, 1) * 4deg))}
  100%{transform:translateY(74px); opacity:0}}
.bm-fig.lunge .bm-eye, .bm-fig.strike .bm-eye{fill:#ffe9a8; opacity:1; animation:none}
.bm-fig.under{animation:none; transform:translateY(96px); opacity:0; transition:transform .6s cubic-bezier(.4,0,.8,.4), opacity .5s}
/* THE ONE THAT TAKES THE BOAT: up out of the water under it, holds while the hull goes, and back down. */
.bm-kraken{opacity:0}
.bm-kraken .bm-eye{fill:#ffd06a; opacity:.95}
.bm-kraken .bm-serp{filter:drop-shadow(0 0 18px rgba(0,0,0,.8))}
.bm-kraken.up{animation:bmKraken 3s cubic-bezier(.25,1,.4,1)}
@keyframes bmKraken{0%{transform:translateY(120px); opacity:0} 14%{transform:translateY(-150px); opacity:1}
  46%{transform:translateY(-186px) rotate(4deg); opacity:1} 70%{transform:translateY(-120px) rotate(-3deg); opacity:.9}
  100%{transform:translateY(140px); opacity:0}}
/* ---------- the crew, working ---------- */
/* Every man walks his own beat: a few seconds at a station doing the job there, then along the deck to the
   next one. The pauses are long and the walks are short, so at any moment most of them are working and one
   is moving - which is what a deck looks like. Each has his own clock, so they never move together. */
.bm-haul-arm{transform-box:fill-box; transform-origin:100% 100%; animation:bmHaul 1.9s ease-in-out infinite}
@keyframes bmHaul{0%,100%{transform:rotate(0) translateY(0)} 45%{transform:rotate(-24deg) translateY(4px)}}
.bm-halyard{opacity:.7}
/* the ARM on the tiller, not the man at it: they had the same class and he inherited the sweep */
.bm-tiller-arm{transform-box:fill-box; transform-origin:100% 0%; animation:bmTiller 5.2s ease-in-out infinite}
@keyframes bmTiller{0%,100%{transform:rotate(0)} 50%{transform:rotate(-16deg)}}
.bm-legs{transform-box:fill-box; transform-origin:50% 0%}
/* the hairline of light down the outside of every arm, so a black limb still reads against a black man */
.bm-halo{stroke:#ffffff; stroke-width:5.2; stroke-linecap:round; stroke-linejoin:round; fill:#ffffff; opacity:.34}
/* the beats: three different routes, so nobody shadows anybody */
.bm-haul{animation:bmBrace 3.4s ease-in-out infinite, bmBeatA 16s ease-in-out infinite}
.bm-man.bm-tiller{animation:bmBrace 3.4s ease-in-out infinite, bmBeatB 21s ease-in-out infinite}
.bm-deck{animation:bmBrace 3.4s ease-in-out infinite, bmBeatC 13s ease-in-out infinite}
@keyframes bmBeatA{0%,26%{transform:translateX(0)} 34%,58%{transform:translateX(-34px)} 66%,92%{transform:translateX(-14px)} 100%{transform:translateX(0)}}
@keyframes bmBeatB{0%,30%{transform:translateX(0)} 38%,62%{transform:translateX(26px)} 70%,94%{transform:translateX(10px)} 100%{transform:translateX(0)}}
@keyframes bmBeatC{0%,22%{transform:translateX(0)} 30%,50%{transform:translateX(30px)} 58%,80%{transform:translateX(-22px)} 88%,100%{transform:translateX(0)}}
/* legs only move while he is moving: the walk is on the same clock as the beat */
.bm-haul .bm-legs{animation:bmStepA 16s linear infinite}
.bm-man.bm-tiller .bm-legs{animation:bmStepB 21s linear infinite}
.bm-deck .bm-legs{animation:bmStepC 13s linear infinite}
@keyframes bmStepA{0%,26%,34%,58%,66%,92%,100%{transform:scaleY(1)} 30%,62%,96%{transform:scaleY(.86)}}
@keyframes bmStepB{0%,30%,38%,62%,70%,94%,100%{transform:scaleY(1)} 34%,66%,97%{transform:scaleY(.86)}}
@keyframes bmStepC{0%,22%,30%,50%,58%,80%,88%,100%{transform:scaleY(1)} 26%,54%,84%{transform:scaleY(.86)}}
/* A MISS SENDS THEM RUNNING (Cory, 2026-09-26: "make them run around the ship more, especially during a
   miss"): the same beats, four times the speed, for as long as it takes to get her back. */
.bm-boat.scramble .bm-haul, .bm-boat.scramble .bm-man.bm-tiller, .bm-boat.scramble .bm-deck{animation-duration:3.4s, 1.5s}
.bm-boat.scramble .bm-legs{animation-duration:1.5s}
/* AND THEY SEE IT COMING: arms up, backing off the rail, until whatever it is arrives. */
.bm-boat.scared .bm-man{animation:bmBrace 3.4s ease-in-out infinite, bmFright .5s ease-in-out infinite}
@keyframes bmFright{0%,100%{transform:translate(0,0) rotate(0)} 50%{transform:translate(-3px,-2px) rotate(-5deg)}}
.bm-boat.scared .bm-arm{animation:bmFrightArm .5s ease-in-out infinite}
@keyframes bmFrightArm{0%,100%{transform:rotate(-52deg)} 50%{transform:rotate(-70deg)}}

/* ---------- the shark, and the squid ---------- */
.bm-shark{opacity:.55; animation:bmSharkRun 46s linear infinite}
@keyframes bmSharkRun{0%{transform:translate(-220px,470px)} 100%{transform:translate(1420px,470px)}}
.bm-shark-in{animation:bmSharkRoll 4.5s ease-in-out infinite alternate}
@keyframes bmSharkRoll{to{transform:translateY(-7px) rotate(-3deg)}}
.bm-squid{opacity:.62; transform:translate(1010px, 560px); animation:bmSquidRise 23s ease-in-out infinite}
@keyframes bmSquidRise{0%,62%,100%{transform:translate(1010px,586px)} 74%{transform:translate(1010px,506px)} 88%{transform:translate(1010px,540px)}}
.bm-squid-in .bm-arm1{transform-box:fill-box; transform-origin:50% 100%; animation:bmCurl 6.5s ease-in-out infinite alternate}
.bm-squid-in .bm-arm2{transform-box:fill-box; transform-origin:0% 100%; animation:bmCurl 5.1s ease-in-out infinite alternate-reverse}
.bm-squid-in .bm-arm3{transform-box:fill-box; transform-origin:100% 100%; animation:bmCurl 7.4s ease-in-out infinite alternate}
@keyframes bmCurl{from{transform:rotate(-7deg)} to{transform:rotate(9deg)}}
.bm-squid .bm-eye{fill:#ffd06a; opacity:.85}

/* ---------- the pirate on the horizon ---------- */
.bm-pirate{opacity:.94}
.bm-prig{stroke:#0a2036; stroke-width:1.3; fill:none; opacity:.75}
.bm-pirate-in{transform-box:fill-box; transform-origin:50% 100%; animation:bmPirateRide 11s ease-in-out infinite alternate}
@keyframes bmPirateRide{from{transform:translateY(0) rotate(-1.4deg)} to{transform:translateY(-5px) rotate(1.4deg)}}
.bm-plamp{animation:bmLampG 4.2s ease-in-out infinite alternate}
/* the guns: the burst punches out to port and hangs there as smoke, and she is shoved off her aim */
.bm-flash{opacity:0; transform-box:fill-box; transform-origin:100% 50%}
.bm-flash.pop{animation:bmGunFlash .85s ease-out}
@keyframes bmGunFlash{
  0%{opacity:0; transform:translateX(10px) scale(.4)}
  9%{opacity:1; transform:translateX(0) scale(1.2)}
  28%{opacity:.85; transform:translateX(-7px) scale(1.05)}
  100%{opacity:0; transform:translateX(-30px) scale(1.8)}}
.bm-pirate.fire .bm-pirate-in{animation:bmRecoil .7s cubic-bezier(.2,1.4,.4,1)}
@keyframes bmRecoil{0%{transform:translateX(0)} 22%{transform:translateX(10px) rotate(2.6deg)} 100%{transform:translateX(0)}}
/* THE SHOT, ON A REAL LOB. Linear, with the arc written into the keyframes rather than into an easing
   curve: a cannonball crosses the ground at a steady rate and only its HEIGHT changes, so a single ease
   makes it look like it is being dragged on a wire. It grows the whole way, because it is coming at you. */
.bm-ball{opacity:0}
.bm-ball.fly{animation:bmBall 1.25s linear}
@keyframes bmBall{
  0%{opacity:0; transform:translate(756px,408px) scale(.5)}
  8%{opacity:1; transform:translate(742px,382px) scale(.64)}
  26%{transform:translate(720px,344px) scale(.82)}
  46%{transform:translate(696px,326px) scale(.98)}
  66%{transform:translate(672px,352px) scale(1.14)}
  85%{transform:translate(646px,412px) scale(1.32)}
  100%{opacity:1; transform:translate(620px,486px) scale(1.5)}}
/* and where it lands: splinters, not a splash - the water gets its own a beat later */
.bm-hit{opacity:0; transform-box:fill-box; transform-origin:50% 50%}
.bm-hit.pop{animation:bmHit .55s ease-out}
@keyframes bmHit{0%{opacity:0; transform:scale(.25)} 14%{opacity:1; transform:scale(1)} 100%{opacity:0; transform:scale(1.85)}}
@media (prefers-reduced-motion:reduce){ .bm-shark, .bm-squid, .bm-pirate-in, .bm-haul-arm, .bm-haul, .bm-tiller, .bm-deck, .bm-squid-in > *{animation:none} }

.bm-splash{animation:bmSplash .9s ease-out forwards}
@keyframes bmSplash{from{transform-box:fill-box; opacity:1} to{opacity:0}}
/* THE OTHER THING IN THE TRIANGLE (Cory, 2026-09-21: "make an alien ship appear and zip off in the distance
   once in a while" — and "have an alien zap it in half sometimes"). Roaming is the long idle cycle: nothing
   for the best part of a minute, then it is there, hangs a moment and is gone into the distance. Attacking
   replaces that cycle outright — it comes down over the boat, cuts it, and leaves. */
.bm-ufo{opacity:0}
.bm-ufo.roam{animation:bmUfoRoam 52s linear infinite}
@keyframes bmUfoRoam{
  0%, 62%{transform:translate(200px, 120px) scale(.5); opacity:0}
  66%{transform:translate(300px, 150px) scale(.85); opacity:.95}
  72%, 78%{transform:translate(430px, 138px) scale(.9); opacity:.95}
  83%{transform:translate(760px, 196px) scale(.6); opacity:.9}
  87%{transform:translate(1180px, 268px) scale(.12); opacity:0}
  100%{transform:translate(1180px, 268px) scale(.12); opacity:0}}
.bm-ufo.attack{animation:bmUfoAttack 3.4s cubic-bezier(.3,.9,.4,1)}
@keyframes bmUfoAttack{
  0%{transform:translate(1280px, 150px) scale(.2); opacity:0}
  14%{transform:translate(700px, 250px) scale(1); opacity:1}
  24%, 46%{transform:translate(566px, 250px) scale(1); opacity:1}
  62%{transform:translate(562px, 244px) scale(1); opacity:1}
  84%{transform:translate(120px, 150px) scale(.5); opacity:.9}
  100%{transform:translate(-320px, 90px) scale(.1); opacity:0}}
.bm-ufo-in{animation:bmUfoWob 3.2s ease-in-out infinite alternate}
@keyframes bmUfoWob{from{transform:rotate(-3deg)} to{transform:rotate(3deg)}}
.bm-ufo-lights circle{animation:bmUfoLight 1.6s ease-in-out infinite}
@keyframes bmUfoLight{0%,100%{opacity:.25} 50%{opacity:1}}
/* The beam only exists while it is firing. */
.bm-beam{opacity:0; transform-box:fill-box; transform-origin:50% 0%}
.bm-ufo.attack .bm-beam{animation:bmZap 3.4s linear}
@keyframes bmZap{0%, 30%{opacity:0; transform:scaleY(.2)} 36%{opacity:.35; transform:scaleY(1)}
  40%{opacity:1; transform:scaleY(1)} 44%{opacity:.5} 48%{opacity:1} 60%{opacity:0; transform:scaleY(1)} 100%{opacity:0}}
.bm-ufo.attack .bm-ufo-hull{animation:bmUfoCharge 3.4s linear}
@keyframes bmUfoCharge{0%,28%{fill:#0c1f33} 38%{fill:#6cffcf} 48%{fill:#b9ffe6} 60%,100%{fill:#0c1f33}}
@media (prefers-reduced-motion:reduce){
  .bm-wave, .bm-fig, .bm-fig .bm-eye, .bm-deep, .bm-deep-body, .bm-boat, .bm-man, .bm-ufo.roam, .bm-ufo-in, .bm-shark, .bm-squid,
  .bm-legs, .bm-haul-arm, .bm-tiller-arm, .bm-wake{animation:none}
}
/* the wake at rest; its breathing overrides this while it runs */
.bd-sea .bm-wake{opacity:.13}

/* SNIPER: the world pans under a fixed cross, the way a real scope does (Cory, 2026-09-18). The swing onto a
   target is quick — it has to land before the shot does. */
#snWorld{transition:transform .32s cubic-bezier(.2,.85,.25,1)}
/* the glass is a round clip over the scope's own units (js BACKDROPS.sniper); the world pans inside it */
.bd-scope .sn-glass{border-radius:50%; overflow:hidden}
/* Each shot: the ring round the centre goes red and kicks once. */
.sn-lock .sn-ring{transition:stroke .2s, stroke-width .2s}
.sn-lock.locked .sn-ring{stroke:#ff3b3b; stroke-width:4; filter:drop-shadow(0 0 8px rgba(255,60,60,.9));
  transform-box:fill-box; transform-origin:center; animation:snRing .45s .28s ease-out}
@keyframes snRing{50%{transform:scale(1.35)}}
@media (prefers-reduced-motion:reduce){ #snWorld{transition:none} }
/* Everybody walks LEFT on one shared loop (numeral.js SN: 1400 → -200 over the same --d). */
.sn-fig{animation:snWalk var(--d, 36s) linear infinite}
@keyframes snWalk{from{transform:translateX(1400px)} to{transform:translateX(-200px)}}
/* The score sits inside the scope's circle, and the circle is as big as the panel's SHORT side. Sized off the
   width alone, a four-figure score on a panel wider than it is tall ran out past the glass (Cory, 2026-10-19,
   on the phone screenshot: "points cut off... not a good look"). */
.scorep.sniper .bigscore{font-size:min(clamp(80px, 24vh, 230px), calc(min(92cqw, 70cqh) / (var(--chars, 3) * .68)), 52cqh)}
/* While shots are landing the score steps back so the takedown under the cross can be seen. */
.scorep.scened > .bigscore, .scorep.scened > .checkout{transition:opacity .25s}
.scorep.sn-shot > .eyebrow, .scorep.sn-shot > .bigscore, .scorep.sn-shot > .checkout{opacity:.14}
/* What a shot leaves: the man falls where he stood, lies there a while, and fades. */
.sn-corpse{animation:snFade 9s linear forwards}
.sn-corpse .sn-leg{animation:none}
.sn-corpse .sn-body{transform-box:fill-box; transform-origin:50% 100%; animation:snDrop .7s cubic-bezier(.3,.9,.4,1) forwards}
@keyframes snFade{0%, 80%{opacity:1} 100%{opacity:0}}
.sn-leg{transform-box:fill-box; transform-origin:50% 0; animation:snStepA .62s ease-in-out infinite alternate; animation-delay:var(--lag, 0s)}
.sn-leg.b{animation-name:snStepB}
@keyframes snStepA{from{transform:rotate(-15deg)} to{transform:rotate(15deg)}}
@keyframes snStepB{from{transform:rotate(15deg)} to{transform:rotate(-15deg)}}
.sn-fig.down .sn-body{transform-box:fill-box; transform-origin:50% 100%; animation:snDrop .7s cubic-bezier(.3,.9,.4,1) forwards}
.sn-fig.down .sn-leg{animation:none}
@keyframes snDrop{60%{transform:rotate(78deg) translateY(4px)} 100%{transform:rotate(86deg) translateY(6px); opacity:.35}}
.sn-spark{animation:snSpark .7s ease-out forwards}
@keyframes snSpark{0%{opacity:1; transform:scale(.5)} 100%{opacity:0; transform:scale(1.5)}}
@media (prefers-reduced-motion:reduce){ .sn-fig, .sn-leg{animation:none} }

/* NUMBERS SIZED BY THEIR OWN BOX (2026-09-18). Tic Tac Toe and Two Line sized their numbers by the screen's
   height, so on a phone upright — boxes a third the size, height much the same — "BULL" and "16" spilled into
   the next box. Each box is now its own size container and the number is capped to it. */
.tl-box, .ttt-cell{container-type:size}
.tl-box .tl-n{font-size:min(clamp(18px, 5.2vh, 44px), 52cqmin)}
.ttt-n{font-size:min(clamp(18px, 4.6vh, 40px), 30cqw)}
@media (max-width:600px){
  .tl-wrap{padding:8px; gap:8px}
  .tl-grid{gap:4px}
  .tl-box{border-radius:8px}
  .tl-lc{grid-template-columns:auto minmax(0,1fr) auto; padding:5px 8px; gap:6px; border-radius:12px}
  .tl-lc small{display:none}
}

/* HUNTER ON A SMALL SCREEN (Cory, 2026-09-18): no "Race to 301" pill — the number is on every vial's cap —
   and the vials run the full height of their panel, from just under the score to just over the name plate. */
@media (max-width:600px), (max-height:500px){
  .hv-stage{padding:10px 8px 8px; gap:6px}
  .hv-goal{display:none}
  .hv-stage .checkout:empty{display:none}
  .hv-row{gap:6px}
  .hv{gap:4px}
  .hv-score{font-size:clamp(18px, 4.2vh, 34px)}
  .hv-tube{min-height:0}
  .hv-plate{padding:3px 7px 3px 3px; gap:4px; font-size:12px; max-width:calc(100% + 6px)}
  /* The vials are the scoreboard here, so on a phone they get the whole width; the thrower panel goes. */
  #scr-game[data-view="hunter"] .x-main{grid-template-columns:minmax(0,1fr)}
  #scr-game[data-view="hunter"] .panel.rounds{display:none}
}
/* A phone on its side leaves the vials ~70px tall: slim them and thin the brass so they still read as vials,
   and drop the stem that poked up into the score. */
@media (max-height:500px){
  .hv-tube{width:min(100%, 46px)}
  .hv-tube::before{display:none}
  .hv-cap, .hv-base{height:13px; left:-5px; right:-5px; border-radius:4px}
  .hv-cap span{font-size:9px}
  .hv-glass{top:10px; bottom:10px}
}

/* MONSTER BASH ON A PHONE (2026-09-18): four HP cards at ~80px each crushed the names into the HP number, and
   the rule pills ran off the side and over the monsters. Cards keep the badge, the HP and the bar (the
   monsters on stage say who is who); the pills shrink and wrap inside the arena.

   SHORT COUNTS AS SMALL (Cory, 2026-09-20, a landscape photo of four full-size cards sitting on top of the
   arena). All of this used to ask about width alone, and a phone on its side is 844 wide and 390 tall, so
   none of it fired: the panel had 140px of height and the bars took half of it. */
@media (max-width:600px), (max-height:560px){
  .mon-hud{left:8px; right:8px; top:8px; gap:6px}
  .mon-hp{padding:6px 8px; border-radius:12px}
  .mon-hp-top{margin-bottom:5px; gap:6px}
  .mon-row strong{font-size:18px}
  .mon-hp-top u{font-size:13px}
  .mon-bar{height:11px; gap:1.5px}
  .mon-name, .mon-hud.n3 .mon-name, .mon-hud.n4 .mon-name{font-size:16px; margin-top:4px}
  /* The chips must never make this row taller than the bar it sits on: the arena on a phone is about a
     hundred pixels and every pixel the cards take is one the monsters lose. */
  .mon-row{gap:5px}
  .mon-nums{gap:3px}
  .mon-nums i{font-size:14px; padding:0 6px; line-height:18px; border-radius:5px}
  /* the pills get a strip of their own under the arena: the drawing stops 40px short of the bottom */
  .mon-scene > svg{height:calc(100% - 40px)}
  .mt{padding:4px 9px; font-size:10px; letter-spacing:.08em}
  .mt b{font-size:15px; margin-left:4px}
  .mt.crit b{font-size:12px}
}
/* THREE OR FOUR CARDS ACROSS A PHONE IS 92px EACH, and a card carries health, a tick bar, the three
   numbers that hurt them and their points — 110px of content. Two columns rather than four while the panel
   is TALL enough to pay for the second row, which it is now the duplicate scoreboard underneath has gone.
   Monster Bash is four players at most, so this is only ever two rows. */
@media (max-width:600px) and (min-height:561px){
  .mon-hud.n3, .mon-hud.n4{grid-template-columns:1fr 1fr}
}
/* And when the box is short, the cards go in ONE row whatever the width: a second row over a 266px panel
   took more than half of it and left the monsters an inch tall. The seat badge goes with it — the card is
   already outlined and lit in that player's colour, which says whose it is without spending 22px on it. */
@media (max-height:560px){
  .mon-hp-top u{font-size:11px}
  .mon-hp{padding:5px 7px}
  .mon-hud{grid-template-columns:repeat(var(--n,2), minmax(0,1fr)); gap:5px; top:6px; left:6px; right:6px}
  .mon-hp{padding:4px 7px; border-radius:10px}
  .mon-hp-top{margin-bottom:4px; gap:5px}
  .mon-row strong{font-size:16px}
  .mon-hp-top u{font-size:11px}
  .mon-bar{height:8px; gap:1px}
  .mon-name, .mon-hud.n3 .mon-name, .mon-hud.n4 .mon-name{font-size:13px; margin-top:3px}
  /* A landscape phone gives each of four cards about 116px, and the top row has to hold three numbers and
     a points total inside it. Measured to fit rather than guessed at: anything larger overflows. */
  .mon-row{gap:4px}
  .mon-hp-top{gap:4px; margin-bottom:4px}
  .mon-nums{gap:2px}
  .mon-nums i{font-size:11px; padding:0 4px; line-height:14px; border-radius:4px}
  .mon-hp-top u{font-size:10px}
  .mon-scene > svg{height:calc(100% - 32px)}
}

/* A phone on its side leaves the game about 130px of height: too short for the thrower panel's numbers (they
   were cut off mid-label), so the panel steps aside and the score takes the whole width. */
@media (max-height:500px){
  .x-main{grid-template-columns:minmax(0,1fr)}
  .x-main > .panel.rounds{display:none}
}
/* On a short globe (a phone on its side) the stacked zoom pair reached up into the sign; side by side, smaller. */
@media (max-height:500px){
  .globe-zoom{flex-direction:row; gap:6px; right:8px; bottom:8px}
  .globe-zoom .gz{width:32px; height:32px}
}

/* ---------- LAST ONE STANDING: the top of the show (Cory, 2026-09-20) ----------
   The camera flies in: the stage starts far off and tilted, the banner drops in over it, and the whole thing
   settles at exactly the size it plays at, so the intro's last frame is the game's first. The title card
   rides in with it and gets out of the way.

   THE TITLE CARD (Cory, 2026-09-20: "broadcast style letter and graphics as if it were the start of a
   gameshow. Maybe frame it in a retro TV shaped rounded rectangle"). A television set switches on over the
   dark stage, the titles slam into it, and it switches off the way an old set does — the picture collapsing
   to a line and then to a dot — which is the cue for the camera to start its run. */
.lo-stage.intro .lo-svg{animation:loFlyIn 1.6s 1.7s cubic-bezier(.34,.9,.3,1) both; transform-origin:50% 62%}
@keyframes loFlyIn{
  0%{transform:perspective(900px) translate3d(0,14%,-820px) rotateX(24deg) rotateZ(-6deg); opacity:0; filter:brightness(1.7) blur(3px)}
  22%{opacity:1; filter:brightness(1.35) blur(1px)}
  70%{transform:perspective(900px) translate3d(0,2%,-90px) rotateX(5deg) rotateZ(-1deg)}
  100%{transform:none; opacity:1; filter:none}
}
.lo-stage.intro .lo-banner{animation:loBannerIn 1.1s 1.85s cubic-bezier(.2,1.4,.4,1) both}
@keyframes loBannerIn{from{opacity:0; transform:translateY(-38px) scale(.9)}}
/* The house lights sweeping the room before anybody throws. */
.lo-stage.intro::after{content:""; position:absolute; inset:0; z-index:3; pointer-events:none;
  background:conic-gradient(from 210deg at 50% -10%, transparent 0deg, rgba(255,236,180,.16) 14deg, transparent 30deg, transparent 150deg, rgba(255,236,180,.14) 168deg, transparent 186deg);
  animation:loSweep 1.6s 1.7s ease-in-out both}
@keyframes loSweep{0%{opacity:0; transform:rotate(-14deg)} 25%{opacity:1} 100%{opacity:0; transform:rotate(14deg)}}

/* THE WHOLE SCREEN IS THE TUBE (Cory, 2026-09-20: "The TV needs to be full screen, all the way to the edges
   that way it looks like a real retro tv. Like the Nintendo wii did on it's interface"). Not a set drawn in
   the middle of the page — the glass itself: corner to corner, rounded like a tube, scanlines and an
   aperture grille over the picture, a band rolling down it, and the corners falling away into the dark.
   Built at the top of the document when the show starts and thrown away after (js/views/lastone.js). */
.crt{position:fixed; inset:0; z-index:90; overflow:hidden; background:#04030a;
  animation:crtRoom 2.2s both}
@keyframes crtRoom{0%{opacity:1} 96%{opacity:1} 100%{opacity:0}}
/* The picture. A tube does not cut to black: it opens from a white line and it dies back into one. */
.crt-pic{position:absolute; inset:0; display:flex; align-items:center; justify-content:center; transform-origin:50% 50%;
  background:radial-gradient(ellipse 120% 90% at 50% 42%, #3a2a86 0%, #1b1150 46%, #0a0722 100%);
  animation:crtSet 2.2s cubic-bezier(.3,0,.15,1) both}
@keyframes crtSet{
  0%{opacity:0; transform:scale(.04,.003); filter:brightness(7) saturate(0)}
  3%{opacity:1; transform:scale(1,.003); filter:brightness(7) saturate(0)}
  9%{transform:scale(1.01,1); filter:brightness(2.6) saturate(1.2)}
  15%{transform:scale(1,1); filter:brightness(1.1)}
  20%,74%{transform:scale(1,1); filter:none; opacity:1}
  80%{transform:scale(1.03,.014); filter:brightness(3.6) saturate(0); opacity:1}
  89%{transform:scale(.05,.01); filter:brightness(7) saturate(0); opacity:1}
  96%,100%{opacity:0; transform:scale(0,.008)}
}
/* The glass: rounded like a tube and dark in the corners, which is what makes a flat screen read as curved. */
.crt-glass{position:absolute; inset:0; z-index:6; pointer-events:none; border-radius:min(11vw,13vh) / min(9vw,11vh);
  box-shadow:inset 0 0 min(16vw,18vh) rgba(0,0,0,.92), inset 0 0 min(4vw,5vh) rgba(0,0,0,.8), 0 0 0 100vmax #04030a;
  background:linear-gradient(118deg, rgba(255,255,255,.1) 0 14%, rgba(255,255,255,0) 32%)}
.crt-scan{position:absolute; inset:0; z-index:4; pointer-events:none; opacity:.42;
  background:repeating-linear-gradient(180deg, rgba(0,0,0,.55) 0 2px, rgba(255,255,255,.03) 2px 4px)}
.crt-grille{position:absolute; inset:0; z-index:4; pointer-events:none; opacity:.3; mix-blend-mode:screen;
  background:repeating-linear-gradient(90deg, rgba(255,40,40,.5) 0 1px, rgba(40,255,90,.5) 1px 2px, rgba(60,90,255,.5) 2px 3px)}
/* The band an old set never quite settles down. */
.crt-roll{position:absolute; left:0; right:0; top:0; height:26%; z-index:5; pointer-events:none; opacity:.14;
  background:linear-gradient(180deg, rgba(255,255,255,0), rgba(255,255,255,.9) 55%, rgba(255,255,255,0));
  animation:crtRoll 3.2s linear infinite}
@keyframes crtRoll{from{transform:translateY(-120%)} to{transform:translateY(520%)}}
/* The titles, on air. */
.crt-copy{position:relative; z-index:3; padding:0 7vw; text-align:center; text-wrap:balance}
.crt-bull{display:block; margin:0 auto min(2vh,18px); width:min(13vh,14vw,120px); aspect-ratio:1; border-radius:50%;
  background:radial-gradient(circle, #ff3b5c 0 15%, #f2ead6 15% 31%, #ff3b5c 31% 47%, #f2ead6 47% 63%, #1c1540 63% 100%);
  box-shadow:0 0 0 .4vh rgba(255,231,163,.8), 0 0 5vh rgba(255,95,58,.65);
  animation:crtBull .5s .2s cubic-bezier(.2,1.5,.35,1) both}
@keyframes crtBull{from{opacity:0; transform:scale(.2) rotate(-120deg)}}
.crt-copy span{display:block; font-weight:800; font-size:min(2.6vh,3.4vw,26px); letter-spacing:.44em; color:#ffe7a3;
  text-shadow:0 0 2vh rgba(255,200,90,.6); animation:crtKick .45s .34s both}
@keyframes crtKick{from{opacity:0; letter-spacing:1em}}
.crt-copy b{display:block; margin:min(1.6vh,14px) 0; font-family:var(--num); font-weight:400; line-height:.92;
  font-size:min(13vh,13.5vw,150px); letter-spacing:.02em; color:#fff;
  text-shadow:0 0 3vh rgba(255,214,120,.6), 0 .7vh 0 #b8871f, 0 1.4vh 3vh rgba(0,0,0,.75);
  animation:crtSlam .42s .5s cubic-bezier(.2,1.5,.35,1) both}
@keyframes crtSlam{from{opacity:0; transform:scale(2.1); filter:blur(9px)}}
.crt-copy em{display:block; font-style:normal; font-weight:800; line-height:1.25;
  font-size:min(3.2vh,4.2vw,34px); letter-spacing:.04em; color:#bfe0ff;
  text-shadow:0 0 2vh rgba(90,160,255,.5); animation:crtLine .5s .8s cubic-bezier(.2,1,.3,1) both}
@keyframes crtLine{from{opacity:0; transform:translateY(2vh); clip-path:inset(0 100% 0 0)}}
/* Two light bars sweeping the picture, the way a title sequence does it. */
.crt-bars{position:absolute; inset:0; z-index:2; pointer-events:none;
  background:linear-gradient(104deg, rgba(255,255,255,0) 38%, rgba(255,255,255,.14) 44%, rgba(255,255,255,0) 48%,
    rgba(255,255,255,0) 56%, rgba(255,255,255,.09) 61%, rgba(255,255,255,0) 66%);
  animation:crtBars 1.5s .35s ease-out both}
@keyframes crtBars{from{transform:translateX(-60%)} to{transform:translateX(60%)}}
@media (prefers-reduced-motion:reduce){
  .lo-stage.intro .lo-svg, .lo-stage.intro .lo-banner, .lo-stage.intro::after,
  .crt, .crt-pic, .crt-bull, .crt-copy span, .crt-copy b, .crt-copy em, .crt-roll, .crt-bars{animation:none}
}

/* ---------- Hunter vials, made of glass ---------- */
/* Cory, 2026-09-28, with a reference shot: "make the vials and animations more like this image".
   The tube was a flat panel with one highlight painted down it. A cylinder is three things at once - dark
   at BOTH edges where the surface turns away, a bright core between them, and one narrow specular line
   that never moves however full it is. Round the ends, give the liquid a real meniscus instead of a flat
   top, and put the number on the glass the way the machines do, and it stops being a bar chart. */
.hv-glass{border-radius:999px; border-width:2.5px;
  background:linear-gradient(90deg, rgba(4,8,20,.55) 0%, rgba(255,255,255,.06) 14%, rgba(255,255,255,.17) 34%,
    rgba(255,255,255,.05) 56%, rgba(4,8,20,.35) 82%, rgba(4,8,20,.6) 100%)}
.hv-glass::after{left:19%; width:7%; top:5%; bottom:5%; border-radius:999px; filter:blur(.4px);
  background:linear-gradient(180deg, rgba(255,255,255,.78), rgba(255,255,255,.18) 38%, rgba(255,255,255,.44))}
/* the far edge catching it too, so it reads as round rather than lit from one side */
.hv-glass::before{content:""; position:absolute; top:6%; bottom:6%; right:12%; width:4%; border-radius:999px;
  background:linear-gradient(180deg, rgba(255,255,255,.3), rgba(255,255,255,.05)); z-index:1; pointer-events:none}
.hv-liquid{border-radius:0 0 999px 999px}
.hv-liquid::before{top:-7px; height:14px; opacity:.95;
  background:radial-gradient(ellipse at 50% 100%, color-mix(in srgb, var(--pc) 25%, #fff), color-mix(in srgb, var(--pc) 60%, #fff));
  box-shadow:0 0 16px color-mix(in srgb, var(--pc) 70%, transparent)}
/* The number rides the glass rather than sitting above it, which is where the eye already is. */
.hv{position:relative}
.hv-score{position:absolute; top:calc(50% - .58em); left:0; right:0; z-index:5; pointer-events:none;
  font-size:clamp(26px, 7vh, 64px);
  text-shadow:0 0 24px rgba(0,0,0,.9), 0 4px 0 rgba(0,0,0,.55), 0 0 36px color-mix(in srgb, var(--pc) 70%, transparent)}
/* And BOOM is a burst, not a caption. */
.hv-boom{top:30%; font-size:clamp(20px, 4vh, 34px)}
.hv-boom::before{content:""; position:absolute; left:50%; top:50%; width:200%; aspect-ratio:1; border-radius:50%;
  transform:translate(-50%,-50%); z-index:-1; opacity:0;
  background:radial-gradient(circle, rgba(255,244,190,.95) 0 6%, rgba(255,150,40,.5) 18%, transparent 62%),
    conic-gradient(from 0deg, transparent 0 4%, rgba(255,226,150,.85) 5%, transparent 6% 12%,
      rgba(255,226,150,.7) 13%, transparent 14% 20%, rgba(255,226,150,.85) 21%, transparent 22% 28%,
      rgba(255,226,150,.7) 29%, transparent 30% 37%, rgba(255,226,150,.85) 38%, transparent 39% 45%,
      rgba(255,226,150,.7) 46%, transparent 47% 54%, rgba(255,226,150,.85) 55%, transparent 56% 62%,
      rgba(255,226,150,.7) 63%, transparent 64% 71%, rgba(255,226,150,.85) 72%, transparent 73% 79%,
      rgba(255,226,150,.7) 80%, transparent 81% 88%, rgba(255,226,150,.85) 89%, transparent 90%)}
.hv-boom.go::before{animation:hvBurst 1.3s ease-out}
@keyframes hvBurst{
  0%{opacity:0; transform:translate(-50%,-50%) scale(.2) rotate(0)}
  18%{opacity:1; transform:translate(-50%,-50%) scale(1) rotate(12deg)}
  100%{opacity:0; transform:translate(-50%,-50%) scale(1.55) rotate(32deg)}}
@media (prefers-reduced-motion:reduce){ .hv-boom.go::before{animation:none} }

/* ---------- and the collars are machined, not brass ---------- */
/* Cory, 2026-09-28: "let's change the gold to something else. Something 80's. Maybe a more industrial
   looking top and bottom. Rivets." The brass belonged to an apothecary bottle; the backdrop behind these
   is a neon grid running to a synth sunset. So: brushed steel collars with a row of rivets struck through
   them, and one neon seam where each collar meets the glass - magenta at the top, cyan at the foot, which
   is the same pair the sunset behind them is made of. */
.hv-cap, .hv-base{
  background:linear-gradient(180deg, #949fab 0%, #e2eaf2 16%, #7b8794 48%, #3d4751 74%, #222a33 100%);
  border:1px solid #151b22; box-shadow:0 3px 10px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.55)}
.hv-cap{border-bottom:2px solid #ff2d9b; box-shadow:0 3px 10px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.55), 0 2px 12px rgba(255,45,155,.45)}
.hv-base{border-top:2px solid #39d5ff; box-shadow:0 3px 10px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.55), 0 -2px 12px rgba(57,213,255,.4)}
/* the rivets: struck through the collar at a fixed pitch, so a fat tube gets more of them and a thin one fewer */
.hv-cap::after, .hv-base::after{content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background-image:radial-gradient(circle at 7px 50%, rgba(255,255,255,.85) 0 1.1px, rgba(176,190,205,.95) 1.2px 2.5px,
    rgba(16,22,29,.9) 2.8px 3.3px, transparent 3.5px);
  background-size:16px 100%; background-repeat:repeat-x}
.hv-cap span{position:relative; z-index:1; color:#0d1420; opacity:.9; text-shadow:0 1px 0 rgba(255,255,255,.45)}
/* the stem on top is steel too */
.hv-tube::before{background:linear-gradient(90deg, #39424c, #cfd8e2 40%, #7b8794 62%, #2a333c)}

/* Straight sided, all the way up (Cory, 2026-09-28: "let's make the vials rectangles all the way up, I
   don't like the pill look"). The cylinder was in the SHADING, not in the rounded ends - dark at both
   edges, bright core, one specular line - so squaring them off costs nothing and the steel collars have
   something to sit flush on. */
.hv-glass{border-radius:4px}
.hv-liquid{border-radius:0}
.hv-cap, .hv-base{border-radius:3px}

/* No graduations (Cory, 2026-09-28: "remove the beaker style horizontal lines, they make it look too
   clinical"). A measuring cylinder belongs in a laboratory; this is a machine in an arcade. The glass
   carries it on its own - two dark turning edges, a bright core, a hard specular line down the near face
   and a soft one down the far one, and a sheen across the top and foot where the light wraps round. */
.hv-ticks{display:none}
.hv-glass{
  background:
    linear-gradient(180deg, rgba(255,255,255,.16), rgba(255,255,255,0) 20%, rgba(255,255,255,0) 76%, rgba(255,255,255,.09)),
    linear-gradient(90deg, rgba(4,8,20,.62) 0%, rgba(255,255,255,.05) 12%, rgba(255,255,255,.20) 32%,
      rgba(255,255,255,.04) 56%, rgba(4,8,20,.40) 80%, rgba(4,8,20,.68) 100%)}
/* and the liquid is lit through, rather than being a flat block of colour */
.hv-liquid{background:
  linear-gradient(180deg, rgba(255,255,255,.22), rgba(255,255,255,0) 26%),
  linear-gradient(90deg, color-mix(in srgb, var(--pc) 42%, #000), var(--pc) 34%,
    color-mix(in srgb, var(--pc) 45%, #fff) 50%, var(--pc) 68%, color-mix(in srgb, var(--pc) 42%, #000))}

/* ---------- the lens, when a vial goes ---------- */
/* We have been watching through a piece of glass all along; his vial has just thrown itself at it. */
.hv-lens{position:absolute; inset:0; z-index:9; pointer-events:none; overflow:hidden; border-radius:inherit}
.hv-lens-wash{position:absolute; inset:0; opacity:0;
  background:radial-gradient(circle at var(--ox, 50%) var(--oy, 45%),
    color-mix(in srgb, var(--pc) 70%, #fff) 0 6%, color-mix(in srgb, var(--pc) 60%, transparent) 26%, transparent 64%);
  animation:hvWash 1.5s cubic-bezier(.15,.8,.3,1) forwards}
@keyframes hvWash{0%{opacity:0; transform:scale(.3)} 12%{opacity:.95; transform:scale(1)} 100%{opacity:0; transform:scale(1.15)}}
/* the fracture, struck out of the point it hit */
.hv-lens-crack{position:absolute; left:var(--ox, 50%); top:var(--oy, 45%); width:190%; height:190%;
  transform:translate(-50%,-50%); overflow:visible}
.hv-lens-crack path{fill:none; stroke:rgba(255,255,255,.85); stroke-width:1.1; stroke-linecap:round;
  filter:drop-shadow(0 0 3px rgba(255,255,255,.7));
  stroke-dasharray:200; stroke-dashoffset:200; animation:hvCrack 1.5s ease-out forwards}
@keyframes hvCrack{0%{stroke-dashoffset:200; opacity:0} 8%{opacity:1} 26%{stroke-dashoffset:0; opacity:1} 100%{stroke-dashoffset:0; opacity:0}}
/* and the drops that stuck, each running at its own pace - one clock for all of them looks drawn */
.hv-lens-drops b{position:absolute; border-radius:52% 48% 46% 54% / 60% 58% 42% 40%; opacity:0;
  background:radial-gradient(ellipse at 34% 28%, rgba(255,255,255,.9), color-mix(in srgb, var(--pc) 72%, #fff) 34%,
    color-mix(in srgb, var(--pc) 85%, #000) 82%);
  box-shadow:inset 0 -2px 6px rgba(0,0,0,.35), 0 0 10px color-mix(in srgb, var(--pc) 60%, transparent);
  animation-name:hvDrop; animation-timing-function:cubic-bezier(.3,.1,.5,1); animation-fill-mode:forwards}
@keyframes hvDrop{
  0%{opacity:0; transform:translateY(-14px) scale(.3)}
  14%{opacity:.95; transform:translateY(0) scale(1)}
  55%{opacity:.9; transform:translateY(calc(var(--run) * .35)) scale(1)}
  100%{opacity:0; transform:translateY(var(--run)) scale(.86)}}
@media (prefers-reduced-motion:reduce){ .hv-lens{display:none} }

/* ---------- CRICKET COUNT UP: the city (js/views/numeral.js BACKDROPS.cricket) ---------- */
/* The city is one static picture on its own layer; only the rain sheet moves, as a whole, on the compositor. */
.bd-cyber{will-change:transform}
.bd .cy-rain{position:absolute; left:0; top:0; width:100%; height:200%; display:block; pointer-events:none; will-change:transform;
  animation:cyRainFall 1.15s linear infinite}
@keyframes cyRainFall{from{transform:translateY(-50%)} to{transform:translateY(0)}}
.bd-cyber .cy-city{font-family:var(--num); font-size:15px; text-anchor:middle; dominant-baseline:central; letter-spacing:.06em}
/* the tower: its name along the parapet, a floor a number, the round's floor burning white */
.bd-cyber .cy-crown{font-family:var(--num); font-size:24px; letter-spacing:.3em; text-anchor:middle; dominant-baseline:central; fill:#ff2d9b; filter:drop-shadow(0 0 6px #ff2d9b) drop-shadow(0 0 14px rgba(255,45,155,.6))}
.bd-cyber .cy-num{font-family:var(--num); font-size:32px; text-anchor:middle; dominant-baseline:central}
.bd-cyber .cy-floor .cy-signbox, .bd-cyber .cy-floor .cy-num{filter:drop-shadow(0 0 4px var(--c))}
.bd-cyber .cy-floor:not(.lit){opacity:.8}
.bd-cyber .cy-floor.lit .cy-glowbar{opacity:.2}
.bd-cyber .cy-floor.lit .cy-num{fill:#fff; filter:drop-shadow(0 0 6px var(--c)) drop-shadow(0 0 18px var(--c))}
.bd-cyber .cy-floor.lit .cy-signbox{filter:drop-shadow(0 0 8px var(--c)) drop-shadow(0 0 22px var(--c))}
.bd-cyber .cy-floor.lit .cy-win{opacity:.95}
/* a scoring dart lights the floor for a beat and shakes it */
.bd-cyber .cy-floor.hit .cy-glowbar{animation:cyHit .55s ease-out}
@keyframes cyHit{0%{opacity:.9} 100%{opacity:.2}}
.bd-cyber .cy-floor.hit{animation:cyShake .3s steps(3)}
@keyframes cyShake{0%{transform:translate(0,0)} 33%{transform:translate(-3px,0)} 66%{transform:translate(2px,0)} 100%{transform:translate(0,0)}}
/* the marks over the floor's windows: burnt on in white with the floor's glow */
.bd-cyber .cy-mark{fill:none; stroke:#fff; stroke-width:5; stroke-linecap:round; filter:drop-shadow(0 0 6px var(--c)) drop-shadow(0 1px 2px #000); animation:cyBurn .26s ease-out both}
@keyframes cyBurn{0%{opacity:0; transform:scale(2.6)} 60%{opacity:1; transform:scale(.92)} 100%{opacity:1; transform:scale(1)}}
/* a miss: the picture tears for a frame */
.bd-cyber .cy-glitch{pointer-events:none}
.bd-cyber.glitch{animation:cyTear .28s steps(3)}
@keyframes cyTear{0%{transform:translate(0,0)} 33%{transform:translate(-12px,0) skewX(4deg)} 66%{transform:translate(9px,0) skewX(-3deg)} 100%{transform:translate(0,0)}}
.bd-cyber.glitch .cy-glitch{animation:cyFlash .28s steps(3)}
@keyframes cyFlash{0%{opacity:0} 33%{opacity:.18} 66%{opacity:0} 100%{opacity:.1}}
/* THE SCORE, BEEFY AND LIT LIKE THE MARKS (Cory: "the main score look like that but the beefy letters").
   Russo One, a white face, and the colour split off it either side - cyan left, magenta right - which is
   what a neon sign does when it is not quite in focus. It sits at the TOP of the panel so the tower owns
   the rest, capped by height so it never comes down over the roof. The TARGET chip is not shown here: the
   lit floor is the target, said bigger than a chip could. */
.scorep.cricket{justify-content:flex-start; padding-top:10px; gap:6px}
.scorep.cricket .checkout{display:none}
.scorep.cricket .bigscore{font-family:var(--num);
  font-size:min(clamp(96px, 30vh, 300px), calc(86cqw / (var(--chars, 3) * .70)), 27cqh)}
.scorep.cricket .bigscore .ext{color:transparent; text-shadow:-6px 0 0 rgba(57,213,255,.85), 6px 0 0 rgba(255,45,155,.85), 0 0 40px rgba(57,213,255,.45), 0 10px 30px rgba(0,0,0,.8)}
.scorep.cricket .bigscore .face{background-image:linear-gradient(180deg,#fff 0%,#eaf9ff 46%,#bfe9ff 52%,#fff 100%);
  filter:drop-shadow(0 0 12px rgba(255,255,255,.7))}
@media (prefers-reduced-motion:reduce){ .bd .cy-rain{animation:none; transform:translateY(-50%)} .bd-cyber .cy-floor.hit{animation:none} }

/* ---------- LAST ONE STANDING: the top of the show (js/views/lastone.js opening) ---------- */
/* Everything here steps at 15 frames a second. A duration written as n frames gets steps(n) to match. */
.show-intro{position:fixed; inset:0; z-index:90; overflow:hidden; background:#04030a; cursor:pointer}
.show-intro .si-set{position:absolute; inset:0}
.show-intro .si-set svg{width:100%; height:100%; display:block}
/* the wall comes on a row at a time, then chases as it does on the set */
.show-intro .sc-wall{animation:siWall .533s steps(8) both}
@keyframes siWall{from{opacity:0} to{opacity:1}}
.show-intro .sc-rail, .show-intro .sc-spot.sweep{animation-name:siIn; animation-duration:.2s; animation-timing-function:steps(3); animation-fill-mode:both; animation-delay:.4s}
@keyframes siIn{from{opacity:0} to{opacity:1}}
/* the title, off the lens */
.show-intro .si-title{position:absolute; left:50%; top:9%; transform:translateX(-50%); text-align:center; opacity:0; z-index:3;
  animation:siTitle .667s .533s steps(10) both}
.show-intro .si-title span{display:block; font-weight:800; font-size:min(2.4vh,3.2vw,24px); letter-spacing:.44em; color:#ffe7a3}
.show-intro .si-title b{display:block; margin-top:.2em; font-family:var(--num); font-weight:400; line-height:.9; font-size:min(11vh,12vw,120px);
  background:linear-gradient(180deg,#fff 0%,#ffe690 30%,#ffbe1a 52%,#fff3c0 54%,#f0a000 80%,#ffd766 100%); -webkit-background-clip:text; background-clip:text; color:transparent;
  filter:drop-shadow(0 6px 0 #6a3c00) drop-shadow(0 0 30px rgba(255,190,40,.55))}
@keyframes siTitle{0%{opacity:0; transform:translateX(-50%) scale(3.2)} 60%{opacity:1; transform:translateX(-50%) scale(.94)} 100%{opacity:1; transform:translateX(-50%) scale(1)}}
.show-intro .si-flash{position:absolute; inset:0; background:#fff; opacity:0; animation:siFlash .2s 1.0s steps(3) both}
@keyframes siFlash{0%{opacity:0} 34%{opacity:.9} 100%{opacity:0}}
/* the contestants, up through the floor one after another: four frames apart, five frames up */
.show-intro .si-riser{transform-box:fill-box; transform-origin:50% 100%; opacity:0;
  animation:siRise .333s steps(5) both, siCheer .4s steps(2) 2 both;
  animation-delay:calc(1.2s + var(--i) * .267s), 3.2s}
@keyframes siRise{0%{opacity:0; transform:translateY(300px)} 20%{opacity:1} 80%{transform:translateY(-22px)} 100%{opacity:1; transform:translateY(0)}}
@keyframes siCheer{0%{transform:translateY(0)} 50%{transform:translateY(-26px)} 100%{transform:translateY(0)}}
.show-intro .si-card{transform-box:fill-box; transform-origin:50% 0%; animation:siCard .2s steps(3) both; animation-delay:calc(1.53s + var(--i) * .267s)}
@keyframes siCard{from{opacity:0; transform:scale(0)} to{opacity:1; transform:scale(1)}}
.show-intro .si-spot{opacity:0; animation:siIn .067s steps(1) both; animation-delay:calc(1.2s + var(--i) * .267s)}
/* the tag line, then the cut */
.show-intro .si-tag{position:absolute; left:50%; bottom:7%; transform:translateX(-50%); z-index:3; opacity:0; padding:.5em 1.2em; border-radius:999px;
  font-weight:800; font-size:min(2.6vh,3.2vw,26px); letter-spacing:.18em; color:#1a1200; background:#ffd84a; white-space:nowrap; max-width:94vw; overflow:hidden; text-overflow:ellipsis;
  /* In with the title and up for the whole show (Cory, 2026-09-28: "it comes in very late and then it
     disappears quickly... make that come in early as soon as all the other graphics come in"). */
  animation:siTag .2s 1.2s steps(3) both}
@keyframes siTag{from{opacity:0; transform:translate(-50%, 30px)} to{opacity:1; transform:translate(-50%, 0)}}
.show-intro .si-cut{position:absolute; inset:0; background:#fff; opacity:0; animation:siCut .6s 4.4s steps(9) both}
@keyframes siCut{0%{opacity:0} 22%{opacity:1} 100%{opacity:1}}
@media (prefers-reduced-motion:reduce){ .show-intro *{animation-duration:.01s !important; animation-delay:0s !important} }

.tb-help{font-size:clamp(14px, 2.3vh, 21px); line-height:1.45; padding-top:10px; padding-bottom:calc(10px + env(safe-area-inset-bottom))}
.tb-help i{height:1.05em; margin:0 12px -.15em}
@media (max-height:560px){ .tb-help{font-size:clamp(12px, 2vh, 15px); line-height:1.3; padding-top:4px} }

/* ---------- BLACKJACK: the table (js/views/blackjack.js) ---------- */
.bj-table{position:relative; overflow:hidden; container-type:size; display:grid; grid-template-rows:auto minmax(0,1fr) auto auto; padding:10px 14px 8px;
  background:#0a2a18; border-color:#5a3a12}
.bj-felt{position:absolute; inset:0; pointer-events:none;
  background:radial-gradient(ellipse 80% 70% at 50% 30%, #2f9a5d 0%, #166b3c 55%, #05281a 100%)}
.bj-felt::before{content:""; position:absolute; left:0; right:0; top:0; height:22%; min-height:22px;
  background:linear-gradient(180deg,#5a2a10,#2a1208 55%,#150804); border-bottom:3px solid #c9a227; border-radius:0 0 50% 50% / 0 0 40% 40%; box-shadow:0 6px 18px rgba(0,0,0,.5)}
.bj-felt::after{content:""; position:absolute; inset:0; background:radial-gradient(ellipse 60% 40% at 50% 0%, rgba(255,246,192,.28), transparent 70%)}
.bj-arc{position:absolute; left:12%; right:12%; top:58%; height:22%; border:3px dashed rgba(255,216,74,.3); border-top:none; border-radius:0 0 50% 50% / 0 0 100% 100%}
/* :where keeps this at one class, so .bj-house, .bj-handend and .bj-fly stay absolute overlays, not grid rows (audit 28 Oct 2026, #41/#66) */
.bj-table > :where(:not(.bj-felt)){position:relative; z-index:1}
/* the dealer and what he says */
/* THE DEALER, OFF DUTY (Cory, 2026-09-28: "put the little dude dead center on top and make him about 4x
   the size. He should be there as an expert observer, a dealer off duty giving advice"). Centred on the
   rail, a third of the table tall on the tablet, his advice in the bubble beside him. */
/* The row is at least the dealer's height and grows if his bubble needs more, so the words are never cut
   off at the top of the table (Cory, 2026-09-28, on a phone: "wording cut off at the top"). */
.bj-dealer{display:flex; align-items:flex-end; justify-content:center; gap:clamp(6px, 1.5cqw, 18px); min-height:clamp(44px, 30cqh, 330px); padding:0 2cqw}
.bj-dealer-art{height:clamp(44px, 30cqh, 330px); width:auto}
.bj-dealer .bj-bubble{margin-bottom:clamp(4px, 8cqh, 90px)}
@container (max-height:560px){ .bj-dealer .bj-bubble{margin-bottom:4px; font-size:clamp(10px, 2.2cqh, 14px)} .bj-dealer{min-height:clamp(40px, 22cqh, 120px)} .bj-dealer-art{height:clamp(40px, 22cqh, 120px)} }
.bj-dealer-art{flex:none; filter:drop-shadow(0 4px 8px rgba(0,0,0,.5))}
.bj-bubble{position:relative; max-width:min(56cqw, 640px); padding:.45em .8em; border-radius:14px; background:#fff8e6; color:#12101f; font-weight:700; font-size:clamp(11px, 2.4cqh, 22px); line-height:1.25; box-shadow:0 6px 16px rgba(0,0,0,.4); transform-origin:left bottom}
.bj-bubble::before{content:""; position:absolute; left:-8px; bottom:10px; border:7px solid transparent; border-right-color:#fff8e6; border-left:0}
.bj-bubble b{color:#0f5a34}
.bj-bubble.say{animation:bjSay .35s cubic-bezier(.2,1.5,.4,1)}
@keyframes bjSay{from{transform:scale(.6); opacity:0}}
/* the house's hand, alone */
.bj-house{position:absolute; right:14px; top:8px; text-align:center}
.bj-house-lbl{font-size:10px; font-weight:800; letter-spacing:.16em; color:#ffe9a3; text-shadow:0 1px 4px rgba(0,0,0,.6); margin-bottom:4px}
.bj-house-cards{display:flex; gap:4px; justify-content:center}
.bj-house .bj-card{width:clamp(30px, 8cqw, 120px); --i:0}
.bj-house-lbl{font-size:clamp(10px, 1.4cqw, 15px)}
/* the hand */
/* The hand row is a size container of its own, so a card is bounded by the row's HEIGHT as well as the
   table's width, and by nothing else: on the tablet the cards are most of the table (Cory, 2026-09-28:
   "cards come out very large to fill out remaining space"), on a phone beside the rounds panel they are
   whatever the short row allows. The face is an SVG, so it is the same card at either size. */
.bj-hand{display:flex; align-items:center; justify-content:center; gap:clamp(6px, 2cqw, 20px); min-height:0; padding:4px 0; container-type:size}
/* Three cards have to fit the row: when they would not, the later ones tuck under the earlier ones the way a hand is held. */
/* One card takes the row's height; two share the width; three share it tighter. */
.bj-card{position:relative; width:min(calc(94cqw / var(--n, 1) - 8px), calc(92cqh * 5 / 7)); aspect-ratio:5/7; border-radius:7% / 5%; perspective:900px; flex:none;
  transform:rotate(calc((var(--i) - (var(--n) - 1) / 2) * 5deg)) translateY(calc(abs((var(--i) - (var(--n) - 1) / 2)) * 1.2cqh)); transform-style:preserve-3d}
.bj-card .bj-face, .bj-card .bj-back{position:absolute; inset:0; border-radius:inherit; backface-visibility:hidden; overflow:hidden}
.bj-card .bj-face{background:linear-gradient(160deg,#fffdf6,#f1eadb); box-shadow:0 1.2cqh 3cqh rgba(0,0,0,.5), inset 0 0 0 .6cqh #fff, inset 0 0 0 .8cqh #e6dcc0}
.bj-card .bj-back{transform:rotateY(180deg); background:#8a1020 repeating-linear-gradient(45deg, rgba(255,255,255,.12) 0 6px, transparent 6px 14px); box-shadow:0 1.2cqh 3cqh rgba(0,0,0,.5), inset 0 0 0 .9cqh #fff}
.bj-card.down .bj-face{transform:rotateY(180deg)} .bj-card.down .bj-back{transform:none}
.bj-cardsvg{position:absolute; inset:0; width:100%; height:100%; display:block}
.bj-cardsvg .bj-rank{font-family:var(--num); font-size:46px}
.bj-cardsvg .bj-csuit{font-size:40px}
.bj-cardsvg .bj-pip{font-size:58px}
.bj-cardsvg .bj-pip.sm{font-size:28px}
.bj-cardsvg .bj-big{font-family:var(--num); font-size:112px}
.bj-cardsvg .bj-big.long{font-size:84px}
.bj-cardsvg .bj-bigsuit{font-size:170px}
.bj-cardsvg .bj-tag{font-family:var(--ui, inherit); font-weight:800; font-size:17px; letter-spacing:3px}
.bj-cardsvg .bj-missx{font-size:120px; fill:#6a7080}
.bj-card.ace .bj-face{box-shadow:0 1.2cqh 3cqh rgba(0,0,0,.5), inset 0 0 0 .6cqh #fff, inset 0 0 0 1cqh #c9a227}
.bj-card.trip .bj-face{box-shadow:0 1.2cqh 3cqh rgba(0,0,0,.5), inset 0 0 0 .6cqh #fff, inset 0 0 0 1cqh #ff5a7a}
.bj-card.dbl .bj-face{box-shadow:0 1.2cqh 3cqh rgba(0,0,0,.5), inset 0 0 0 .6cqh #fff, inset 0 0 0 1cqh #39d5ff}
.bj-card.miss .bj-face{background:#3a3f4d; box-shadow:0 1.2cqh 3cqh rgba(0,0,0,.5), inset 0 0 0 .6cqh #2a2e38}
.bj-card.deal{animation:bjDeal .5s cubic-bezier(.2,1.2,.4,1)}
@keyframes bjDeal{from{transform:translate(40cqw, -70cqh) rotate(40deg) scale(.6); opacity:0}}
/* the total */
.bj-total{position:relative; text-align:center; line-height:1; margin-top:-2px}
/* the pot: a pile of chips at the right of the total, the count on it */
.bj-pot{position:absolute; right:0; top:50%; transform:translateY(-50%); display:flex; align-items:center; gap:calc(var(--chip) * .18); padding:calc(var(--chip) * .12) calc(var(--chip) * .3) calc(var(--chip) * .12) calc(var(--chip) * .16); border-radius:999px; background:rgba(0,0,0,.35); border:1px solid rgba(255,216,74,.35)}
.bj-pot i{position:relative; width:var(--chip); height:calc(var(--chip) * .5 + var(--edge) * 2.4); flex:none}
.bj-pot i::before, .bj-pot i::after{content:""; position:absolute; left:0; right:0}
.bj-pot i::before{bottom:0} .bj-pot i::after{bottom:calc(var(--edge) * 1.15)}
.bj-pot em{font-style:normal; font-size:clamp(9px, 1.3cqw, 14px); font-weight:800; letter-spacing:.18em; color:#ffe9a3}
.bj-pot b{font-family:var(--num); font-weight:400; font-size:clamp(18px, 4.5cqw, 48px); color:#fff; text-shadow:0 2px 0 rgba(0,0,0,.5)}
.bj-pot.empty{opacity:.45}
.bj-pot.bump{animation:bjPot .5s cubic-bezier(.2,1.6,.4,1)}
@keyframes bjPot{40%{transform:translateY(-50%) scale(1.18)}}
@container (max-width:380px){ .bj-pot{position:static; transform:none; display:inline-flex; margin-top:4px; padding:2px 8px 2px 4px} .bj-pot b{font-size:14px} .bj-pot.bump{animation:none} }
/* Child combinators: the pot's own count is a <b> inside .bj-total too (audit 28 Oct 2026, #67) */
.bj-total > b{display:block; font-family:var(--num); font-weight:400; font-size:clamp(28px, 12cqh, 110px); color:#fff; text-shadow:0 3px 0 rgba(0,0,0,.5), 0 0 24px rgba(255,255,255,.25)}
.bj-total > span{display:block; margin-top:2px; font-size:clamp(9px, 1.8cqh, 16px); font-weight:800; letter-spacing:.18em; color:#ffe9a3}
.bj-total.warm > b{color:#ffe07a}
.bj-total.win > b{color:#ffd84a; text-shadow:0 3px 0 rgba(0,0,0,.5), 0 0 30px rgba(255,216,74,.8)}
.bj-total.bust > b{color:#ff3b5c; text-shadow:0 3px 0 rgba(0,0,0,.6), 0 0 30px rgba(255,59,92,.7)}
.bj-table.shake{animation:bjShake .4s}
@keyframes bjShake{0%,100%{transform:translateX(0)} 20%{transform:translateX(-8px)} 40%{transform:translateX(7px)} 60%{transform:translateX(-4px)} 80%{transform:translateX(3px)}}
/* the seats along the rail: the head, the name, a stack or two of big chips, the hand's total */
/* BIG, THICK GOLD COINS (Cory, 2026-09-28: "they're still not big enough. They should be large 3D coins,
   gold coins, and thick"). The coin is a share of the table's width, held back only so two columns still
   fit a seat when eight are playing (--np is the number of seats, set by the view). */
.bj-table{--chip:clamp(22px, min(8.5cqw, 40cqw / var(--np, 2)), 120px); --edge:calc(var(--chip) * .16)}
.bj-seats{display:flex; gap:clamp(4px, 1cqw, 10px); padding-top:6px; overflow-x:auto; scrollbar-width:none}
.bj-seats::-webkit-scrollbar{display:none}
.bj-seat{flex:1 0 clamp(44px, 9cqw, 150px); min-width:0; position:relative; text-align:center; padding:clamp(4px, 1cqh, 10px) 4px clamp(3px, .8cqh, 8px); border-radius:14px; background:rgba(0,0,0,.28); border:1px solid rgba(255,216,74,.18); opacity:.78; transition:opacity .3s, box-shadow .3s}
/* Whose turn: no word for it (Cory, 2026-09-28: "remove the UP wording... make the color glow and breathe around the pill"). */
.bj-seat.on{opacity:1; border-color:var(--pc); animation:bjBreathe 2s ease-in-out infinite}
@keyframes bjBreathe{
  0%, 100%{box-shadow:0 0 10px color-mix(in srgb, var(--pc) 40%, transparent), inset 0 0 14px color-mix(in srgb, var(--pc) 12%, transparent)}
  50%{box-shadow:0 0 30px color-mix(in srgb, var(--pc) 85%, transparent), 0 0 60px color-mix(in srgb, var(--pc) 35%, transparent), inset 0 0 26px color-mix(in srgb, var(--pc) 24%, transparent)}}
@media (prefers-reduced-motion:reduce){ .bj-seat.on{animation:none; box-shadow:0 0 22px color-mix(in srgb, var(--pc) 60%, transparent)} }
.bj-seat-top{display:flex; align-items:center; justify-content:center; gap:5px; min-width:0}
.bj-seat-top .avatar{width:clamp(18px, 3cqw, 34px); height:clamp(18px, 3cqw, 34px); font-size:clamp(10px, 1.5cqw, 16px); flex:none}
.bj-seat-top b{font-size:clamp(9px, 1.6cqw, 16px); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0}
.bj-stack{position:relative; display:flex; justify-content:center; align-items:flex-end; gap:calc(var(--chip) * .1); height:calc(var(--chip) * .5 + var(--edge) + 5 * var(--edge) * 1.15); margin:calc(var(--chip) * .16) auto 0}
.bj-col{position:relative; width:var(--chip); height:100%}
/* a coin: the face is a gold disc with a raised rim and a stamped star, the edge under it is the thickness */
.bj-col i, .bj-fly, .bj-pot i::before, .bj-pot i::after{border-radius:50%; height:calc(var(--chip) * .5); border:none;
  background:radial-gradient(ellipse at 50% 36%, #fff3b8 0%, #ffd84a 22%, #d9a423 58%, #a8761a 100%);
  box-shadow:0 var(--edge) 0 #8a5a10, 0 var(--edge) 0 1px #5a3a08, 0 calc(var(--edge) + 4px) 10px rgba(0,0,0,.5),
    inset 0 0 0 calc(var(--chip) * .05) rgba(120,80,10,.45), inset 0 0 0 calc(var(--chip) * .07) rgba(255,240,180,.55)}
.bj-col i{position:absolute; left:0; right:0; bottom:calc(var(--k) * var(--edge) * 1.15)}
.bj-col i::after{content:"★"; position:absolute; inset:0; display:grid; place-items:center; font-size:calc(var(--chip) * .26); color:#8a5a10; opacity:.55; text-shadow:0 1px 0 rgba(255,240,180,.6)}
.bj-stack em{position:absolute; right:-4px; bottom:0; font-style:normal; font-size:clamp(10px, 1.6cqw, 15px); font-weight:800; color:#ffe9a3; text-shadow:0 1px 3px #000}
.bj-stack[data-n="0"]{opacity:.3; border-bottom:2px dashed rgba(255,255,255,.4)}
.bj-seat-chips{font-size:clamp(9px, 1.4cqw, 13px); font-weight:800; letter-spacing:.06em; color:#ffe9a3; margin-top:calc(var(--chip) * .1)}
.bj-seat-status{font-family:var(--num); font-size:clamp(12px, 3.4cqh, 30px); color:#fff; min-height:1.1em; margin-top:1px}
.bj-seat.bust .bj-seat-status{color:#ff3b5c} .bj-seat.twentyone .bj-seat-status{color:#ffd84a}
/* the ante on its way to the pot */
.bj-fly{position:absolute; z-index:6; width:var(--chip); pointer-events:none}
@container (max-width:380px){ .bj-seat-chips{display:none} .bj-seat{padding:4px 3px 3px} .bj-seat-top b{display:none}
  .bj-dealer{height:auto; min-height:40px; align-items:center; gap:4px; padding:0} .bj-dealer-art{height:40px} .bj-dealer .bj-bubble{margin-bottom:0; max-width:100%; font-size:10px; padding:.35em .6em} }
/* A short table (a phone, upright, beside the rounds panel) keeps the seats to a strip - head and hand
   total side by side, no chip stack - so the cards get the height. */
@container (max-height:300px){ .bj-seat{display:flex; align-items:center; justify-content:center; gap:4px; padding:2px 4px} .bj-stack, .bj-seat-chips, .bj-fly{display:none} .bj-seat-status{margin:0; font-size:13px} .bj-total > b{font-size:clamp(22px, 12cqh, 40px)} .bj-seats{padding-top:4px} }
/* the hand is settled */
.bj-handend{position:absolute; inset:0; z-index:5; display:grid; place-items:center; background:rgba(3,20,10,.55); backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px)}
.bj-he-card{text-align:center; padding:clamp(12px, 4cqh, 24px) clamp(18px, 6cqw, 40px); border-radius:18px; background:linear-gradient(160deg,#fffdf6,#efe6d2); color:#12101f; border:5px solid #fff; box-shadow:0 0 0 2px #c9a227, 0 24px 60px rgba(0,0,0,.6); max-width:88%}
.bj-he-card .eyebrow{font-size:10px; letter-spacing:.3em; color:#7a6a40; font-weight:800}
.bj-he-card h3{margin:4px 0 2px; font-family:var(--num); font-weight:400; font-size:clamp(22px, 9cqh, 46px); line-height:1; color:var(--pc, #12101f); text-wrap:balance}
.bj-he-card p{margin:0; font-size:clamp(11px, 2.8cqh, 15px); font-weight:800; letter-spacing:.12em; color:#0f5a34}
.bj-handend.push .bj-he-card h3{color:#3a3f4d} .bj-handend.push .bj-he-card p{color:#6a6a7a}
.bj-handend.natural .bj-he-card{box-shadow:0 0 0 3px #ffd84a, 0 0 60px rgba(255,216,74,.7), 0 24px 60px rgba(0,0,0,.6)}
.bj-handend.go .bj-he-card{animation:bjLand .5s cubic-bezier(.2,1.4,.4,1)}
@keyframes bjLand{from{transform:translateY(-40px) rotate(-6deg) scale(.7); opacity:0}}
/* the thrower's hands down the side */
.bj-rlist > div{display:flex; align-items:center; gap:6px}
.bj-rlist > div b{margin-left:auto; font-family:var(--num); font-weight:400; font-size:15px}
.bj-rlist > div.bust b{color:#ff3b5c}
.bj-mini{display:inline-flex; gap:3px}
.bj-mini i{display:inline-grid; place-items:center; min-width:16px; height:20px; padding:0 3px; border-radius:3px; background:#fffdf6; color:#12101f; font-style:normal; font-size:10px; font-weight:800}
@media (prefers-reduced-motion:reduce){ .bj-card.deal, .bj-bubble.say, .bj-handend.go .bj-he-card, .bj-table.shake{animation:none} }
@media (max-height:500px){
  .bj-table{padding:6px 10px 6px}
  .bj-dealer{height:clamp(40px, 22cqh, 64px)}
  .bj-seat{padding:3px 3px 3px} .bj-stack{height:14px; width:26px} .bj-stack i{height:7px} .bj-seat-chips{display:none}
}

/* ---------- TUG OF WAR: the pit (js/views/tugofwar.js) ---------- */
.tw-stage{position:relative; overflow:hidden; padding:0; background:#07051f; border-color:#3a5a2a; container-type:inline-size}
/* the bayou is laid out in its own units (js/fx.js sceneFit): the land in a few still drawings, and every
   moving thing an element of its own over them */
.tw-units{z-index:0}
/* the water's pieces at rest; the animations override these while they run */
.tw-units .tw-foam{opacity:.75} .tw-units .tw-bubble{opacity:.5}
/* the current on the stage: dash-sized stripes sliding behind a still mask of the wavy lines (fx.js fxHtml) */
.tw-current-h{opacity:.28; -webkit-mask-size:100% 100%; mask-size:100% 100%; -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat; overflow:hidden}
.tw-current-h i{position:absolute; top:0; bottom:0; left:-32px; right:0; background:repeating-linear-gradient(90deg, #bfe9c0 0 18px, transparent 18px 32px); animation:twCurrentH 1.5s linear infinite}
@keyframes twCurrentH{to{transform:translateX(32px)}}
/* the edges of the picture fall off into dark; the side in danger glows red */
.tw-vignette{position:absolute; inset:0; pointer-events:none; background:radial-gradient(ellipse at 50% 55%, transparent 55%, rgba(3,8,6,.55) 100%)}
.tw-vignette::after{content:""; position:absolute; inset:0; opacity:0; transition:opacity .5s}
.left-danger .tw-vignette::after{opacity:1; background:linear-gradient(90deg, rgba(255,59,92,.45), transparent 40%); animation:twPulse 1.1s ease-in-out infinite alternate}
.right-danger .tw-vignette::after{opacity:1; background:linear-gradient(270deg, rgba(255,59,92,.45), transparent 40%); animation:twPulse 1.1s ease-in-out infinite alternate}
@keyframes twPulse{from{opacity:.55} to{opacity:1}}
/* the camera drifts after the rope, the far country less than the rest */
.tw-cam, .tw-far{transition:transform .9s cubic-bezier(.3,.8,.3,1)}
/* the rope and everyone on it slide as one; a reset twangs it back */
.tw-rope{transition:transform .6s cubic-bezier(.3,.9,.3,1.12); will-change:transform}
.tw-rope.twang{transition:transform .85s cubic-bezier(.2,1.7,.4,1)}
.tw-rope.twang .tw-ropebits{animation:twTwangGlow .7s ease-out}
@keyframes twTwangGlow{20%{filter:brightness(1.9)}}
/* the rope snaps tight on a pull: the sag flattens for a beat */
.tw-ropebits{transform-box:fill-box; transform-origin:50% 0}
.tw-ropebits.tight{animation:twTight .5s cubic-bezier(.2,1.4,.4,1)}
@keyframes twTight{30%{transform:scaleY(.82)} 70%{transform:scaleY(1.05)}}
/* THE LINES. A pull is a heave the whole line lunges into; the other line is dragged forward on its
   heels; a reset staggers both. --hx is 1 for the left bank and -1 for the right, so one set of frames
   serves both: the pulling line always goes AWAY from the pit, the dragged line toward it. */
.tw-team{transform-box:fill-box; transform-origin:50% 100%}
.tw-team.heave{animation:twHeave .6s cubic-bezier(.2,1.3,.4,1)}
@keyframes twHeave{0%{transform:none} 30%{transform:translateX(calc(var(--hx) * -14px)) rotate(calc(var(--hx) * -5deg))} 65%{transform:translateX(calc(var(--hx) * 3px)) rotate(calc(var(--hx) * 1deg))} 100%{transform:none}}
.tw-team.heave.big{animation-name:twHeaveBig; animation-duration:.7s}
@keyframes twHeaveBig{0%{transform:none} 28%{transform:translateX(calc(var(--hx) * -26px)) rotate(calc(var(--hx) * -9deg))} 60%{transform:translateX(calc(var(--hx) * 5px)) rotate(calc(var(--hx) * 2deg))} 100%{transform:none}}
.tw-team.dragged{animation:twDragged .6s ease-out}
@keyframes twDragged{0%{transform:none} 35%{transform:translateX(calc(var(--hx) * 12px)) rotate(calc(var(--hx) * 6deg))} 100%{transform:none}}
.tw-team.dragged.big{animation-name:twDraggedBig; animation-duration:.7s}
@keyframes twDraggedBig{0%{transform:none} 35%{transform:translateX(calc(var(--hx) * 22px)) rotate(calc(var(--hx) * 10deg))} 70%{transform:translateX(calc(var(--hx) * 4px))} 100%{transform:none}}
.tw-team.stagger{animation:twStagger .7s ease-out}
@keyframes twStagger{0%{transform:none} 30%{transform:translateX(calc(var(--hx) * 18px)) rotate(calc(var(--hx) * 8deg))} 60%{transform:translateX(calc(var(--hx) * -8px)) rotate(calc(var(--hx) * -3deg))} 100%{transform:none}}
/* nobody stands still: every body strains on the rope, each on its own beat */
.tw-body{transform-box:fill-box; transform-origin:88% 100%; animation:twStrain 2.4s ease-in-out infinite alternate}
.tw-seat:nth-child(even) .tw-body{animation-delay:-1.3s} .tw-seat:nth-child(3n) .tw-body{animation-duration:2.9s; animation-delay:-.6s}
@keyframes twStrain{from{transform:rotate(-1.6deg) translateX(1px)} to{transform:rotate(1.2deg) translateX(-2px)}}
.tw-seat.up .tw-body{animation-duration:1.1s}
/* dirt flies off the heels on a heave, skids out from under them when dragged */
.tw-dust circle{transform-box:fill-box; transform-origin:50% 50%}
.tw-team.heave .tw-dust circle{animation:twDust .55s ease-out; animation-delay:var(--d)}
@keyframes twDust{0%{opacity:0; transform:translate(0,0) scale(.4)} 20%{opacity:.85} 100%{opacity:0; transform:translate(var(--vx), -26px) scale(1.6)}}
.tw-team.dragged .tw-dust circle{animation:twSkid .5s ease-out; animation-delay:var(--d)}
@keyframes twSkid{0%{opacity:0; transform:translate(0,0) scale(.5)} 25%{opacity:.7} 100%{opacity:0; transform:translate(calc(var(--vx) * -.6), -10px) scale(1.3)}}
/* the thrower's seat is lit from below; mouths change with the state */
.tw-spot{opacity:0; transition:opacity .4s}
.tw-seat.up .tw-spot{opacity:1; animation:twSpot 1.6s ease-in-out infinite alternate}
@keyframes twSpot{from{opacity:.65} to{opacity:1}}
.tw-puller .tw-mouth{display:none} .tw-puller .tw-mouth.calm{display:block}
.tw-team.heave .tw-mouth.calm, .tw-team.dragged .tw-mouth.calm, .left-danger .tw-team.left .tw-mouth.calm, .right-danger .tw-team.right .tw-mouth.calm{display:none}
.tw-team.heave .tw-mouth.grit, .tw-team.dragged .tw-mouth.grit{display:block}
.left-danger .tw-team.left .tw-mouth.yell, .right-danger .tw-team.right .tw-mouth.yell{display:block}
.left-danger .tw-team.left .tw-mouth.grit, .right-danger .tw-team.right .tw-mouth.grit{display:none}
.tw-puller .tw-sweat{display:none}
.tw-team.heave .tw-sweat, .tw-team.dragged .tw-sweat, .left-danger .tw-team.left .tw-sweat, .right-danger .tw-team.right .tw-sweat{display:block}
.tw-tail{transform-box:fill-box; transform-origin:100% 50%; animation:twTail .7s ease-in-out infinite alternate}
@keyframes twTail{from{transform:rotate(-6deg)} to{transform:rotate(8deg)}}
/* the winners jump: up slowing, down speeding (alternate reverses the curve) */
.tw-team.winners .tw-body{animation:twCheer .26s ease-out infinite alternate}
@keyframes twCheer{from{transform:rotate(-3deg) translateY(0)} to{transform:rotate(3deg) translateY(-26px)}}
/* the knot and the arrow over it */
.tw-knot{transform-box:fill-box; transform-origin:50% 100%}
.tw-knot.jolt{animation:twJolt .45s cubic-bezier(.2,1.6,.4,1)}
@keyframes twJolt{30%{transform:scale(1.25) rotate(-8deg)} 60%{transform:scale(.95) rotate(5deg)}}
.tw-arrow{transform-box:fill-box; transform-origin:50% 100%; animation:twArrow 1.2s ease-in-out infinite alternate}
@keyframes twArrow{from{transform:translateY(0)} to{transform:translateY(-8px)}}
.tw-name{font-family:var(--ui, inherit); font-weight:800; font-size:24px; fill:#fff; paint-order:stroke; stroke:#05100a; stroke-width:5px; letter-spacing:.04em}
/* THE GATORS: drifting in the pool, snapping when a team is at the edge, feasting at the end */
.tw-gator{transform-box:fill-box; transform-origin:50% 60%; animation:twBob 3.4s ease-in-out infinite alternate; animation-delay:calc(var(--k) * -1.1s)}
@keyframes twBob{from{transform:translate(0,0)} to{transform:translate(6px, 4px)}}
.tw-jaw-top{transform-box:fill-box; transform-origin:0% 100%}
.left-danger .tw-jaw-top, .right-danger .tw-jaw-top{animation:twSnap 1.2s cubic-bezier(.6,0,.9,.4) infinite; animation-delay:calc(var(--k) * -.4s)}
@keyframes twSnap{0%,55%{transform:rotate(0)} 70%{transform:rotate(-30deg)} 78%{transform:rotate(-26deg)} 86%{transform:rotate(0)} 100%{transform:rotate(0)}}
.feast .tw-jaw-top{animation:twSnap .5s linear infinite}
.feast .tw-gator{animation:twFeast .45s ease-in-out infinite alternate; animation-delay:calc(var(--k) * -.15s)}
@keyframes twFeast{from{transform:translate(0,0)} to{transform:translate(14px,-8px)}}
.left-danger .tw-eye, .right-danger .tw-eye, .feast .tw-eye{fill:#ff3b3b}
.tw-ripple{opacity:.3; transform-box:fill-box; transform-origin:50% 50%; animation:twRipple 3s ease-out infinite; animation-delay:calc(var(--k) * -1s)}
@keyframes twRipple{0%{transform:scale(.8); opacity:.4} 100%{transform:scale(1.15); opacity:0}}
/* the lip crumbles where a team is at the edge */
.tw-clods circle{transform-box:fill-box; transform-origin:50% 50%}
.left-danger .tw-clods.l circle, .right-danger .tw-clods.r circle{animation:twClod 1.3s cubic-bezier(.4,0,.9,.5) infinite; animation-delay:var(--d)}
@keyframes twClod{0%{opacity:0; transform:translateY(0)} 10%{opacity:1} 100%{opacity:0; transform:translateY(90px)}}
/* the water: the fall, the foam, the current, the mist, the bubbles; the sky's stars and the fireflies */
.tw-water{animation:twWater 4s ease-in-out infinite alternate}
@keyframes twWater{from{opacity:.94} to{opacity:1}}
.tw-fallwater{animation:twFalling .78s linear infinite}
@keyframes twFalling{to{transform:translateY(26px)}}
.tw-foam{transform-box:fill-box; transform-origin:50% 50%; animation:twFoam 1.6s ease-in-out infinite alternate; animation-delay:var(--d)}
@keyframes twFoam{from{transform:scale(.9) translateY(0); opacity:.35} to{transform:scale(1.15) translateY(3px); opacity:.6}}
.tw-mist{animation:twMist 9s ease-in-out infinite alternate; animation-delay:var(--d)}
@keyframes twMist{from{transform:translateX(-40px); opacity:.08} to{transform:translateX(40px); opacity:.16}}
.tw-current{stroke-dasharray:18 14; animation:twCurrent 3s linear infinite}
@keyframes twCurrent{to{stroke-dashoffset:-64}}
.tw-star{animation:twStar 3s ease-in-out infinite alternate; animation-delay:var(--d)}
@keyframes twStar{from{opacity:.2} to{opacity:.9}}
.tw-fly{animation:twFly 3s ease-in-out infinite; animation-delay:var(--d)}
@keyframes twFly{0%,100%{opacity:.05} 50%{opacity:.95}}
.tw-bubble{animation:twBubble 4s ease-in infinite; animation-delay:var(--d); transform-box:fill-box; transform-origin:50% 50%}
@keyframes twBubble{0%{transform:translateY(0) scale(.6); opacity:0} 20%{opacity:.6} 100%{transform:translateY(-70px) scale(1.2); opacity:0}}
.tw-shim{animation:twShim 2.4s ease-in-out infinite alternate; animation-delay:var(--d)}
@keyframes twShim{from{opacity:.12} to{opacity:.6}}
@container (max-width:520px){ .tw-name{display:none} }
/* the fall */
.tw-fall{transform-box:fill-box; transform-origin:50% 100%}
.tw-ring{animation:twRing 1.3s ease-out forwards; transform-box:fill-box; transform-origin:50% 50%}
@keyframes twRing{from{transform:scale(.4); opacity:1} to{transform:scale(7); opacity:0}}
.tw-plume{transform-box:fill-box; transform-origin:50% 100%; animation:twPlume .75s ease-out forwards; animation-delay:var(--d)}
@keyframes twPlume{0%{transform:translateY(0) scaleY(.3); opacity:1} 100%{transform:translateY(-70px) scaleY(1.5); opacity:0}}
.falling .tw-rope{transition:none}
.falling .tw-ropebits{animation:twRopeDrop 1.4s cubic-bezier(.5,0,.8,.6) .9s forwards}
@keyframes twRopeDrop{to{transform:translateY(120px); opacity:.35}}
/* THE HUD over the sky: each team's name, its numbers and its footing; the count between them */
.tw-hud{position:absolute; left:10px; right:10px; top:8px; display:flex; align-items:flex-start; justify-content:space-between; gap:8px; pointer-events:none}
.tw-team-hud{padding:6px 12px 8px; border-radius:14px; background:rgba(5,16,10,.62); border:1px solid rgba(232,255,176,.25); min-width:0; flex:0 1 30cqw; opacity:.75; transition:opacity .3s, box-shadow .3s}
.tw-team-hud.on{opacity:1; border-color:#e8ffb0; box-shadow:0 0 18px rgba(232,255,176,.5)}
.tw-team-hud b{display:block; font-size:clamp(10px, 1.6cqw, 15px); letter-spacing:.06em; color:#fff; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.tw-team-hud span{display:block; font-family:var(--num); font-size:clamp(11px, 1.8cqw, 18px); color:#e8ffb0}
.tw-team-hud.right{text-align:right}
/* FOOTING: ten blocks that drain toward the pit as the team is dragged; green, then amber, then red */
.tw-foot{display:flex; gap:2px; margin-top:5px; height:clamp(5px, .9cqw, 9px)}
.tw-foot b{flex:1; border-radius:2px; background:rgba(255,255,255,.12); transition:background .3s, box-shadow .3s}
.tw-foot b.on{background:#5ee88a; box-shadow:0 0 6px rgba(94,232,138,.6)}
.tw-team-hud.low .tw-foot b.on{background:#ffd23a; box-shadow:0 0 6px rgba(255,210,58,.6)}
.tw-team-hud.dire .tw-foot b.on{background:#ff3b5c; box-shadow:0 0 8px rgba(255,59,92,.8); animation:twDire .6s ease-in-out infinite alternate}
@keyframes twDire{from{opacity:.5} to{opacity:1}}
.tw-count{text-align:center; padding:6px 14px; border-radius:14px; background:rgba(5,16,10,.62); border:1px solid rgba(255,255,255,.18); transition:border-color .3s, box-shadow .3s}
.tw-count.warn{border-color:rgba(255,59,92,.7); box-shadow:0 0 18px rgba(255,59,92,.35)}
.tw-count b{display:block; font-family:var(--num); font-size:clamp(12px, 2.2cqw, 24px); color:#fff; letter-spacing:.04em; white-space:nowrap}
.tw-count span{display:block; font-size:clamp(9px, 1.3cqw, 12px); font-weight:800; letter-spacing:.1em; color:#ffd0d8; white-space:nowrap}
/* the side list: a dart is an arrow toward the bank it pulled for */
.tw-marks{display:inline-flex; gap:4px}
.tw-d{display:inline-grid; place-items:center; min-width:20px; height:20px; padding:0 3px; border-radius:4px; font-style:normal; font-weight:800; font-size:12px; background:rgba(255,255,255,.08); color:rgba(255,255,255,.35)}
.tw-d.mine{background:rgba(46,230,160,.25); color:#7fffc8} .tw-d.theirs{background:rgba(255,59,92,.25); color:#ff8fa3} .tw-d.bull{color:#ffd84a}
/* a phone upright: the whole bayou, and the rounds panel steps aside for it */
@media (max-width:600px){
  #scr-game[data-view="tugofwar"] .x-main{grid-template-columns:minmax(0,1fr)}
  #scr-game[data-view="tugofwar"] .x-main > .panel.rounds{display:none}
  .tw-hud{left:6px; right:6px; top:6px; gap:5px}
  .tw-team-hud{padding:4px 8px 6px; border-radius:10px}
  .tw-count{padding:4px 8px; border-radius:10px}
}
@media (prefers-reduced-motion:reduce){ .tw-star{opacity:.6} .tw-fly{opacity:.5} .tw-shim{opacity:.45} .tw-gator, .tw-water, .tw-fly, .tw-star, .tw-jaw-top, .tw-body, .tw-mist, .tw-foam, .tw-fallwater, .tw-current-h i, .tw-arrow, .tw-tail, .tw-ripple, .tw-shim{animation:none} .tw-rope, .tw-cam, .tw-far{transition:none} }

/* ---------- HOT STREAK: the fire (js/views/hotstreak.js) ---------- */
.hs-stage{position:relative; overflow:hidden; padding:0; background:#1a0e2e; border-color:#5a2a1a; container-type:size}
.hs-scene{position:absolute; inset:0; width:100%; height:100%; display:block}
/* the live fire (views/hotstreak.js HsFx): a canvas over the wood, stretched to the stage */
.hs-fx{position:absolute; inset:0; width:100%; height:100%; display:block; pointer-events:none}
/* the gas and the inferno: the whole stage kicks once */
.hs-stage.roar{animation:hsRoar .45s ease-out}
@keyframes hsRoar{20%{transform:translate(-4px, 2px)} 40%{transform:translate(4px, -2px)} 60%{transform:translate(-2px, 1px)} 80%{transform:translate(1px, 0)}}
.hs-stage.jolt{animation:hsRoar .3s ease-out}
/* the bucket: in fast, tipped, the water gone out of it as a shower (the canvas draws the water) */
.hs-bucket{transform-box:fill-box; transform-origin:50% 0; transform:translateY(-170px)}
.hs-stage.douse .hs-bucket{animation:hsBucket 1.6s cubic-bezier(.3,.8,.4,1) forwards}
@keyframes hsBucket{0%{transform:translateY(-170px) rotate(0)} 14%{transform:translateY(0) rotate(0)} 24%{transform:translateY(0) rotate(-115deg)} 72%{transform:translateY(0) rotate(-115deg)} 100%{transform:translateY(-170px) rotate(0)}}
/* the storm: the sky darkens and the clouds roll in */
.hs-dim{opacity:0; transition:opacity .35s}
.hs-stage.stormy .hs-dim{opacity:.55}
.hs-cloud{transition:transform .45s cubic-bezier(.2,.9,.3,1)}
.hs-cloud.l{transform:translateX(-620px)} .hs-cloud.r{transform:translateX(620px)}
.hs-stage.stormy .hs-cloud{transform:translateX(0)}
.hs-score{position:absolute; left:0; right:0; top:4%; text-align:center; pointer-events:none}
.hs-score b{display:block; font-family:var(--num); font-weight:400; font-size:clamp(56px, 30cqh, 220px); line-height:1; color:#fff;
  background:linear-gradient(180deg,#fff 0%,#ffe9a3 40%,#ff9a2a 52%,#fff 56%,#ffb020 100%); -webkit-background-clip:text; background-clip:text; color:transparent;
  filter:drop-shadow(0 0 14px rgba(255,120,30,.7)) drop-shadow(0 6px 0 rgba(90,20,0,.9))}
.hs-score span{display:block; margin-top:4px; font-size:clamp(10px, 2.4cqh, 16px); font-weight:800; letter-spacing:.24em; color:#ffe9a3; text-shadow:0 2px 6px #000}
.hs-stage.streak .hs-score span{color:#fff; text-shadow:0 0 14px #ff6a1a, 0 0 30px #ff6a1a}
.hs-turn{position:absolute; left:0; right:0; bottom:4%; text-align:center; display:flex; justify-content:center; align-items:center; gap:6px; pointer-events:none}
.hs-turn span{font-family:var(--num); font-size:clamp(14px, 4cqh, 28px); padding:2px 10px; border-radius:999px; background:rgba(0,0,0,.4); color:#ffe9a3; border:1px solid rgba(255,200,100,.35)}
.hs-turn span.out{color:#9fe3ff; border-color:rgba(159,227,255,.4); text-decoration:line-through}
.hs-turn i{color:#ffb020; font-style:normal; font-size:clamp(14px, 4cqh, 28px)}
/* the side list */
.hs-marks{display:inline-flex; gap:4px}
.hs-d{display:inline-grid; place-items:center; min-width:22px; height:20px; padding:0 4px; border-radius:4px; font-style:normal; font-weight:800; font-size:11px; background:rgba(255,255,255,.08); color:rgba(255,255,255,.35)}
.hs-d.hot{background:rgba(255,120,30,.3); color:#ffd23a} .hs-d.out{background:rgba(47,123,255,.3); color:#9fe3ff}
.hs-rlist > div b{margin-left:auto; font-family:var(--num); font-weight:400; font-size:15px}
.hs-rlist > div.doused b{color:#9fe3ff}
@media (prefers-reduced-motion:reduce){ .hs-stage.roar, .hs-stage.jolt{animation:none} }

/* ---------- COUNT UP: big numbers in a cool room (js/views/numeral.js BACKDROPS.pulse) ----------
   Everything that moves is its own layer moved by transform or opacity, so the phone never repaints the
   room for it (see the note in numeral.js). The room itself is one painted background. */
.pu-room{position:absolute; inset:0; overflow:hidden;
  background:radial-gradient(rgba(159,196,255,.17) 1.2px, transparent 1.7px) 0 0/30px 30px, linear-gradient(180deg, #03041a 0%, #0a0f3c 55%, #170a3a 100%)}
.pu-aur{position:absolute; border-radius:50%; will-change:transform}
.pu-aur.a{left:-18%; top:-22%; width:86%; height:100%; background:radial-gradient(closest-side, rgba(57,213,255,.5), rgba(57,213,255,0)); animation:puAurA 22s ease-in-out infinite alternate}
.pu-aur.b{right:-20%; top:-26%; width:82%; height:96%; background:radial-gradient(closest-side, rgba(255,63,180,.4), rgba(255,63,180,0)); animation:puAurB 27s ease-in-out infinite alternate}
.pu-aur.c{left:4%; bottom:-34%; width:92%; height:86%; background:radial-gradient(closest-side, rgba(122,92,255,.46), rgba(122,92,255,0)); animation:puAurC 31s ease-in-out infinite alternate}
@keyframes puAurA{from{transform:translate(-5%, 3%) scale(1)} to{transform:translate(12%, -4%) scale(1.15)}}
@keyframes puAurB{from{transform:translate(5%, -2%) scale(1.08)} to{transform:translate(-14%, 6%) scale(.92)}}
@keyframes puAurC{from{transform:translate(0, 0) scale(1)} to{transform:translate(-7%, -8%) scale(1.12)}}
/* light streaming up, all the time: the game is counting up */
.pu-streak{position:absolute; top:100%; border-radius:2px; background:linear-gradient(0deg, rgba(127,227,255,0), rgba(191,241,255,.55) 70%, #fff);
  opacity:0; will-change:transform, opacity; animation:puRise var(--t) linear infinite; animation-delay:var(--d)}
@keyframes puRise{0%{transform:translateY(0); opacity:0} 10%{opacity:1} 80%{opacity:.8} 100%{transform:translateY(-135cqh); opacity:0}}
/* the ring behind the number when a dart scores */
.pu-ring{position:absolute; left:50%; top:44%; width:26cqh; height:26cqh; margin:-13cqh 0 0 -13cqh; border-radius:50%; border:.7cqh solid #bff1ff; opacity:0; will-change:transform, opacity}
.pu-ring.r2{border-color:#ff7ad0; border-width:.45cqh}
.pu-ring.go{animation:puRing .9s cubic-bezier(.1,.7,.3,1)}
.pu-ring.r2.go{animation-delay:.12s}
@keyframes puRing{0%{transform:scale(.6); opacity:.9} 100%{transform:scale(3.4); opacity:0}}
.pu-flare{position:absolute; inset:0; background:#e9fbff; opacity:0}
.pu-room.flare .pu-flare{animation:puFlare .8s ease-out}
@keyframes puFlare{0%{opacity:0} 15%{opacity:.28} 100%{opacity:0}}
.pu-room.dud{animation:puDud .35s steps(3)}
@keyframes puDud{33%{opacity:.75} 66%{opacity:.9}}
/* THE METER (js BACKDROPS.pulse paint): unlit segments glow faintly, lit ones burn, the peak light brightest;
   the red line marks where the red zone starts, and a slam into it jolts the meter and flashes the line. */
.pu-meter{position:absolute; left:0; right:0; bottom:0; height:23%; transform-origin:50% 100%}
.pu-meter svg{display:block; width:100%; height:100%}
.pu-seg{opacity:.12} .pu-seg.g{fill:#3dff8e} .pu-seg.a{fill:#ffd23a} .pu-seg.r{fill:#ff3348}
.pu-seg.on{opacity:.92} .pu-seg.pk{opacity:1}
.pu-redline{stroke:#ff3348; stroke-width:2; stroke-dasharray:10 8; fill:none; opacity:.4}
.pu-room.slam .pu-meter{animation:puSlam .4s cubic-bezier(.2,1.6,.4,1)}
.pu-room.slam .pu-redline{animation:puRedHit .7s ease-out}
@keyframes puSlam{25%{transform:translateY(-1.2%) scaleY(1.05)} 55%{transform:translateY(.3%) scaleY(.99)}}
@keyframes puRedHit{0%{opacity:1; stroke-width:5} 100%{opacity:.4; stroke-width:2}}
/* THE NUMBER: as big as the panel allows, white-hot chrome with a cyan edge and a glow - text shadows, not
   filters, because the number changes on every frame of its count and a filter is redrawn each time. */
.scorep.pulse .bigscore{font-size:min(clamp(130px, 50vh, 600px), calc(97cqw / (var(--chars, 3) * .62)), 70cqh); margin-bottom:12cqh}
.scorep.pulse .bigscore .ext{color:transparent; -webkit-text-stroke:.06em #39d5ff; paint-order:stroke; opacity:.9;
  text-shadow:0 0 .07em rgba(57,213,255,.8), 0 0 .16em rgba(255,63,180,.35)}
.scorep.pulse .bigscore .face{background:linear-gradient(180deg,#ffffff 0%,#f2fbff 22%,#9fe8ff 46%,#ffffff 49.5%,#ffd6f1 51%,#c7a6ff 78%,#ffffff 100%);
  -webkit-background-clip:text; background-clip:text; color:transparent; filter:none}
.scorep.pulse .checkout .lbl, .scorep.pulse .eyebrow{color:#dff6ff; text-shadow:0 2px 8px rgba(0,0,0,.9)}
@media (prefers-reduced-motion:reduce){ .pu-aur, .pu-streak{animation:none} }
/* A phone held upright gives the number the whole width: the round list steps aside, as it does for Tug of
   War (the cards under the board still carry every score). */
@media (max-width:600px){
  .x-main:has(> .scorep.pulse){grid-template-columns:minmax(0,1fr)}
  .x-main:has(> .scorep.pulse) > .panel.rounds{display:none}
}

/* Cricket on a phone held upright: the five capsules were sharing 220px with the thrower panel beside
   them and came out 20px wide (store screenshots at 440px, 2026-10-16). Under 600px the capsules take
   the whole width and the thrower panel steps aside; the player cards below still carry marks and points. */
@media (orientation:portrait) and (max-width:600px){
  #scr-game[data-view="cricket"] .x-main{grid-template-columns:minmax(0,1fr)}
  #scr-game[data-view="cricket"] .x-main > .panel.rounds{display:none}
}
