/* Bonkolino: Fruit Punch — the 2-D overlay (DESIGN §10/§11, design/ui/export/export/NOTES.md).
   Quake-style: bitmap fonts on the 8/10 px grid, textured bevelled panels, hard 2 px shadows, no radius, no blur,
   no gradients apart from the optional scanline / vignette. Every size below is a multiple of the font grid —
   Press Start 2P at 16/24/32/48/64, VT323 at 20/24/30/40/60 — and nothing scales with the viewport except the
   panel stages (--ui-scale, written once per resize by hud.js) so text never lands on a half pixel. */

/* ---------- the overlay's own pixel art (assets/ui/, all shipped by build.mjs) ----------
   Redraw any of these and the HUD follows: they are plain PNGs with no CSS geometry of their own — every rule below
   draws them centred in their box with image-rendering: pixelated, so a 2-px pixel style stays crisp.
     ui-icon-health.png   56x56   the health plate: a red plate with a cream cross, cross centred on the square
     ui-icon-armor.png    56x56   the armor shield (transparent ground; it carries no frame — a shield is not a box)
     ui-glyph-<id>.png    32x32   one per weapon id — nailgun shotgun ssg chaingun railgun rocket (transparent ground);
                                  the weapon-bar slot draws it under its key number. A selected slot darkens the glyph
                                  (filter: brightness(0)) so it reads on the cider fill, so draw them LIGHT.
     ui-digits.png · ui-plate.png · ui-console.png · ui-button.png · ui-frame.png · ui-logo-rust.png   the sheets below
   Palette (design/ui/export/export/tokens.css): cream #fff0c0 · tan #c8a464 · beam #7a4e28 · outline #2a1810 ·
   cider #ff8a20 · cider glow #ffb060 · brass #b08a30 · stone #8a8580 · danger #ff2020. Keep the art CENTRED in the
   file — nothing here nudges it — and keep the transparent ground transparent. */

/* ---------- fonts (self-hosted, OFL; assets/fonts/LICENSE-*.txt) ----------
   font-display: block, not swap: a pixel headline reflowing from a fallback mid-boot is worse than a blank frame. */
@font-face { font-family: 'Press Start 2P'; font-style: normal; font-weight: 400; font-display: block; src: url('../assets/fonts/press-start-2p-latin-400-normal.woff2') format('woff2'); }
@font-face { font-family: 'VT323'; font-style: normal; font-weight: 400; font-display: block; src: url('../assets/fonts/vt323-latin-400-normal.woff2') format('woff2'); }

/* ---------- tokens (design/ui/export/export/tokens.css — single source of the palette, type scale and timings) ---------- */
:root {
  /* materials */
  --c-planks: #b8895a; --c-beam: #7a4e28; --c-crate: #c08040; --c-rust: #8a4a20;
  --c-brass: #b08a30; --c-stone: #8a8580; --c-plate: #6e7078;
  /* accents & states */
  --c-cider: #ff8a20; --c-cider-glow: #ffb060; --c-danger: #ff2020; --c-ok: #40ff70;
  /* text */
  --c-text: #c8a464; --c-text-bright: #fff0c0; --c-text-dim: #6e5a3a; --c-shadow: #2a1810;
  /* grounds */
  --c-ground: #14100c; --c-panel-dark: #14161a; --c-inner: #0e0c0a; --c-inner-soft: rgba(18,14,11,.94);
  /* bevel */
  --bevel-light: #8e9098; --bevel-dark: #2c2e34; --bevel-hover: #b0b2ba;
  --bevel-wood-light: #7a5e3a; --bevel-wood-dark: #1a1210;
  --btn-light: #a89a88; --btn-light-hover: #c8bca8; --btn-face: #8a7a6a;
  /* type */
  --f-head: 'Press Start 2P', 'Courier New', monospace;   /* 8px grid: 16 24 32 48 64 */
  --f-body: 'VT323', 'Courier New', monospace;            /* 10px grid: 20 24 30 40 60 */
  --fs-h1: 64px; --fs-h2: 48px; --fs-h3: 32px; --fs-h4: 16px;
  --fs-body-l: 40px; --fs-body-m: 30px; --fs-body-s: 20px; --fs-stat: 60px;
  --shadow-text: 2px 2px 0 var(--c-shadow);
  --shadow-text-h1: 4px 4px 0 var(--c-shadow);
  --shadow-text-h3: 3px 3px 0 var(--c-shadow);
  /* digit sprite: assets/ui/ui-digits.png 2696x360, ten glyphs, drawn at x0.2 */
  --digit-w: 54px; --digit-h: 72px; --digit-sheet: 540px 72px;
  /* layout */
  --hud-inset: 32px; --panel-w: 1280px; --slot: 64px; --icon: 56px; --pip: 20px; --toast-y: 80px; --banner-y: 320px;
  /* timing */
  --t-notify: 3s; --t-toast: 4s; --t-weaponbar: 1.5s; --t-banner-in: 2s; --t-modeline: 2s; --t-damage: .4s;
  /* textures (tiled at 50 % of the source, so a 512 px sheet reads as 256 px) */
  --tx-plate: url('../assets/ui/ui-plate.png') 0 0/256px 256px;
  --tx-planks: url('../assets/ui/ui-console.png') 0 0/256px 256px;
  --tx-button: url('../assets/ui/ui-button.png') 0 0/256px 256px;
  --tx-rail: url('../assets/ui/ui-frame.png') 0 0/256px 32px repeat-x;
  /* stage scale: 1 at 1920x1080 and above, shrinks so a 1280x1000 frame still fits a 1366x768 laptop (hud.js, on resize).
     --card-scale is the death / results pair's own factor: those cards are content-sized (1100 wide, 550 tall for the
     death card and up to 766 for a four-player results table), so they stay at 1:1 on a 1366x768 laptop where
     --ui-scale would have shrunk them to 72 % for nothing. */
  --ui-scale: 1; --stage-scale: 1; --card-scale: 1;
}

/* ---------- base ---------- */
html, body { margin: 0; height: 100%; overflow: hidden; background: var(--c-ground); color: var(--c-text); font-family: var(--f-body); user-select: none; }
#game { position: fixed; inset: 0; width: 100%; height: 100%; display: block; cursor: crosshair; }
.hidden { display: none !important; }

/* old-screen overlay: 2 px scanlines + a soft vignette over EVERYTHING (world, HUD, panels).
   body.noscan turns both off (hud.setCrt(false)); default on at the token opacities. */
#crt, #uivig { position: fixed; inset: 0; pointer-events: none; z-index: 50; }
#crt { background: repeating-linear-gradient(#000 0 2px, transparent 2px 4px); opacity: .05; }
#uivig { background: radial-gradient(ellipse at center, transparent 55%, var(--c-ground) 100%); opacity: .5; }
body.noscan #crt, body.noscan #uivig { display: none; }

/* ---------- treatments (tokens.css .panel .slab .btn .chip .field .toast .digit) ---------- */
.panel { background: var(--tx-plate); image-rendering: pixelated; border: 2px solid; border-color: var(--bevel-light) var(--bevel-dark) var(--bevel-dark) var(--bevel-light); box-shadow: 0 0 0 2px var(--c-panel-dark); }
.slab { background: var(--tx-planks); image-rendering: pixelated; border: 2px solid; border-color: var(--bevel-wood-light) var(--bevel-wood-dark) var(--bevel-wood-dark) var(--bevel-wood-light); box-shadow: 0 0 0 2px #0c0a08; }
/* the button face is multiplied down to a mid-brown: the raw ui-button.png is a light grey-tan, and tan text on it
   measured 2.1:1 in the 2026-09-14 screenshots — the "too blended with the background" the review called out.
   At this face tan reads 4.9:1, cream 10:1 and the cider selected state 4.9:1. */
.btn { height: 56px; padding: 0 32px; box-sizing: border-box; background: var(--tx-button); background-color: var(--btn-face); background-blend-mode: multiply;
  image-rendering: pixelated; border: 2px solid;
  border-color: var(--btn-light) var(--c-shadow) var(--c-shadow) var(--btn-light); font: var(--fs-h4) var(--f-head); color: var(--c-text);
  text-shadow: var(--shadow-text); display: inline-flex; align-items: center; justify-content: center; gap: 12px; cursor: pointer; pointer-events: auto; white-space: nowrap; }
.btn:hover { color: var(--c-text-bright); border-color: var(--btn-light-hover) var(--c-shadow) var(--c-shadow) var(--btn-light-hover); }
.btn.on { color: var(--c-text-bright); border-color: var(--btn-light-hover) var(--c-shadow) var(--c-shadow) var(--btn-light-hover); }
.btn.selected { color: var(--c-cider); border-color: var(--c-shadow) var(--btn-light) var(--btn-light) var(--c-shadow); }
.btn.off, .btn.off:hover { color: var(--c-text-dim); border-color: #6e6a62 var(--c-shadow) var(--c-shadow) #6e6a62; opacity: .7; cursor: default; }
/* the cider triangle that marks the default action (START / DONE) and the menu cursor */
.tri { width: 0; height: 0; border-left: 14px solid var(--c-cider); border-top: 10px solid transparent; border-bottom: 10px solid transparent; }
.chip { height: 36px; padding: 0 14px; box-sizing: border-box; background: var(--c-shadow); border: 2px solid var(--c-text-dim);
  font: 24px var(--f-body); color: var(--c-text); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; pointer-events: auto; white-space: nowrap; }
.chip:hover { border-color: var(--c-text); color: var(--c-text-bright); }
.chip.on { background: var(--c-cider); border-color: var(--c-cider-glow); color: var(--c-shadow); }
.chip.flat { cursor: default; pointer-events: none; }
.chip.off, .chip.off:hover { border-color: #3e3020; color: var(--c-text-dim); cursor: default; }
.field { height: 48px; box-sizing: border-box; background: var(--c-ground); border: 2px solid;
  border-color: var(--bevel-dark) var(--bevel-light) var(--bevel-light) var(--bevel-dark);
  font: var(--fs-body-m) var(--f-body); color: var(--c-text-bright); padding: 0 14px; outline: none; pointer-events: auto; }
.field::placeholder { color: var(--c-text-dim); }
/* the browser's blue selection would be the only non-palette colour on screen */
.field::selection, #coopCode::selection { background: var(--c-cider); color: var(--c-shadow); }
.rail { width: 100%; height: 32px; background: var(--tx-rail); image-rendering: pixelated; flex: 0 0 auto; }
.hr { height: 2px; background: var(--bevel-dark); flex: 0 0 auto; }

/* ---------- panel stage: every framed panel is drawn at its design size and scaled as one block ---------- */
.stage { transform: scale(var(--ui-scale)); transform-origin: center center; }
/* the title and the pause screen are full 1920x1080 boards, so they get their own factor */
#title .stage, #pause .stage { transform: scale(var(--stage-scale, 1)); }
.frame { box-sizing: border-box; padding: 24px; }
.frame > .inner { height: 100%; box-sizing: border-box; background: var(--c-inner-soft);
  border: 2px solid; border-color: var(--bevel-dark) var(--bevel-light) var(--bevel-light) var(--bevel-dark);
  padding: 36px 56px; display: flex; flex-direction: column; }
.phead { display: flex; justify-content: space-between; align-items: center; flex: 0 0 auto; }
.ptitle { font: var(--fs-h3) var(--f-head); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text-h3); }
/* contrast pass (2026-09-14): a LABEL is tan (--c-text) and a VALUE is cream (--c-text-bright); only a footer stays dim.
   The dim tan was chosen against the artboards' flat ground and fell under 4.5:1 over the real panel texture. */
.psub, .pesc { font-size: 24px; line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }
.pesc { pointer-events: auto; cursor: pointer; }
.pesc:hover { color: var(--c-text-bright); }
.plabel { font: var(--fs-h4) var(--f-head); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }
.phint { font-size: 24px; line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }
.pfoot { font-size: 24px; line-height: 1; color: var(--c-text-dim); text-shadow: var(--shadow-text); }
.box { background: var(--c-inner); border: 2px solid var(--bevel-dark); box-sizing: border-box; }

/* ===================================================================== HUD ===================================================================== */
#hud { position: fixed; inset: 0; pointer-events: none; color: var(--c-text); overflow: hidden; font-family: var(--f-body); }
#hud > * { z-index: 3; }
#hud > #submerge, #hud > #pain, #hud > #vignette, #hud > #dmgarc, #hud > #lowhp, #hud > #flash { z-index: 1; }
#hud > #lens, #hud > #floattext { z-index: 2; }

/* notify column (the Quake message area): 4 lines, newest at the bottom, each one linearly fading over 3 s so the
   stack reads 100 / 70 / 35 % by age on its own. hud.js removes a line on animationend — no per-frame DOM writes. */
#notify { position: absolute; left: var(--hud-inset); top: var(--hud-inset); display: flex; flex-direction: column; gap: 4px; max-width: 46vw; }
#notify .nline { font-size: var(--fs-body-m); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); animation: nfade var(--t-notify) linear forwards; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#notify .nline.hot { color: var(--c-cider-glow); }
#notify .nline.bright { color: var(--c-text-bright); }
@keyframes nfade { from { opacity: 1; } to { opacity: 0; } }

/* teammate rows sit under the notify column at y=200: name · hp · kills, MULCHED while dead */
#teammates { position: absolute; left: var(--hud-inset); top: 200px; display: flex; flex-direction: column; gap: 6px; font-size: var(--fs-body-m); line-height: 1; text-shadow: var(--shadow-text); }
#teammates .tm { display: flex; gap: 16px; color: var(--c-text); }
#teammates .tmname { width: 96px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#teammates .tmhp { width: 48px; text-align: right; color: var(--c-ok); }
#teammates .tmhp.hurt { color: var(--c-cider-glow); }
#teammates .tmhp.low { color: var(--c-danger); }
#teammates .tmkills { width: 72px; text-align: right; }
#teammates .tm.dead { color: var(--c-text-dim); }
#teammates .tm.dead .tmhp { display: none; }
#teammates .tm.dead .tmkills { width: 136px; }

/* mode line top-right (WAVE 4 · 6 LEFT / TIME 2:14 · 23 KILLS): 2 s after a change, then out */
#modeline { position: absolute; right: var(--hud-inset); top: var(--hud-inset); font-size: var(--fs-body-m); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); opacity: 0; transition: opacity .4s; text-align: right; }
#modeline.show { opacity: .6; transition: none; }
#editorchip { position: absolute; right: var(--hud-inset); top: 76px; padding: 2px 10px; border: 2px solid var(--c-text-dim); background: var(--c-shadow); color: var(--c-text); font-size: var(--fs-body-s); line-height: 24px; pointer-events: auto; cursor: pointer; white-space: nowrap; }
#editorchip:hover { border-color: var(--c-cider); color: var(--c-text-bright); }
#editorchip i { font-style: normal; color: var(--c-cider-glow); margin-left: 8px; }

/* boss bar, top centre */
#bossbar { position: absolute; left: 50%; top: 24px; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: 8px; }
#bossbar .bossname { font: var(--fs-h4) var(--f-head); line-height: 1; color: var(--c-text-bright); text-shadow: var(--shadow-text); }
#bossbar .bar { width: 600px; max-width: 60vw; height: 20px; background: var(--c-shadow); border: 2px solid; border-color: var(--bevel-light) var(--bevel-dark) var(--bevel-dark) var(--bevel-light); box-sizing: border-box; position: relative; }
#bossbar .fill { position: absolute; left: 0; top: 0; bottom: 0; width: 100%; background: var(--c-danger); transition: width .2s; }
#bossbar .bossnum { font-size: var(--fs-body-m); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }

/* crosshair — 2 px strokes, cream (#fff0c0); the variant class is the weapon's hud.crosshair (src/weapons/<id>.js).
   EVERY stroke lands on the viewport centre BY CONSTRUCTION, because that is where the aim ray goes: #crosshair is a
   0x0 box at (50%, 50%) and each child's negative margin is exactly HALF ITS OWN border box, written as a calc over the
   size token next to it. The rings used to carry a hand-typed margin (-12px on a 20px border box, -18 on 32, -20 on 36,
   -7 on 10), which drew every ring weapon — Splinter Gun, Twin Trunk, Rip Saw, third person — 2 px up and left of the
   shot; that is the off-centre cross the 2026-09-14 review caught. Change a size and the margin now follows it. */
#crosshair { position: absolute; left: 50%; top: 50%; width: 0; height: 0;
  --ch-dot: 4px; --ch-tick: 2px; --ch-len: 10px; --ch-gap: 8px; --ch-ring: 20px; }
#crosshair .dot { position: absolute; width: var(--ch-dot); height: var(--ch-dot); margin: calc(var(--ch-dot) / -2); background: var(--c-text-bright); box-shadow: 0 0 0 1px var(--c-shadow); }
#crosshair .tick { position: absolute; background: var(--c-text-bright); box-shadow: 0 0 0 1px var(--c-shadow); transition: transform .08s; }
#crosshair .tick.n, #crosshair .tick.s { width: var(--ch-tick); height: var(--ch-len); margin-left: calc(var(--ch-tick) / -2); }
#crosshair .tick.e, #crosshair .tick.w { height: var(--ch-tick); width: var(--ch-len); margin-top: calc(var(--ch-tick) / -2); }
/* the gap is measured from the centre, so the four ticks stay mirrored as --spread (the spin-up weapon) opens them */
#crosshair .tick.n { top: calc(0px - var(--ch-gap) - var(--ch-len) - var(--spread, 0px)); }
#crosshair .tick.s { top: calc(var(--ch-gap) + var(--spread, 0px)); }
#crosshair .tick.w { left: calc(0px - var(--ch-gap) - var(--ch-len) - var(--spread, 0px)); }
#crosshair .tick.e { left: calc(var(--ch-gap) + var(--spread, 0px)); }
#crosshair .ring { position: absolute; width: var(--ch-ring); height: var(--ch-ring); margin: calc(var(--ch-ring) / -2);
  border: 2px solid var(--c-text-bright); border-radius: 50%; box-sizing: border-box; display: none; }
#crosshair.ch-ring .ring, #crosshair.ch-ring-wide .ring, #crosshair.ch-dot .ring, #crosshair.tp .ring { display: block; }
/* the ring SIZE is the variant; the order is the old precedence — third person beats ring-wide, the dot beats both */
#crosshair.ch-ring-wide { --ch-ring: 32px; }
#crosshair.tp { --ch-ring: 36px; }
#crosshair.ch-dot { --ch-ring: 10px; }
#crosshair.ch-dot .tick { display: none; }
#crosshair.hit .tick { background: var(--c-cider); transform: translate(var(--tx), var(--ty)); }
#hitmarker { position: absolute; left: 50%; top: 50%; width: 0; height: 0; opacity: 0; }
#hitmarker i { position: absolute; width: 2px; height: 26px; margin: -13px -1px; background: var(--c-danger); }
#hitmarker i:first-child { transform: rotate(45deg); } #hitmarker i:last-child { transform: rotate(-45deg); }
#hitmarker.show { opacity: 1; }

/* streak / multikill pops: 50 px off the crosshair, drifting 40 px up while they fade over 1 s */
#streak, #multikill { position: absolute; left: 50%; top: 50%; opacity: 0; text-shadow: var(--shadow-text); white-space: nowrap; }
#streak { transform: translate(50px, -50px); font: 24px/1 var(--f-head); color: var(--c-cider); }
#multikill { transform: translate(50px, 30px); font: 24px/1 var(--f-head); color: var(--c-cider-glow); }
#streak.show { animation: pop-up 1s linear forwards; }
#multikill.show { animation: pop-down 1s linear forwards; }
@keyframes pop-up { from { opacity: 1; transform: translate(50px, -50px); } to { opacity: 0; transform: translate(50px, -90px); } }
@keyframes pop-down { from { opacity: 1; transform: translate(50px, 30px); } to { opacity: 0; transform: translate(50px, -10px); } }
#camlabel { position: absolute; left: 50%; top: 60%; transform: translateX(-50%); font-size: var(--fs-body-m); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); opacity: 0; transition: opacity .3s; }
#camlabel.show { opacity: 1; transition: none; }

/* centre banners: 32 px Press Start 2P at y=320, fading IN over 2 s so nothing snaps over the crosshair */
#banner { position: absolute; left: 0; right: 0; top: var(--banner-y); text-align: center; animation: bannerin var(--t-banner-in) ease-out; }
@keyframes bannerin { from { opacity: 0; } to { opacity: 1; } }
#banner .btitle { font: var(--fs-h3)/1 var(--f-head); color: var(--c-text); text-shadow: var(--shadow-text-h3); }
#banner .bsub { margin-top: 16px; font-size: var(--fs-body-m); line-height: 1; color: var(--c-text-bright); text-shadow: var(--shadow-text); }
#banner.boss .btitle { color: var(--c-danger); }
#countdown { position: absolute; left: 50%; top: 280px; transform: translateX(-50%); font: var(--fs-h1)/1 var(--f-head); color: var(--c-text-bright); text-shadow: var(--shadow-text-h1); }

/* intermission: no dimming, no slab — a headline and a translucent stat band in the TOP THIRD, the run keeps going */
#intermission { position: absolute; left: 0; right: 0; top: 120px; display: flex; flex-direction: column; align-items: center; gap: 20px; transition: opacity .5s; }
#intermission.out { opacity: 0; }
/* the headline and the counter line float over the sky, where cream measured 1.3:1 — they now carry the band's ground */
#intermission .ititle { font: var(--fs-h2)/1 var(--f-head); color: var(--c-text-bright); text-shadow: var(--shadow-text-h1); background: rgba(18,14,11,.88); padding: 14px 32px; }
/* the band was 70 % over the world: tan labels over a bright sky came out under 3:1. At 88 % the band is its own ground */
#intermission .iband { width: min(1280px, 100vw - 64px); box-sizing: border-box; background: rgba(18,14,11,.88); border-top: 2px solid var(--bevel-light); border-bottom: 2px solid var(--bevel-dark);
  padding: 20px 0; display: grid; grid-template-columns: repeat(5, 1fr); text-align: center; row-gap: 12px; }
#intermission .iband > div { font: var(--fs-h4)/1 var(--f-head); color: var(--c-text); text-shadow: var(--shadow-text); }
#intermission .iband > b { font: var(--fs-stat)/1 var(--f-body); font-weight: normal; color: var(--c-text-bright); text-shadow: var(--shadow-text); }
#intermission .iband > b#iBonus { color: var(--c-cider-glow); }
#intermission .inext { font-size: var(--fs-body-m); line-height: 1; color: var(--c-text-bright); text-shadow: var(--shadow-text); background: rgba(18,14,11,.88); padding: 8px 22px; }

/* NEW TOOL pointer: text + a cider triangle, pushed to the screen edge by hud.js (projected pedestal position) */
#newtool { position: absolute; left: 0; top: 0; display: none; align-items: center; gap: 12px; }
#newtool.show { display: flex; }
#newtool .txt { font: var(--fs-h4)/1 var(--f-head); color: var(--c-cider-glow); text-shadow: var(--shadow-text); }
#newtool .arrow { width: 0; height: 0; border-left: 24px solid var(--c-cider); border-top: 16px solid transparent; border-bottom: 16px solid transparent; animation: nudge .6s infinite alternate; }
@keyframes nudge { from { transform: translateX(0); } to { transform: translateX(8px); } }

/* sprite digits (assets/ui/ui-digits.png at x0.2 = 54x72); a non-digit glyph (∞, :, %) falls back to the head font */
.digits { display: flex; align-items: flex-end; gap: 2px; height: var(--digit-h); }
.digits .d { width: var(--digit-w); height: var(--digit-h); background: url('../assets/ui/ui-digits.png') 0 0 / var(--digit-sheet); image-rendering: pixelated; flex: 0 0 auto; }
.digits .g { height: var(--digit-h); font: 48px/72px var(--f-head); color: var(--c-text-bright); text-shadow: var(--shadow-text-h3); flex: 0 0 auto; }
.digits.low { animation: lowpulse .5s steps(2, jump-none) infinite; }
@keyframes lowpulse { 0%, 100% { filter: none; } 50% { filter: sepia(1) saturate(8) hue-rotate(-40deg) brightness(1.2); } }
.digits.flash { filter: brightness(2.2) saturate(.4); }

/* bottom corners: health · armor · stamina left, weapon bar over ammo right — both pinned to the TRUE edges */
#hudleft { position: absolute; left: var(--hud-inset); bottom: var(--hud-inset); display: flex; align-items: flex-end; gap: 16px; }
#hudright { position: absolute; right: var(--hud-inset); bottom: var(--hud-inset); display: flex; flex-direction: column; align-items: flex-end; gap: 16px; }
.stat { display: flex; align-items: flex-end; gap: 16px; }
#armor { margin-left: 24px; }
#ammo { display: flex; align-items: flex-end; gap: 16px; }
/* health and armor are PNGs (assets/ui, see the icon block at the top of this file) drawn centred in the 56 px box.
   They used to be CSS shapes whose hand-typed offsets were 2 px past the middle of the square — the off-centre cross
   and the off-centre armor rectangle the 2026-09-14 review caught. The art carries its own frame, so .icon has none. */
.icon { width: var(--icon); height: var(--icon); box-sizing: border-box; margin-bottom: 8px; position: relative; flex: 0 0 auto;
  background-position: center center; background-repeat: no-repeat; background-size: var(--icon) var(--icon); image-rendering: pixelated; }
.icon.plus { background-image: url('../assets/ui/ui-icon-health.png'); }
.icon.plank { background-image: url('../assets/ui/ui-icon-armor.png'); }
.icon.ammo { background: var(--c-shadow); border: 2px solid var(--c-text-dim); color: var(--c-text); font: 30px/52px var(--f-body); text-align: center; }
/* stamina pips: one 20 px square per dash charge, green when spendable */
#stamina { display: flex; gap: 6px; margin-left: 24px; margin-bottom: 8px; }
#stamina i { display: block; width: var(--pip); height: var(--pip); box-sizing: border-box; background: var(--c-shadow); border: 2px solid var(--c-text-dim); }
#stamina i.full { background: var(--c-ok); border-color: var(--c-shadow); }
#stamina.empty i { animation: stamflash .3s; }
@keyframes stamflash { 0%, 100% { border-color: var(--c-text-dim); } 40% { background: var(--c-danger); border-color: var(--c-danger); } }
/* weapon bar: six 64 px slots riding above the ammo, faded in on a switch and out 1.5 s later */
#weaponbar { display: flex; gap: 8px; opacity: 0; transition: opacity .4s; }
#weaponbar.show { opacity: .85; transition: none; }
.slot { width: var(--slot); height: var(--slot); box-sizing: border-box; background: var(--c-shadow); border: 2px solid var(--c-text-dim); position: relative; opacity: .6; }
.slot .key { position: absolute; left: 4px; top: 2px; font-size: var(--fs-body-s); line-height: 1; color: var(--c-text); }
/* the slot glyph is the WEAPON's 32 px silhouette (assets/ui/ui-glyph-<id>.png, hud.js writes data-w), not the ammo
   letter it used to show: a shell reads faster than an S, and the letter was the ammo KIND, which two weapons share */
.slot .glyph { position: absolute; left: 0; right: 0; bottom: 6px; height: 32px; background-position: center bottom; background-repeat: no-repeat; background-size: 32px 32px; image-rendering: pixelated; }
.slot[data-w="nailgun"] .glyph { background-image: url('../assets/ui/ui-glyph-nailgun.png'); }
.slot[data-w="shotgun"] .glyph { background-image: url('../assets/ui/ui-glyph-shotgun.png'); }
.slot[data-w="ssg"] .glyph { background-image: url('../assets/ui/ui-glyph-ssg.png'); }
.slot[data-w="chaingun"] .glyph { background-image: url('../assets/ui/ui-glyph-chaingun.png'); }
.slot[data-w="railgun"] .glyph { background-image: url('../assets/ui/ui-glyph-railgun.png'); }
.slot[data-w="rocket"] .glyph { background-image: url('../assets/ui/ui-glyph-rocket.png'); }
.slot.owned { opacity: 1; }
.slot.owned.empty .glyph { opacity: .35; }
.slot.locked { opacity: .4; border-color: #3e3020; }
.slot.locked .key { color: var(--c-text-dim); }
.slot.locked .glyph { opacity: .5; }
.slot.current { background: var(--c-cider); border-color: var(--c-cider-glow); opacity: 1; }
.slot.current .key { color: var(--c-shadow); }
.slot.current .glyph { filter: brightness(0); }   /* the light art reads as a dark silhouette on the cider fill */
/* the pickup flash brightens whatever the slot already is (dark or cider-filled) instead of forcing a colour */
.slot.new { animation: slotnew .4s 4; }
@keyframes slotnew { 0%, 100% { filter: none; } 50% { filter: brightness(2.6); } }

/* full-screen overlays (unchanged behaviour, tokenised colours) */
#submerge { position: absolute; inset: 0; background: var(--c-cider); opacity: 0; }
#pain { position: absolute; inset: 0; background: #ff1a10; opacity: 0; }
#pain.armor { background: #d8a028; }
#vignette { position: absolute; inset: 0; background: radial-gradient(ellipse at center, rgba(255,0,0,0) 40%, rgba(200,10,10,.95) 100%); opacity: 0; }
#lowhp { position: absolute; inset: 0; background: radial-gradient(ellipse at center, rgba(120,0,0,0) 50%, rgba(110,0,10,.75) 100%); opacity: 0; }
#flash { position: absolute; inset: 0; background: #fff8b0; opacity: 0; }
#lens { position: absolute; inset: 0; width: 100%; height: 100%; }
#floattext { position: absolute; inset: 0; }
#floattext .ft { position: absolute; transform: translate(-50%, -50%); font-size: var(--fs-body-m); line-height: 1; text-shadow: var(--shadow-text); white-space: nowrap; display: none; }
/* damage arc: an 8 px red bar 400 tall (400 wide on the top / bottom edges) pinned to the hit edge for 0.4 s */
#dmgarc { position: absolute; background: var(--c-danger); opacity: 0; }
#dmgarc.e-l { left: 0; top: 50%; margin-top: -200px; width: 8px; height: 400px; }
#dmgarc.e-r { right: 0; top: 50%; margin-top: -200px; width: 8px; height: 400px; }
#dmgarc.e-t { top: 0; left: 50%; margin-left: -200px; height: 8px; width: 400px; }
#dmgarc.e-b { bottom: 0; left: 50%; margin-left: -200px; height: 8px; width: 400px; }
#dmgarc.armor { background: var(--c-brass); }
#debug { position: absolute; left: var(--hud-inset); top: 300px; font: 13px/1.4 monospace; color: #9f9; text-shadow: 1px 1px 0 #000; white-space: pre; }
/* the bench's key legend (?sandbox=1, hud.setSandboxLegend): bottom left, clear of the health row under it */
#sandbox { position: absolute; left: var(--hud-inset); bottom: 150px; display: flex; flex-direction: column; gap: 4px; }
#sandbox .srow { font-size: var(--fs-body-s); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); white-space: nowrap; }
#sandbox .srow b { display: inline-block; min-width: 32px; font-weight: normal; color: var(--c-cider-glow); }
#tags, #nametags { position: absolute; inset: 0; pointer-events: none; }

/* spectate overlay: three centred lines 64 px off the bottom; the world only loses 15 % so the team stays readable */
#spectate { position: absolute; left: 0; right: 0; bottom: 64px; display: flex; flex-direction: column; align-items: center; gap: 16px; }
#spectate::before { content: ''; position: fixed; inset: 0; background: var(--c-ground); opacity: .15; z-index: -1; }
#spectate .stitle { font: var(--fs-h3)/1 var(--f-head); color: var(--c-danger); text-shadow: var(--shadow-text-h3); }
#spectate .ssub { font-size: var(--fs-body-m); line-height: 1; color: var(--c-text-bright); text-shadow: var(--shadow-text); }
#spectate .swatch { font-size: var(--fs-body-m); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }
#spectate .swatch b { font-weight: normal; color: var(--c-text); }

/* ===================================================================== screens ===================================================================== */
.screen { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 20; color: var(--c-text); }
.screen::before { content: ''; position: absolute; inset: 0; background: var(--c-ground); opacity: .35; }
.screen > * { position: relative; }

/* ---------- title / main menu (1b, 1c) ----------
   One 1920x1080 stage so every offset in the notes lands exactly; the stage scales as a block on smaller displays. */
#title { cursor: default; }
#title .stage { width: 1920px; height: 1080px; position: relative; flex: 0 0 auto; }
#titletop { position: absolute; left: 0; right: 0; top: 144px; display: flex; flex-direction: column; align-items: center; gap: 24px; }
#wordmark { font: var(--fs-h1)/1 var(--f-head); color: var(--c-text); text-shadow: var(--shadow-text-h1); letter-spacing: 4px; }
#subtitle { font: var(--fs-h3)/1 var(--f-head); color: var(--c-cider); text-shadow: var(--shadow-text-h3); }
#titlerail { width: 832px; height: 32px; background: var(--tx-rail); image-rendering: pixelated; margin-top: 8px; }
#tagline { font-size: var(--fs-body-m); line-height: 1; color: var(--c-text-dim); text-shadow: var(--shadow-text); }
#menu { position: absolute; left: 0; right: 0; top: 520px; display: flex; flex-direction: column; align-items: center; gap: 32px; }
#menu .mitem { position: relative; width: 480px; display: flex; align-items: center; justify-content: center; font: var(--fs-h3)/1 var(--f-head); color: var(--c-text); text-shadow: var(--shadow-text-h3); pointer-events: auto; cursor: pointer; }
#menu .mitem .cursor { position: absolute; left: 24px; width: 0; height: 0; border-left: 20px solid var(--c-cider); border-top: 14px solid transparent; border-bottom: 14px solid transparent; visibility: hidden; }
#menu .mitem.on { color: var(--c-text-bright); }
#menu .mitem.on .cursor { visibility: visible; }
#title.loading #menu { opacity: .35; pointer-events: none; }
#title.loading #menu .mitem.on { color: var(--c-text); }
#title.loading #menu .cursor { visibility: hidden; }
#hint { position: absolute; left: 0; right: 0; top: 800px; text-align: center; font-size: var(--fs-body-m); line-height: 1; color: var(--c-text-dim); text-shadow: var(--shadow-text); }
#title.loading #hint { display: none; }
#loading { position: absolute; left: 0; right: 0; top: 800px; display: flex; flex-direction: column; align-items: center; gap: 12px; font-size: var(--fs-body-m); line-height: 1; color: var(--c-text-bright); text-shadow: var(--shadow-text); }
#loadsub { color: var(--c-text); }
#loadbar { width: 288px; height: 12px; box-sizing: border-box; border: 2px solid var(--c-text); background: var(--c-shadow); position: relative; }
#loadbar i { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: var(--c-cider); transition: width .3s linear; }
#best { position: absolute; left: 64px; bottom: 56px; display: flex; flex-direction: column; gap: 8px; font-size: var(--fs-body-m); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }
#title.loading #best { opacity: .5; }
#sharedbadge { position: absolute; right: 64px; top: 56px; display: flex; align-items: center; gap: 12px; background: var(--c-shadow); border: 2px solid var(--c-cider); padding: 10px 16px; font-size: var(--fs-body-m); line-height: 1; color: var(--c-cider-glow); }
#sharedbadge i { width: 12px; height: 12px; background: var(--c-cider); }
#studio { position: absolute; right: 64px; bottom: 48px; display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }
.plate { width: 256px; height: 144px; background: url('../assets/ui/ui-logo-rust.png') center/contain no-repeat; image-rendering: pixelated; }
#version { font-size: var(--fs-body-s); line-height: 1; color: var(--c-text-dim); text-shadow: var(--shadow-text); }

/* ---------- PLAY (2a) ---------- */
#play .frame { width: 1280px; height: 840px; }
#play .inner { gap: 28px; }
#play .pcols { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; flex: 1; min-height: 0; }
#play .pcol { display: flex; flex-direction: column; gap: 14px; min-height: 0; }
#play .prow { display: flex; justify-content: space-between; align-items: baseline; }
#play .plistwrap { display: flex; flex: 1; min-height: 0; }
#levelList { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; background: var(--c-inner); border: 2px solid var(--bevel-dark); box-sizing: border-box; pointer-events: auto; }
#levelList::-webkit-scrollbar { width: 20px; }
#levelList::-webkit-scrollbar-track { background: var(--c-inner); border-left: 2px solid var(--bevel-dark); }
#levelList::-webkit-scrollbar-thumb { background: var(--c-brass); border: 2px solid var(--c-inner); }
#levelList .lvl { display: flex; align-items: center; gap: 20px; height: 56px; padding: 0 20px; box-sizing: border-box; border-bottom: 2px solid var(--bevel-dark);
  font: 24px/1 var(--f-head); text-shadow: var(--shadow-text); color: var(--c-text); cursor: pointer; }
#levelList .lvl .cur { width: 16px; flex: 0 0 auto; }
#levelList .lvl .nm { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#levelList .lvl .by { font: var(--fs-body-s)/1 var(--f-body); color: var(--c-text); margin-left: auto; text-shadow: none; }
#levelList .lvl:hover { color: var(--c-text-bright); }
#levelList .lvl.on { color: var(--c-text-bright); background: var(--c-shadow); }
#levelList .lvl.on .cur { width: 0; height: 0; border-left: 16px solid var(--c-cider); border-top: 12px solid transparent; border-bottom: 12px solid transparent; }
#play .pbtns { margin-top: auto; display: flex; justify-content: flex-end; gap: 16px; }
#modeCards { display: flex; flex-direction: column; gap: 14px; }
.mcard { display: flex; flex-direction: column; border: 2px solid var(--c-text-dim); pointer-events: auto; cursor: pointer; }
.mcard .mhead { height: 56px; padding: 0 20px; box-sizing: border-box; background: var(--tx-button); background-color: #6a5a4a; background-blend-mode: multiply; image-rendering: pixelated; display: flex; align-items: center; gap: 16px; border-bottom: 2px solid var(--c-shadow); opacity: .85; }
.mcard .radio { width: 20px; height: 20px; box-sizing: border-box; border: 2px solid var(--c-text-dim); background: var(--c-inner); flex: 0 0 auto; }
.mcard .mname { font: var(--fs-h4)/1 var(--f-head); color: var(--c-text); text-shadow: var(--shadow-text); }
.mcard .mdesc { padding: 16px 20px; background: var(--c-inner); font-size: var(--fs-body-m); line-height: 1.1; color: var(--c-text); text-shadow: var(--shadow-text); }
.mcard .mcur { margin-left: auto; width: 0; height: 0; visibility: hidden; border-left: 16px solid var(--c-cider); border-top: 12px solid transparent; border-bottom: 12px solid transparent; }
.mcard.on { border-color: var(--c-cider); box-shadow: 0 0 0 2px var(--c-shadow); }
.mcard.on .mhead { opacity: 1; }
.mcard.on .radio { border-color: var(--c-cider-glow); background: var(--c-cider); }
.mcard.on .mname { color: var(--c-cider-glow); }
.mcard.on .mdesc { color: var(--c-text-bright); }
.mcard.on .mcur { visibility: visible; }

/* ---------- OPTIONS (2b) ---------- */
#options .frame { width: 1280px; height: 1000px; }
#options .inner { gap: 20px; }
#options .ocols { display: grid; grid-template-columns: 1fr 1fr; column-gap: 64px; }
#options .ocol { display: flex; flex-direction: column; }
#options .ogroup { font: var(--fs-h4)/1 var(--f-head); color: var(--c-text); text-shadow: var(--shadow-text); margin-bottom: 6px; }
.krow { display: flex; justify-content: space-between; align-items: center; gap: 16px; height: 52px; box-sizing: border-box; border-bottom: 2px solid var(--bevel-dark);
  font-size: var(--fs-body-m); line-height: 1; text-shadow: var(--shadow-text); }
.krow:last-child { border-bottom: 0; }
.krow .klabel { color: var(--c-text); }
.krow .kslots { display: flex; gap: 8px; align-items: center; }
.kslot { min-width: 48px; height: 36px; padding: 0 10px; box-sizing: border-box; background: var(--c-shadow); border: 2px solid var(--c-text-dim);
  display: inline-flex; align-items: center; justify-content: center; color: var(--c-text-bright); font-size: 24px; cursor: pointer; pointer-events: auto; white-space: nowrap; }
.kslot:hover { border-color: var(--c-text); }
.kslot.empty { color: var(--c-text-dim); }
.krow.capturing { background: var(--c-shadow); border: 2px solid var(--c-cider); margin: 0 -12px; padding: 0 12px; }
.krow.capturing .klabel { color: var(--c-text-bright); }
.krow.capturing .kslot { color: var(--c-cider); animation: blink .5s steps(1) infinite; }
.krow .kcancel { font-size: 24px; color: var(--c-text); }
@keyframes blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: .25; } }
#keynote { min-height: 30px; font-size: var(--fs-body-m); line-height: 1; color: var(--c-cider); text-shadow: var(--shadow-text); opacity: 0; transition: opacity .2s; }
#keynote.show { opacity: 1; }
#options .orow { display: flex; justify-content: space-between; align-items: center; height: 52px; font-size: var(--fs-body-m); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }
#options .chips { display: flex; gap: 8px; }
#options .ofoot2 { font-size: 24px; line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }
#options .obtm { margin-top: auto; display: flex; justify-content: space-between; align-items: flex-end; }
#options .ofoot { font-size: var(--fs-body-s); line-height: 1; color: var(--c-text-dim); text-shadow: var(--shadow-text); }
#options .pbtns { display: flex; gap: 16px; }

/* ---------- CO-OP panel (2c) ---------- */
#coop .frame { width: 1280px; height: 840px; }
#coop .inner { gap: 24px; }
#coop .cname { display: flex; align-items: center; gap: 24px; }
#coop .cname input { width: 320px; text-transform: uppercase; }
#coop .ccols { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; flex: 1; min-height: 0; }
#coop .ccol { padding: 24px; display: flex; flex-direction: column; gap: 18px; }
#coop .chead { font: var(--fs-h4)/1 var(--f-head); color: var(--c-text-bright); text-shadow: var(--shadow-text); }
#coop .crow { display: flex; align-items: flex-start; gap: 16px; }
#coop .crowlabel { font-size: 24px; line-height: 36px; color: var(--c-text); text-shadow: var(--shadow-text); width: 80px; flex: 0 0 auto; }
#coop .cseg { display: flex; gap: 8px; flex-wrap: wrap; }
#coop .cseg .chip i { font-style: normal; margin-right: 6px; }
#coop .clabel { font-size: 24px; line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }
/* the room-code entry: five 48 px Press Start 2P cells + a blinking block caret, with the real <input> laid over it */
#coopCodeBox { position: relative; height: 96px; background: var(--c-ground); border: 2px solid var(--c-cider); box-shadow: 0 0 0 2px var(--c-shadow);
  display: flex; align-items: center; justify-content: center; gap: 8px; pointer-events: auto; cursor: text; }
#coopCodeView { display: flex; align-items: center; gap: 8px; font: 48px/1 var(--f-head); color: var(--c-text-bright); text-shadow: var(--shadow-text-h1); }
#coopCodeView .cc { width: 48px; text-align: center; }
#coopCodeView .caret { width: 24px; height: 48px; background: var(--c-cider); animation: blink .5s steps(1) infinite; }
#coopCode { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; border: 0; background: none; font-size: 48px; text-align: center; pointer-events: auto; }
#coopNote { min-height: 30px; font-size: var(--fs-body-m); line-height: 1; color: var(--c-cider-glow); text-shadow: var(--shadow-text); }
#coopNote.warn { color: var(--c-danger); }
#coop .cbtn { margin-top: auto; }
#coop .cbtn .btn { width: 100%; }
#coop.busy #coopJoin, #coop.busy #coopHost { color: var(--c-text-dim); border-color: #6e6a62 var(--c-shadow) var(--c-shadow) #6e6a62; opacity: .7; pointer-events: none; }

/* ---------- LOBBY (2d) ---------- */
#lobby .frame { width: 1280px; height: 880px; }
#lobby .inner { gap: 22px; }
#lobby .lcode { padding: 20px 24px; display: flex; align-items: center; gap: 32px; border-color: var(--c-cider); box-shadow: 0 0 0 2px var(--c-shadow); }
#lobby .lcodeleft { display: flex; flex-direction: column; gap: 10px; }
#lobby .lcodelabel { font: var(--fs-h4)/1 var(--f-head); color: var(--c-text); text-shadow: var(--shadow-text); }
#lobbyCode { font: var(--fs-h1)/1 var(--f-head); color: var(--c-text-bright); text-shadow: var(--shadow-text-h1); letter-spacing: 8px; }
#lobby .lcoderight { display: flex; flex-direction: column; gap: 12px; margin-left: auto; align-items: flex-end; }
#lobbyCopied { font-size: var(--fs-body-m); line-height: 1; color: var(--c-ok); text-shadow: var(--shadow-text); opacity: 0; transition: opacity .2s; }
#lobbyCopied.show { opacity: 1; transition: none; }
#lobby .lbtns { display: flex; gap: 12px; }
#lobbyList { display: flex; flex-direction: column; }
#lobbyList .lrow { display: flex; align-items: center; gap: 24px; height: 60px; padding: 0 20px; box-sizing: border-box; border-bottom: 2px solid var(--bevel-dark); font-size: var(--fs-body-m); line-height: 1; }
#lobbyList .lrow:last-child { border-bottom: 0; }
#lobbyList .lname { width: 240px; color: var(--c-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#lobbyList .lrow.ready .lname { color: var(--c-text-bright); }
#lobbyList .lrow.empty .lname { width: auto; color: var(--c-text); }
#lobbyList .lstate { margin-left: auto; color: var(--c-text); }
#lobbyList .lstate.ready { color: var(--c-ok); }
#lobbyList .lstate.wait { color: var(--c-cider-glow); }
#lobby .lhint { font-size: var(--fs-body-m); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }
#lobby .lfoot { margin-top: auto; display: flex; justify-content: space-between; }
#lobby.joining #lobbyList, #lobby.joining .lbtns { opacity: .5; }

/* ---------- DEATH (1g, reworked 2026-09-14) ----------
   The card is sized to its CONTENT (1100 wide, ~600 tall at 1080p) instead of the old 1280x856 board, and its ground is
   translucent, so the arena the player just lost in still reads behind it. Hierarchy: an eyebrow + headline, then ONE OR
   TWO hero numbers at the 60 px stat size, then everything else in a compact 30 px block — the review's "information
   overload, focus on 1-2 most important things". */
#death { cursor: pointer; }
#death::before { opacity: .5; }
#death .stage { transform: scale(var(--card-scale, 1)); }
#death .slab { width: 1100px; box-sizing: border-box; padding: 36px 56px 28px; display: flex; flex-direction: column; align-items: center; gap: 18px;
  animation: slabin .3s ease-out; position: relative; background: rgba(20,16,12,.78); }
/* the plank grain at 55 % over that dark ground: the card reads as wood without becoming a wall */
#death .slab::before, #results .slab::before { content: ''; position: absolute; inset: 0; background: var(--tx-planks); image-rendering: pixelated; opacity: .55; pointer-events: none; }
#death .slab > *, #results .slab > * { position: relative; z-index: 1; }
@keyframes slabin { from { transform: translateY(40px) scale(var(--card-scale, 1)); opacity: 0; } to { transform: scale(var(--card-scale, 1)); opacity: 1; } }   /* keeps --card-scale: an animated transform overrides the declared one, so without the scale here the card snapped ~5 % when the slide ended on 768-px screens */
/* the eyebrow says WHICH run ended; with it the cider TIME'S UP can never be mistaken for the tan death pun */
#deathEyebrow, #resultsEyebrow { font: var(--fs-h4)/1 var(--f-head); color: var(--c-cider-glow); text-shadow: var(--shadow-text); letter-spacing: 4px; }
#deathHeadline { font: var(--fs-h3)/1 var(--f-head); color: var(--c-text-bright); text-shadow: var(--shadow-text-h3); text-align: center; }
#deathHeadline.clock, #resultsHeadline.clock { color: var(--c-cider); }
#killedBy { display: flex; align-items: center; gap: 20px; }
#killedBy .kb { font-size: var(--fs-body-m); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }
#killerFace { width: 64px; height: 64px; border: 2px solid var(--c-text); background: var(--c-shadow); image-rendering: pixelated; }
#killerName { font: 24px/1 var(--f-head); color: var(--c-text-bright); text-shadow: var(--shadow-text); }
/* the hero row: one or two 60 px numbers (WAVES + SCORE, or SCORE + KILLS in Juice Rush) and an optional smaller aside */
#deathHero { display: flex; align-items: flex-end; justify-content: center; gap: 72px; margin-top: 4px; }
#deathHero .hstat { display: flex; flex-direction: column; align-items: center; gap: 10px; }
#deathHero .hstat b { font: var(--fs-stat)/1 var(--f-body); font-weight: normal; color: var(--c-text-bright); text-shadow: var(--shadow-text-h3); }
#deathHero .hstat span { font: var(--fs-h4)/1 var(--f-head); color: var(--c-text); text-shadow: var(--shadow-text); }
#deathHero .hstat.score b { color: var(--c-cider-glow); }
#deathHero .hstat.aside b { font: var(--fs-body-l)/1 var(--f-body); }
/* everything else, three to a row: tan labels, bright values, half the height the old 40 px grid took */
#deathStats { display: grid; grid-template-columns: repeat(3, 1fr); column-gap: 48px; row-gap: 12px; width: 100%;
  font-size: var(--fs-body-m); line-height: 1; text-shadow: var(--shadow-text); }
#deathStats .row { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding-bottom: 8px; border-bottom: 2px solid var(--bevel-dark); }
#deathStats .row span { color: var(--c-text); }
#deathStats .row b { font-weight: normal; color: var(--c-text-bright); }
#death .dbtm { margin-top: 8px; display: flex; flex-direction: column; align-items: center; gap: 16px; }
/* blinks between the two cider tones — never to transparent, where the line used to vanish for half of every second */
#newhigh { font: 24px/1 var(--f-head); color: var(--c-cider); text-shadow: var(--shadow-text); animation: highblink .6s steps(1) infinite; }
@keyframes highblink { 0%, 49% { color: var(--c-cider); } 50%, 100% { color: var(--c-cider-glow); } }
#restartline { font-size: var(--fs-body-m); line-height: 1; color: var(--c-text-bright); text-shadow: var(--shadow-text); }
#deathLogo { position: absolute; right: 40px; bottom: 32px; width: 192px; height: 108px; opacity: .8; }

/* ---------- co-op results (2g, reworked 2026-09-14) ----------
   Same treatment as the death card: content height, one translucent plank ground (the .inner is only a frame line now,
   not a second fill), an eyebrow so TIME'S UP never reads like the wipe headline, and tan table headers. */
#results { cursor: pointer; }
#results::before { opacity: .5; }
#results .stage { transform: scale(var(--card-scale, 1)); }
#results .frame { width: 1100px; position: relative; background: rgba(20,16,12,.78); }
#results .inner { gap: 18px; align-items: center; height: auto; background: none; padding: 28px 44px; }
#resultsHeadline { font: var(--fs-h3)/1 var(--f-head); color: var(--c-text-bright); text-shadow: var(--shadow-text-h3); text-align: center; }
#resultsWave { font-size: var(--fs-body-l); line-height: 1; color: var(--c-text-bright); text-shadow: var(--shadow-text); text-align: center; }
#resultsTable { width: 100%; display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; font-size: var(--fs-body-m); line-height: 1; text-shadow: var(--shadow-text);
  background: var(--c-inner); border: 2px solid var(--bevel-dark); box-sizing: border-box; }
#resultsTable .rh { padding: 14px 20px; border-bottom: 2px solid var(--bevel-light); font: var(--fs-h4)/1 var(--f-head); color: var(--c-text); text-align: right; }
#resultsTable .rh.first { text-align: left; }
#resultsTable .rc { height: 60px; padding: 0 20px; box-sizing: border-box; display: flex; align-items: center; justify-content: flex-end; border-bottom: 2px solid var(--bevel-dark); color: var(--c-text); }
#resultsTable .rc.rname { justify-content: flex-start; gap: 16px; color: var(--c-text-bright); }
#resultsTable .rc.mvp { background: var(--c-shadow); }
#resultsTable .rc.score { color: var(--c-cider-glow); }
#resultsTable .rc.other { color: var(--c-text); }
#resultsTable .rrow-last .rc { border-bottom: 0; }
#resultsMvp { font-size: var(--fs-body-m); line-height: 1; color: var(--c-cider-glow); text-shadow: var(--shadow-text); }
#results .rbtm { margin-top: 4px; display: flex; flex-direction: column; align-items: center; gap: 16px; }
#resultsLine { font-size: var(--fs-body-m); line-height: 1; color: var(--c-text-bright); text-shadow: var(--shadow-text); }
#resultsLine.wait { color: var(--c-text); }

/* ---------- pause (1h) ---------- */
#pause { cursor: pointer; align-items: flex-start; }
#pause::before { opacity: .6; }
#pause .stage { width: 1920px; height: 1080px; position: relative; flex: 0 0 auto; }
/* the board hangs from the top (flex-start) and scales about its centre, so a HEIGHT-limited window (1366x657, 1280x600)
   pushed its lower half, the FULL SCREEN button with it, below the fold. Lift it by what overflows: min(0, H - 540 - 540 g)
   is 0 wherever the board already fit, so those layouts are unchanged, and makes a height-limited board fill the window */
#pause .stage { transform: translateY(min(0px, calc(100vh - 540px - 540px * var(--stage-scale, 1)))) scale(var(--stage-scale, 1)); }
#pause .plines { position: absolute; left: 0; right: 0; top: 400px; display: flex; flex-direction: column; align-items: center; gap: 40px; }
#pause .big { font: var(--fs-h1)/1 var(--f-head); color: var(--c-text-bright); text-shadow: var(--shadow-text-h1); }
#resumeline { font-size: var(--fs-body-l); line-height: 1; color: var(--c-text-bright); text-shadow: var(--shadow-text); }
#pauseHint { font-size: var(--fs-body-m); line-height: 1; color: var(--c-text); text-shadow: var(--shadow-text); }
#pause .pbottom { position: absolute; left: 0; right: 0; bottom: 56px; display: flex; justify-content: center; }

/* the big FULL SCREEN button (F1): the .btn treatment at slot height, Press Start 2P 24 (the 8 px font grid), a fixed
   width that fits "EXIT FULL SCREEN" (16 ch x 24 + 2 x 32 padding = 448) so the label flip never moves it */
.btn.fsbtn { height: var(--slot); min-width: 448px; font: 24px/1 var(--f-head); }
#fsTitle { position: absolute; right: 64px; top: 160px; }
#pause .plines .fsbtn { margin-top: 24px; }
/* in the OPTIONS header it rides the title line: 48 tall with -8 margins takes the ptitle's own 32 px, no row moves */
#options .phead .fsbtn { height: 48px; margin: -8px 0; }

/* editor test loop (?from=editor): the chips and BACK TO EDITOR buttons only exist in developer runs */
.ebtn { background: var(--c-shadow); border: 2px solid var(--c-text-dim); padding: 8px 16px; font-size: var(--fs-body-m); line-height: 1; color: var(--c-text); cursor: pointer; pointer-events: auto; white-space: nowrap; }
.ebtn:hover { border-color: var(--c-cider); color: var(--c-text-bright); }
.ebtn b { font-weight: normal; color: var(--c-cider-glow); margin-right: 8px; }
#intermission .ebtn { font-size: var(--fs-body-s); padding: 4px 10px; }

/* network toast: one line, top centre, above every screen; a new one replaces the old */
#netstatus { position: fixed; left: 50%; top: var(--toast-y); transform: translateX(-50%); z-index: 60; background: var(--c-shadow); border: 2px solid var(--c-text);
  padding: 10px 24px; font-size: var(--fs-body-m); line-height: 1; color: var(--c-text-bright); text-shadow: 2px 2px 0 var(--c-ground); pointer-events: none; white-space: nowrap; }
#netstatus.error { border-color: var(--c-danger); color: var(--c-danger); }

/* ---- touch (src/touch.js; html.touch / body.touch only — a desktop never matches) ----
   guards: no scroll, rubber band, pull-to-refresh, pinch, long-press menu or selection while a finger plays; the lobby's
   room-code field stays typeable (the inherited user-select: none blocks typing on iOS) */
html.touch, body.touch { overscroll-behavior: none; touch-action: none; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
body.touch { position: fixed; inset: 0; }
body.touch input, body.touch textarea { -webkit-user-select: text; user-select: text; touch-action: auto; }
body.touch #levelList { touch-action: pan-y; }
/* iOS sends a compat mousedown only to "clickable" targets: the layers that resume / restart on a tap must look clickable */
body.touch #pause, body.touch #death, body.touch #results, body.touch #spectate { cursor: pointer; }
/* the weapon bar is the tap strip: always shown, takes its own taps (the guards are in touch.js) */
body.touch #weaponbar { opacity: .85; pointer-events: auto; touch-action: none; }
/* only the NEXT WAVE line takes the skip tap: the whole band would swallow the stick and FIRE under it on a phone */
body.touch #intermission.tapskip .inext { pointer-events: auto; touch-action: none; }
/* the overlay: under #hud in paint order (#hud is pointer-events: none), over the canvas. Plain boxes in the HUD tokens */
/* the safe-area insets once, as variables: every touch / phone rule and touch.js read these, so a test can set them
   (a desktop browser reports 0 for env()) and a notch or home-bar phone moves the whole layout with one source */
:root { --sa-t: env(safe-area-inset-top, 0px); --sa-r: env(safe-area-inset-right, 0px); --sa-b: env(safe-area-inset-bottom, 0px); --sa-l: env(safe-area-inset-left, 0px); }
#touch { position: fixed; inset: 0; touch-action: none; -webkit-user-select: none; user-select: none; }
/* the buttons: the panel bevel (2 px, light top-left / dark bottom-right) over --c-ground at 60 %, a VT323 label sized by
   touch.js from the button (16-30 px); held = the pressed bevel (dark top-left) in cider. No radius, no PNG */
#touch .tbtn { position: absolute; box-sizing: border-box; display: flex; align-items: center; justify-content: center;
  font: 30px/1 var(--f-body); letter-spacing: 1px; color: var(--c-text-bright); text-shadow: 2px 2px 0 var(--c-shadow); background: rgba(20,16,12,.6);
  border: 2px solid; border-color: var(--bevel-light) var(--bevel-dark) var(--bevel-dark) var(--bevel-light); box-shadow: 0 0 0 2px rgba(20,16,12,.6); }
#touch .tbtn.tfire { color: var(--c-cider-glow); }
#touch .tbtn.on { color: var(--c-shadow); text-shadow: none; background: var(--c-cider); border-color: var(--c-shadow) var(--c-cider-glow) var(--c-cider-glow) var(--c-shadow); }
/* PAUSE: two 6 x 20 bars, the classic sign — a phone has no ESC, this is the way out */
#touch .tpause { top: calc(8px + var(--sa-t)); right: calc(8px + var(--sa-r)); width: 56px; height: 56px; }
#touch .tpause::before, #touch .tpause::after { content: ''; width: 6px; height: 20px; margin: 0 3px; background: var(--c-text-bright); box-shadow: 2px 2px 0 var(--c-shadow); }
#touch .tpause.on::before, #touch .tpause.on::after { background: var(--c-shadow); box-shadow: none; }
#touch .tring { position: absolute; pointer-events: none; }
#touch .tring canvas, #touch .tknob canvas { display: block; image-rendering: pixelated; }
#touch .tring.ghost { opacity: .35; }
#touch .tknob { position: absolute; left: 50%; top: 50%; }
#tsafe { position: fixed; left: 0; top: 0; width: 0; height: 0; visibility: hidden; pointer-events: none;
  padding: var(--sa-t) var(--sa-r) var(--sa-b) var(--sa-l); }
#rotate { display: none; }
@media (orientation: portrait) {
  body.touch #rotate { display: flex; position: fixed; inset: 0; z-index: 70; flex-direction: column; align-items: center; justify-content: center; gap: 40px; padding: 24px;
    text-align: center; background: var(--c-ground); color: var(--c-text-bright); font: 16px/1.5 var(--f-head); }
  /* a phone outline (a 4 px box) tipping a quarter turn in two steps */
  body.touch #rotate::before { content: ''; width: 40px; height: 72px; box-sizing: border-box; border: 4px solid var(--c-text);
    border-top-width: 10px; border-bottom-width: 14px; animation: rotnudge 1.2s steps(1) infinite; }
}
@keyframes rotnudge { 0%, 49% { transform: rotate(0deg); } 50%, 100% { transform: rotate(-90deg); border-color: var(--c-cider); } }

/* ---- the phone layout: body.phone = body.touch AND a short side of at most 500 px (hud.js resizeUi). Numbers in
   design/ui/NOTES-mobile.md. Text drops one grid step (Press Start 2P 16, VT323 20 / 30); the PNG corner art draws at
   0.5 (2 px art pixels -> 1 px, still crisp); the panels drop their scaled 1280-px boards for a viewport-filling compact
   variant where every tappable row is at least 48 px tall; the middle third holds only the crosshair and the countdown */
body.phone { --fs-h1: 32px; --fs-h2: 24px; --fs-h3: 16px; --fs-h4: 16px; --fs-body-l: 30px; --fs-body-m: 20px; --fs-body-s: 20px; --fs-stat: 40px;
  --hud-inset: 8px; --slot: 56px; --banner-y: 64px; --toast-y: 64px; }
/* play HUD: health top-left, the weapon strip bottom-centre, ammo under PAUSE top-right, prints under the health */
body.phone #hudleft { top: calc(8px + var(--sa-t)); left: calc(8px + var(--sa-l)); bottom: auto; transform: scale(.5); transform-origin: 0 0; align-items: flex-start; }
body.phone #hudright { top: calc(68px + var(--sa-t)); right: calc(8px + var(--sa-r)); bottom: auto; }
body.phone #ammo { transform: scale(.5); transform-origin: 100% 0; }
/* the weapon strip at the bottom centre (where the mobile shooters keep their weapon slots), only the owned weapons: a
   short strip keeps out of the stick zone on a small phone */
body.phone #weaponbar { position: fixed; bottom: calc(8px + var(--sa-b)); left: calc(50% + (var(--sa-l) - var(--sa-r)) / 2); transform: translateX(-50%); gap: 4px; }
body.phone #weaponbar .slot:not(.owned) { display: none; }
body.phone .slot .key { display: none; }
body.phone #notify { gap: 2px; top: calc(52px + var(--sa-t)); left: calc(8px + var(--sa-l)); max-width: min(250px, calc(33vw - 16px)); }
/* co-op rows at the top centre, above the boss bar (the left FIRE owns the space under the prints, and a long print
   runs under where a row right of the health would sit): name + health, no kills */
body.phone #teammates { top: calc(8px + var(--sa-t)); left: calc(50% + (var(--sa-l) - var(--sa-r)) / 2); transform: translateX(-50%); gap: 2px; font-size: 16px; }
body.phone #teammates .tm { gap: 8px; }
body.phone #teammates .tmname { width: 64px; }
body.phone #teammates .tmhp { width: 40px; }
body.phone #teammates .tmkills { display: none; }
body.phone #modeline { top: calc(104px + var(--sa-t)); right: calc(8px + var(--sa-r)); }
body.phone #editorchip { top: calc(8px + var(--sa-t)); right: calc(72px + var(--sa-r)); }   /* left of PAUSE: JUMP now starts at 128-138 under the mode line */
body.phone #bossbar { top: calc(68px + var(--sa-t)); gap: 4px; }
body.phone #bossbar .bar { width: min(300px, 33vw); height: 12px; }
body.phone #bossbar .bossname { font: 20px/1 var(--f-body); }   /* the name in Press Start 2P ran past the bar into the prints on 667 */
body.phone #banner .bsub { margin-top: 8px; }
body.phone #intermission { top: calc(60px + var(--sa-t)); gap: 6px; }
body.phone #intermission .ititle { padding: 6px 16px; }
body.phone #intermission .iband { display: none; }   /* the tally would fill the middle third; the death / results cards carry the run's numbers */
body.phone #intermission .inext { padding: 14px 16px; }   /* the NEXT WAVE line is the skip tap: 48 px tall */
body.phone #spectate { bottom: calc(72px + var(--sa-b)); gap: 6px; }
body.phone #camlabel { top: 70%; }
/* panels: no scaled board, the viewport minus 8 px, the inner box scrolls (pan-y) when a list is long */
body.phone #title .stage, body.phone #pause .stage, body.phone #play .stage, body.phone #options .stage, body.phone #coop .stage,
body.phone #lobby .stage, body.phone #death .stage, body.phone #results .stage { transform: none; }
body.phone #play .stage, body.phone #options .stage, body.phone #coop .stage, body.phone #lobby .stage {
  width: calc(100% - 16px - var(--sa-l) - var(--sa-r)); height: calc(100% - 16px); padding: 4px;
  margin: 0 var(--sa-r) 0 var(--sa-l); }   /* centred between the side insets (a notch on one side only too). Not the bottom
  inset: the PLAY panel then scrolls START half out of sight at 844 x 390 (measured); the home bar only covers the frame edge */
body.phone .frame > .inner { padding: 8px 12px; gap: 8px; overflow-y: auto; overflow-x: hidden; touch-action: pan-y; overscroll-behavior: contain; }
body.phone .phead { flex-wrap: wrap; gap: 8px; }
body.phone .rail { height: 8px; }
body.phone .psub, body.phone .pfoot { font-size: 20px; }
body.phone .phint { display: none; }
body.phone .pesc { min-height: 48px; display: flex; align-items: center; padding: 0 12px; font: 16px/1 var(--f-head); border: 2px solid var(--c-text-dim); background: var(--c-shadow); box-sizing: border-box; }
body.phone .btn { height: 48px; padding: 0 16px; }
body.phone .chip { height: 48px; min-width: 48px; padding: 0 12px; font-size: 20px; }
body.phone .field { height: 48px; font-size: 20px; }
body.phone .plabel { font-size: 16px; }
body.phone #play .inner, body.phone #options .inner, body.phone #coop .inner, body.phone #lobby .inner { gap: 8px; }
body.phone #play .pcols { gap: 12px; min-height: 180px; }
body.phone #play .pcol { gap: 8px; }
body.phone #levelList .lvl { height: 48px; padding: 0 12px; gap: 12px; font-size: 16px; }
body.phone #modeCards { gap: 8px; }
body.phone .mcard .mhead { height: 48px; padding: 0 12px; }
body.phone .mcard:not(.on) .mdesc { display: none; }
body.phone .mcard .mdesc { padding: 8px 12px; }
body.phone #options .ocols { column-gap: 16px; }
body.phone #options .orow { min-height: 52px; height: auto; gap: 8px; }
body.phone #options .chips { flex-wrap: wrap; justify-content: flex-end; }
body.phone #options .ofoot2 { font-size: 20px; line-height: 1.1; }
body.phone #options .obtm { gap: 8px; flex-wrap: wrap; }
/* DONE / RESET DEFAULTS ride the bottom of the scrolling panel: the list is taller than a phone, and a tap must leave OPTIONS
   without first finding the swipe. -8 px + 8 px padding cover the inner's own bottom padding; the opaque fill hides the rows */
body.phone #options .obtm { position: sticky; bottom: -8px; padding: 8px 0; background: var(--c-inner); z-index: 1; }
body.phone #options .pbtns, body.phone #play .pbtns, body.phone #lobby .lbtns { gap: 8px; }
body.phone #options .phead .fsbtn { height: 48px; margin: 0; }
body.phone #coop .cname { gap: 12px; }
body.phone #coop .cname input { width: 200px; }
body.phone #coop .ccols { gap: 8px; min-height: auto; }
body.phone #coop .ccol { padding: 8px; gap: 8px; }
body.phone #coop .crowlabel { font-size: 20px; line-height: 48px; width: 56px; }
body.phone #coopCodeBox { height: 56px; }
body.phone #coopCodeView { font-size: 24px; }
body.phone #coopCodeView .cc { width: 24px; }
body.phone #coopCodeView .caret { width: 12px; height: 24px; }
body.phone #lobby .lcode { padding: 8px 12px; gap: 12px; flex-wrap: wrap; }
body.phone #lobbyCode { letter-spacing: 4px; }
body.phone #lobbyList .lrow { height: 40px; padding: 0 12px; }
body.phone #lobby .lfoot { gap: 8px; }
/* the big FULL SCREEN button: 48 tall, Press Start 2P 16; 288 px fits EXIT FULL SCREEN, so the flip never moves it */
body.phone .btn.fsbtn { height: 48px; min-width: 288px; font-size: 16px; }
/* title: two columns — the wordmark, tagline and best lines left, the menu and FULL SCREEN right */
body.phone #title .stage, body.phone #pause .stage { width: 100%; height: 100%; }
body.phone #titletop { left: calc(16px + var(--sa-l)); right: 50%; top: 16px; align-items: flex-start; gap: 8px; }
body.phone #wordmark { letter-spacing: 0; }
body.phone #titlerail { display: none; }
body.phone #tagline { line-height: 1.1; }
body.phone #menu { left: 50%; right: calc(16px + var(--sa-r)); top: 16px; gap: 8px; align-items: stretch; }
body.phone #menu .mitem { width: auto; height: 48px; font-size: 16px; border: 2px solid var(--c-text-dim); background: rgba(20,16,12,.6); box-sizing: border-box; }
body.phone #menu .mitem.on { border-color: var(--c-cider); }
body.phone #menu .mitem .cursor { left: 12px; border-left-width: 12px; border-top-width: 8px; border-bottom-width: 8px; }
body.phone #hint { left: 50%; right: calc(16px + var(--sa-r)); top: 192px; font-size: 20px; }
body.phone #loading { left: 50%; right: calc(16px + var(--sa-r)); top: 192px; gap: 8px; }
body.phone #loadbar { width: 100%; max-width: 288px; }
body.phone #best { left: calc(16px + var(--sa-l)); bottom: calc(16px + var(--sa-b)); right: calc(50% + 8px); gap: 4px; }
body.phone #sharedbadge { left: calc(16px + var(--sa-l)); right: auto; top: 128px; padding: 6px 8px; max-width: calc(50% - 32px); font-size: 20px; }
body.phone #studio { display: none; }
body.phone #fsTitle { right: calc(16px + var(--sa-r)); top: auto; bottom: calc(16px + var(--sa-b)); }
/* pause: the lines at a fifth of the height, the FULL SCREEN button under them */
body.phone #pause .plines { top: 20%; gap: 16px; padding: 0 16px; }
body.phone #pauseHint { text-align: center; line-height: 1.1; }
body.phone #pause .pbottom { bottom: 8px; }
/* the death and results cards: the viewport's width, content height, scroll if a big table runs long */
body.phone #death .stage, body.phone #results .stage { width: calc(100% - 16px); max-height: calc(100% - 16px); overflow-y: auto; touch-action: pan-y; animation: none; }
body.phone #death .slab { padding: 12px 16px; gap: 8px; }
body.phone #killedBy { gap: 12px; }
body.phone #killerFace { width: 32px; height: 32px; }
body.phone #killerName { font-size: 16px; }
body.phone #deathHero { gap: 32px; margin-top: 0; }
body.phone #deathHero .hstat { gap: 4px; }
body.phone #deathStats { column-gap: 16px; row-gap: 4px; }
body.phone #deathStats .row { padding-bottom: 4px; }
body.phone #death .dbtm, body.phone #results .rbtm { margin-top: 0; gap: 8px; }
body.phone #newhigh { font-size: 16px; }
body.phone #deathLogo { display: none; }
body.phone #results .inner { padding: 12px 16px; gap: 8px; }
body.phone #resultsTable .rh { padding: 8px 12px; }
body.phone #resultsTable .rc { height: 36px; padding: 0 12px; }
