:root {
  --amber: #ffb340;
  --amber-dim: #c8862b;
  --blood: #c8202a;
  --ink: #0a0d12;
  --paper: #efe6d2;
  --green: #7ddc6a;
  /* No webfont is loaded — no build step, no CDN, no offline failure mode — so
     these stacks have to land on something condensed on every desktop OS by
     themselves. macOS takes DIN, Windows takes Bahnschrift, Linux takes the
     Liberation/DejaVu condensed cuts, and Arial Narrow is the floor. */
  --ui: 'Rajdhani', 'Bahnschrift', 'DIN Alternate', 'Roboto Condensed',
        'Liberation Sans Narrow', 'Arial Narrow', Arial, sans-serif;
  --num: 'DIN Condensed', 'Bahnschrift Condensed', 'Bahnschrift', 'Oswald',
         'Roboto Condensed', 'Arial Narrow', Impact, sans-serif;

  /* ONE gutter for the whole HUD. Every corner block hangs off this, so the
     four corners stay on the same optical margin instead of drifting apart
     every time one of them is nudged. */
  --gut: 38px;
  --gut-b: 30px;
  /* Radar diameter. hud.js reads this off :root rather than carrying its own
     copy, so the canvas backing store cannot drift out of step with the dial. */
  --rd: 118px;
  /* Two-stop shadow: a tight 1 px contact line so glyph edges stay crisp on a
     bright wall, plus a wide soft one so the text has a body behind it. A
     single blurred shadow does one job or the other, never both. */
  --tshadow: 0 1px 0 rgba(0,0,0,0.9), 0 2px 10px rgba(0,0,0,0.8);
  --tshadow-lite: 0 1px 0 rgba(0,0,0,0.8), 0 1px 6px rgba(0,0,0,0.6);
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; background: #05070a; }
body {
  font-family: var(--ui);
  color: var(--paper);
  user-select: none;
  -webkit-user-select: none;
  letter-spacing: 0.03em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Every number in this HUD changes while you are reading it. Proportional
   digits make the whole block twitch sideways on every kill; tabular figures
   are the single cheapest thing that makes a game UI look shipped. */
#points, #pointsDelta, #roundNum, #ammoMag, #ammoRes, #fragCount, #nextWave,
#roomCode, #lobbyCode, .sb-row, .gb, .sq .hp, #fps, #promptCost {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
#app { position: fixed; inset: 0; }
canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.hidden { display: none !important; }

/* ============================ HUD ============================ */
#hud { position: absolute; inset: 0; pointer-events: none; z-index: 10; }

/* The LENS vignette now lives in the composite shader, where it is aspect-
   correct and sits under the grain and the tone map like a real one. What is
   left here is not a vignette at all — it is a READABILITY SCRIM: two shallow
   linear washes top and bottom that give the corner HUD blocks something to sit
   on when the player walks into a lit room. Doubling the radial darkening in
   both places was crushing the frame edges twice. */
#vignette {
  position: absolute; inset: 0;
  background:
    linear-gradient(to top, rgba(0,0,0,0.46) 0%, rgba(0,0,0,0.16) 14%, transparent 30%),
    linear-gradient(to bottom, rgba(0,0,0,0.34) 0%, rgba(0,0,0,0.10) 12%, transparent 24%),
    radial-gradient(ellipse at center, transparent 62%, rgba(0,0,0,0.22) 100%);
}
#dmgflash {
  position: absolute; inset: 0; opacity: 0;
  background: radial-gradient(ellipse at center, transparent 30%, rgba(150,10,10,0.75) 100%);
  transition: opacity 0.28s ease-out;
}
/* Low health reads as blood in the periphery. Pulled back from 0.62 because
   the composite pass now runs its own damage grade underneath — two full-frame
   reds stacked went opaque and cost more visibility than the state was worth. */
#lowhp {
  position: absolute; inset: 0; opacity: 0;
  background: radial-gradient(ellipse at center, transparent 22%, rgba(122,4,4,0.50) 96%);
  transition: opacity 0.4s;
}
#effectTint { position: absolute; inset: 0; opacity: 0; transition: opacity 0.5s; mix-blend-mode: screen; }

/* Crosshair. A white bar on a bright wall disappears; the fix is not more
   white, it is a hard 1 px black keyline on all four sides of every stroke, so
   the reticle carries its own contrast whatever it is standing on. */
#crosshair.ch-hide { opacity: 0 !important; }
#crosshair {
  position: absolute; left: 50%; top: 50%; width: 40px; height: 40px;
  transform: translate(-50%, -50%);
  transition: opacity 0.15s;
}
.ch {
  position: absolute; background: rgba(246,246,240,0.94);
  box-shadow: 0 0 0 1px rgba(0,0,0,0.72), 0 0 4px rgba(0,0,0,0.85);
  border-radius: 0.5px;
}
.ch-t { left: 50%; top: 0; width: 2px; height: 7px; margin-left: -1px; }
.ch-b { left: 50%; bottom: 0; width: 2px; height: 7px; margin-left: -1px; }
.ch-l { top: 50%; left: 0; height: 2px; width: 7px; margin-top: -1px; }
.ch-r { top: 50%; right: 0; height: 2px; width: 7px; margin-top: -1px; }
.ch-dot {
  position: absolute; left: 50%; top: 50%; width: 2px; height: 2px;
  margin: -1px 0 0 -1px; background: rgba(255,255,255,0.82); border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.6);
}

#hitmarker {
  position: absolute; left: 50%; top: 50%; width: 26px; height: 26px;
  transform: translate(-50%, -50%) scale(1.4); opacity: 0;
}
#hitmarker span {
  position: absolute; width: 9px; height: 2px; background: #fff;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.55), 0 0 5px rgba(0,0,0,0.9);
}
#hitmarker span:nth-child(1) { left: 0; top: 3px; transform: rotate(45deg); }
#hitmarker span:nth-child(2) { right: 0; top: 3px; transform: rotate(-45deg); }
#hitmarker span:nth-child(3) { left: 0; bottom: 3px; transform: rotate(-45deg); }
#hitmarker span:nth-child(4) { right: 0; bottom: 3px; transform: rotate(45deg); }
#hitmarker.hit { animation: hm 0.2s ease-out; }
#hitmarker.kill span { background: #ff5a4a; }
#hitmarker.head span { background: #ffd24a; }
#hitmarker.armor span { background: #6ee7ff; box-shadow: 0 0 5px #6ee7ff; }
@keyframes hm {
  0% { opacity: 1; transform: translate(-50%,-50%) scale(0.6); }
  100% { opacity: 0; transform: translate(-50%,-50%) scale(1.5); }
}

/* sniper scope */
#scope { position: absolute; inset: 0; pointer-events: none; }
#scope .scope-inner {
  position: absolute; left: 50%; top: 50%; width: 76vh; height: 76vh;
  transform: translate(-50%,-50%); border-radius: 50%;
  box-shadow: 0 0 0 100vmax #000, inset 0 0 90px 30px rgba(0,0,0,0.92);
}
#scope .scope-ret {
  position: absolute; left: 50%; top: 50%; width: 76vh; height: 76vh;
  transform: translate(-50%,-50%);
}
#scope .sr-v { position: absolute; left: 50%; top: 6%; bottom: 6%; width: 1.5px; margin-left: -0.75px; background: rgba(10,10,10,0.9); }
#scope .sr-h { position: absolute; top: 50%; left: 6%; right: 6%; height: 1.5px; margin-top: -0.75px; background: rgba(10,10,10,0.9); }
#scope .sr-dot { position: absolute; left: 50%; top: 50%; width: 3px; height: 3px; margin: -1.5px 0 0 -1.5px; background: rgba(10,10,10,0.95); border-radius: 50%; }
#scope .sr-mil {
  position: absolute; left: 50%; top: calc(50% + var(--i) * 5.2%);
  width: 12px; height: 1.5px; margin-left: -6px; background: rgba(10,10,10,0.85);
}
#scope .sr-mil2 {
  position: absolute; top: 50%; left: calc(50% + var(--i) * 5.2%);
  width: 1.5px; height: 12px; margin-top: -6px; background: rgba(10,10,10,0.85);
}
#scope .scope-glare {
  position: absolute; left: 50%; top: 50%; width: 76vh; height: 76vh;
  transform: translate(-50%,-50%); border-radius: 50%;
  background: radial-gradient(circle at 32% 26%, rgba(150,190,230,0.16), transparent 45%),
              radial-gradient(circle at 70% 78%, rgba(90,120,160,0.10), transparent 40%);
}

/* ---------------------------------------------------------------------------
   The two corner blocks.

   Hierarchy, in the order the eye should hit them: ROUND (how bad is it), AMMO
   (can I keep shooting), POINTS (can I buy), everything else. That ordering is
   carried by SIZE and by COLOUR TEMPERATURE, not by position alone — the round
   is the only blood-red thing in the corner and the only one with a rule under
   it, ammo is the only 56 px figure on the right, and points sits a full step
   down in both size and weight.

   Both blocks share one baseline grid off --gut so the left and right corners
   read as one piece of furniture rather than two widgets that happen to be
   near the same edge.
   ------------------------------------------------------------------------- */
#blockLeft { position: absolute; left: var(--gut); bottom: var(--gut-b); }
#roundRow {
  display: flex; align-items: baseline; gap: 12px; margin-top: 4px;
  padding-top: 7px; border-top: 1px solid rgba(200,32,42,0.34);
}
#roundLabel {
  font-size: 11px; letter-spacing: 0.42em; font-weight: 700;
  color: rgba(214,60,58,0.92); text-shadow: var(--tshadow-lite);
}
#roundNum {
  font-family: var(--num); font-size: 58px; line-height: 0.82; font-weight: 700;
  letter-spacing: 0.01em; color: #d8242c;
  text-shadow: 0 0 26px rgba(200,32,42,0.5), var(--tshadow);
}
#roundNum.pulse { animation: rpulse 1.1s cubic-bezier(.16,1,.3,1); }
@keyframes rpulse {
  0% { transform: scale(1.55); color: #ff6a58; text-shadow: 0 0 46px rgba(255,90,74,0.9), var(--tshadow); }
  100% { transform: scale(1); }
}
#pointsRow { display: flex; align-items: baseline; gap: 9px; }
#points {
  font-family: var(--num); font-size: 38px; line-height: 1; font-weight: 700;
  letter-spacing: 0.02em; color: var(--amber);
  text-shadow: 0 0 18px rgba(255,179,64,0.30), var(--tshadow);
}
/* The currency mark is a label, not a digit — half the size and dimmed so it
   never competes with the number it is qualifying. */
#points::before {
  content: '$'; font-size: 20px; opacity: 0.55; margin-right: 3px;
  vertical-align: 0.06em; letter-spacing: 0;
}
#pointsDelta {
  font-family: var(--num); font-size: 21px; font-weight: 700; opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.18s ease-out, transform 0.18s ease-out;
  text-shadow: var(--tshadow-lite);
}
#pointsDelta.up { color: var(--green); }
#pointsDelta.down { color: #ff6a5a; }
#pointsDelta.show { opacity: 1; transform: none; }

#perkRow { display: flex; gap: 8px; margin-bottom: 12px; }
.perk {
  width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center;
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.04em;
  /* A dark bezel OUTSIDE the light ring: the icon then reads against a lit
     wall and against a black corridor without changing anything else. */
  border: 1.5px solid rgba(255,255,255,0.34);
  box-shadow:
    0 0 0 1.5px rgba(0,0,0,0.62),
    0 3px 10px rgba(0,0,0,0.65),
    inset 0 1px 0 rgba(255,255,255,0.34),
    inset 0 -5px 10px rgba(0,0,0,0.34);
  animation: perkin 0.52s cubic-bezier(.2,1.6,.4,1);
  text-shadow: 0 1px 2px rgba(0,0,0,0.85);
}
@keyframes perkin { 0% { transform: scale(0) rotate(-90deg); } 100% { transform: none; } }

/* bottom-right block */
#blockRight { position: absolute; right: var(--gut); bottom: var(--gut-b); text-align: right; }
#weaponSlots {
  display: flex; justify-content: flex-end; gap: 5px; margin-bottom: 5px;
  min-height: 16px; font-family: var(--num); font-size: 9px; letter-spacing: 0.08em;
}
.weapon-slot {
  max-width: 112px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding: 3px 6px; color: rgba(239,230,210,0.46); background: rgba(5,8,12,0.55);
  border: 1px solid rgba(239,230,210,0.14); border-radius: 2px;
}
.weapon-slot.active { color: var(--amber); border-color: rgba(255,179,64,0.55); }
.weapon-slot.locked { opacity: 0.28; }
#weaponName {
  font-size: 13px; letter-spacing: 0.28em; opacity: 0.72; margin-bottom: 1px;
  text-transform: uppercase; text-shadow: var(--tshadow-lite);
}
#weaponName.pap { color: #c9a6ff; opacity: 1; text-shadow: 0 0 14px rgba(143,90,255,0.75), var(--tshadow-lite); }
#ammoRow {
  font-family: var(--num); font-weight: 700; line-height: 0.88;
  display: flex; align-items: baseline; justify-content: flex-end; gap: 1px;
}
#ammoMag { font-size: 56px; letter-spacing: 0.01em; text-shadow: var(--tshadow); }
/* Low ammo is a STATE, not a strobe. A blinking opacity makes the number
   unreadable exactly when you most need to read it, so this holds the digits at
   full strength and pulses the glow behind them instead. */
#ammoMag.low { color: #ff7a52; animation: lowpulse 1.05s ease-in-out infinite; }
@keyframes lowpulse {
  0%, 100% { text-shadow: 0 0 10px rgba(255,90,50,0.30), var(--tshadow); }
  50%      { text-shadow: 0 0 22px rgba(255,90,50,0.85), var(--tshadow); }
}
@keyframes lowblink { 50% { opacity: 0.45; } }
#ammoSep { font-size: 24px; opacity: 0.34; margin: 0 5px; font-weight: 400; }
#ammoRes { font-size: 26px; opacity: 0.66; text-shadow: var(--tshadow-lite); }
#gearRow {
  margin-top: 6px; font-size: 13px; opacity: 0.78; letter-spacing: 0.16em;
  text-shadow: var(--tshadow-lite);
}
#fragIcon { color: var(--amber); margin-right: 6px; opacity: 0.95; }
/* Packs are dimmed rather than hidden at zero: a counter that vanishes when
   empty never teaches anyone the mechanic is there. */
#packRow { margin-left: 16px; }
#packRow.empty { opacity: 0.36; }
#packRow.using { animation: packpulse 0.55s ease-in-out infinite; }
#packIcon { color: #3ff2b0; margin-right: 6px; opacity: 0.95; }
@keyframes packpulse { 50% { opacity: 0.42; } }

/* ---------------------------------------------------------------------------
   Health. The one meter that is always on screen, so it has to survive being
   seen only in peripheral vision: it carries its state in HUE (bone → amber →
   blood), not in length alone, because a bar you are not looking at reads as a
   colour long before it reads as a width.
   Bone rather than green at full: the palette already spends green on pickups,
   and an operator's body going from bone to blood is the reading this game
   wants anyway.
   --------------------------------------------------------------------------- */
#healthWrap {
  position: absolute; left: 50%; bottom: 92px; transform: translateX(-50%);
  width: 190px; transition: opacity 0.25s;
}
#healthWrap.downed { opacity: 0.45; }
#shieldStatus {
  position: absolute; right: 0; top: -2px; font-family: var(--num); font-size: 10px;
  letter-spacing: 0.14em; color: #72cfff; text-shadow: 0 0 8px rgba(70,170,255,0.7), var(--tshadow-lite);
}
#shieldStatus.depleted { opacity: 0.42; }
#healthNum {
  font-family: var(--num); font-size: 13px; line-height: 1; font-weight: 700;
  letter-spacing: 0.07em; opacity: 0.6; margin-bottom: 3px;
  text-shadow: var(--tshadow-lite);
}
#healthTrack {
  position: relative; height: 7px; border-radius: 2px; overflow: hidden;
  background: rgba(255,255,255,0.085);
  box-shadow: 0 0 0 1px rgba(0,0,0,0.55), inset 0 1px 2px rgba(0,0,0,0.5);
}
#healthTrack > div { position: absolute; left: 0; top: 0; height: 100%; }
/* Where the pack in flight is going to land — drawn BEHIND the bar, in the
   powerup's own mint, so it reads as health arriving rather than health held. */
#healthGhost {
  width: 0; background: rgba(63,242,176,0.32);
  box-shadow: inset 0 0 7px rgba(63,242,176,0.4);
}
/* A short lag, not a soft one. 0.1s smooths the server's 20 Hz steps into a
   slide; anything longer starts hiding the moment you got hit, which is the
   one thing this bar must never do. */
#healthBar { width: 100%; transition: width 0.1s linear, background-color 0.3s; }
/* The regen tick lives just inside the leading edge of the fill: dim while the
   post-hit delay runs down, lit and breathing once health is actually coming
   back. Two states, one element, no layout thrash. */
#healthRegen {
  width: 15px; margin-left: -15px; opacity: 0;
  background: linear-gradient(90deg, rgba(125,220,106,0), rgba(125,220,106,0.9));
}
#healthRegen.on { opacity: 1; animation: regenpulse 0.9s ease-in-out infinite; }
@keyframes regenpulse { 50% { opacity: 0.35; } }

/* stamina */
#staminaWrap {
  position: absolute; left: 50%; bottom: 82px; transform: translateX(-50%);
  width: 148px; height: 3px; background: rgba(255,255,255,0.10); opacity: 0;
  transition: opacity 0.3s; border-radius: 2px; overflow: hidden;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.45);
}
#staminaWrap.show { opacity: 1; }
#staminaBar {
  height: 100%; width: 100%;
  background: linear-gradient(90deg, var(--amber-dim), var(--amber));
  box-shadow: 0 0 8px rgba(255,179,64,0.45);
}

/* Banners. The old animation opened at 0.40em tracking and closed at 0.14em —
   a 3x letter-spacing sweep that reflows the whole string every frame and reads
   as a slide transition. Cut to a small settle: the type arrives already set,
   and only the last few percent of the tracking and the scale move. */
#banner {
  position: absolute; left: 50%; top: 10.5%; transform: translateX(-50%);
  font-family: var(--num); font-size: 54px; font-weight: 700; letter-spacing: 0.16em;
  text-align: center; opacity: 0; white-space: nowrap;
  text-shadow: 0 0 34px rgba(0,0,0,0.95), 0 3px 8px #000;
}
#banner.show { animation: bannerIn 3.4s cubic-bezier(.16,1,.3,1) forwards; }
@keyframes bannerIn {
  0%   { opacity: 0; transform: translateX(-50%) scale(1.12); letter-spacing: 0.30em; }
  12%  { opacity: 1; transform: translateX(-50%) scale(1); letter-spacing: 0.16em; }
  76%  { opacity: 1; }
  100% { opacity: 0; }
}
#subBanner {
  position: absolute; left: 50%; top: 18.4%; transform: translateX(-50%);
  font-size: 14px; letter-spacing: 0.34em; opacity: 0; text-align: center;
  text-transform: uppercase; color: rgba(239,230,210,0.78);
  text-shadow: var(--tshadow); white-space: nowrap;
}
#subBanner.show { animation: bannerIn 3.4s cubic-bezier(.16,1,.3,1) forwards; }

#effectRow {
  position: absolute; left: 50%; top: 24px; transform: translateX(-50%);
  display: flex; gap: 10px;
}
.effpill {
  padding: 5px 14px 4px; border-radius: 2px; font-size: 11.5px; font-weight: 700;
  letter-spacing: 0.22em; background: rgba(4,6,10,0.62);
  border: 1px solid currentColor; border-left-width: 3px;
  text-shadow: 0 0 12px currentColor; backdrop-filter: blur(2px);
}

/* Interaction prompt. Squared off, not pill-shaped: this is the same industrial
   language as the door signage in the world, and a rounded floating card was
   the single most "web app" thing on screen. */
#prompt {
  position: absolute; left: 50%; top: 57%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 13px;
  background: rgba(5,8,12,0.68); padding: 9px 18px 9px 9px; border-radius: 3px;
  border: 1px solid rgba(255,179,64,0.26); border-left: 2px solid var(--amber);
  backdrop-filter: blur(3px); min-width: 232px;
  box-shadow: 0 6px 26px rgba(0,0,0,0.55);
}
.pkey {
  /* min-width, not width: a rebind can put "L SHIFT" in here */
  min-width: 30px; padding: 0 8px; height: 30px; display: grid; place-items: center; border-radius: 3px;
  background: linear-gradient(180deg, rgba(255,179,64,0.24), rgba(255,179,64,0.10));
  border: 1.5px solid var(--amber);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.22), 0 2px 0 rgba(0,0,0,0.45);
  color: var(--amber); font-weight: 800; font-size: 14px; letter-spacing: 0.06em;
}
.ptext { display: flex; flex-direction: column; gap: 2px; line-height: 1.2; }
#promptLabel { font-size: 14.5px; letter-spacing: 0.10em; }
#promptCost {
  font-size: 11px; color: var(--amber); font-weight: 700; letter-spacing: 0.20em;
  opacity: 0.92;
}
#promptCost:empty { display: none; }
#prompt.cant { border-color: rgba(200,40,40,0.45); border-left-color: #c8202a; }
#prompt.cant .pkey {
  border-color: #c8202a; color: #ff6a5a;
  background: linear-gradient(180deg, rgba(200,32,42,0.24), rgba(200,32,42,0.08));
}
#prompt.cant #promptCost { color: #ff6a5a; }
#promptBar { position: absolute; left: 0; bottom: 0; height: 2px; width: 100%; background: transparent; }
#promptFill {
  height: 100%; width: 0%; background: var(--amber);
  box-shadow: 0 0 8px rgba(255,179,64,0.7); transition: width 0.06s linear;
}

/* squad */
#squad { position: absolute; left: var(--gut); top: 26px; display: flex; flex-direction: column; gap: 8px; }
#voiceHud {
  position: absolute; left: var(--gut); top: 142px; padding: 4px 8px;
  border: 1px solid rgba(255,255,255,.14); border-radius: 2px;
  background: rgba(5,8,12,.55); color: rgba(239,230,210,.62);
  font-size: 9.5px; letter-spacing: .16em; text-shadow: var(--tshadow-lite);
}
#voiceHud.voice-ready { color: var(--green); border-color: rgba(125,220,106,.35); }
#voiceHud.voice-talking { color: #fff; border-color: rgba(255,179,64,.72); background: rgba(200,92,24,.42); }
#voiceHud.voice-error { color: #ff7a6a; border-color: rgba(255,106,90,.45); }
.sq { display: flex; align-items: center; gap: 9px; font-size: 12.5px; opacity: 0.94; text-shadow: var(--tshadow-lite); }
.sq .dot { width: 8px; height: 8px; border-radius: 50%; box-shadow: 0 0 8px currentColor, 0 0 0 1px rgba(0,0,0,0.5); }
.sq .nm { width: 92px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; letter-spacing: 0.08em; }
.sq .hp {
  width: 76px; height: 4px; background: rgba(255,255,255,0.12); border-radius: 2px;
  overflow: hidden; box-shadow: 0 0 0 1px rgba(0,0,0,0.45);
}
/* display:block is load-bearing. .sq is the flex container, so .hp gets
   blockified as a flex item and takes its 76x4 — but .hpf is a <span> INSIDE
   .hp, which is not a flex container, so it stayed inline and height:100%
   resolved to zero. Every squad health bar in the game has been rendering as an
   empty grey trough. */
/* A FULL health bar carries no information, and this one was carrying it in the
   loudest colour on screen: a #6fd45c -> #cbe86a ramp, 76 px wide, at the top
   left corner where the eye lands first, in a game whose frames are graded to a
   mean saturation of about 0.28. It won every staring contest with the thing
   the player was supposed to be looking at.
   So the bar earns its ink instead of demanding it: at full health it is barely
   more than the trough it sits in, and it gains colour, brightness and finally
   motion exactly as fast as it starts to matter. Nothing here is new geometry
   or a new element — it is the same 76x4 strip, told when to speak. */
.sq .hpf {
  display: block; height: 100%; width: 0;
  background: #5c7f52;
  opacity: 0.5;
  transition: width 0.2s, background 0.3s, opacity 0.3s;
}
.sq.hurt .hpf { background: linear-gradient(90deg, #7fb35e, #b9c96a); opacity: 0.85; }
.sq.crit .hpf { background: linear-gradient(90deg, #d8632c, #ffb340); opacity: 1; }
.sq.crit .hp { box-shadow: 0 0 0 1px rgba(0,0,0,0.45), 0 0 10px rgba(216,99,44,0.45); }
.sq.down .hpf { background: #c8202a; opacity: 1; }
.sq.down .nm { color: #ff6a5a; animation: lowblink 1s infinite; }
.sq.dead { opacity: 0.32; }
.sq .pk { font-size: 9.5px; opacity: 0.62; letter-spacing: 0.14em; }
.sq .vc { min-width: 42px; color: var(--green); font-size: 8px; letter-spacing: .12em; }
.sq .vc.talking { color: var(--amber); text-shadow: 0 0 8px rgba(255,179,64,.8); }

/* Zombie radar. Sits under the squad list and the fixed voice strip — five
   operators come to about 107 px off the 26 px top edge, the voice strip begins
   at 142 px, and 178 px clears both without anything shifting as players join.
   Everything here is chrome that never changes: the ring, the range circle, the
   reticle hairlines and the facing wedge are all static, which is the whole
   reason they are CSS and not canvas draws. The canvas holds blips only. */
#radar {
  position: absolute; left: var(--gut); top: 178px;
  width: var(--rd); height: var(--rd); border-radius: 50%;
  /* Darker in the middle than at the rim: it puts the most contrast exactly
     where the closest — and therefore most urgent — blips land. */
  background:
    radial-gradient(circle at 50% 50%, rgba(6,9,14,0.62) 0%, rgba(6,9,14,0.46) 62%, rgba(5,8,12,0.54) 100%);
  border: 1px solid rgba(255,179,64,0.20);
  box-shadow: inset 0 0 20px rgba(0,0,0,0.55), 0 4px 18px rgba(0,0,0,0.45);
  /* The scrim alone cannot win this fight. Walk into a lit room, or let a body
     get close enough to catch the muzzle flash, and a 60%-opaque wash still
     leaves a 2 px blood-red blip sitting on top of a bright orange arm. Knocking
     the backdrop's brightness and saturation down first means the dial is dark
     because of what is BEHIND it, not because it is a black slab bolted over the
     world — the room still shows through, it just cannot shout. */
  backdrop-filter: blur(3px) brightness(0.4) saturate(0.55);
  opacity: 0.94;
}
/* Reticle hairlines, drawn as two 1 px gradient stops rather than two more
   elements. Faint on purpose — they are a scale reference, not content. */
#radar::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background:
    linear-gradient(to right, transparent calc(50% - 0.5px), rgba(239,230,210,0.075) calc(50% - 0.5px),
                    rgba(239,230,210,0.075) calc(50% + 0.5px), transparent calc(50% + 0.5px)),
    linear-gradient(to bottom, transparent calc(50% - 0.5px), rgba(239,230,210,0.075) calc(50% - 0.5px),
                    rgba(239,230,210,0.075) calc(50% + 0.5px), transparent calc(50% + 0.5px));
}
/* Half-range circle. Without it every blip is just "somewhere in the dish" —
   with it you can read 20 m from 35 m at a glance, which is the difference
   between reloading now and reloading in four seconds. */
#radar::after {
  content: ''; position: absolute; inset: 25%; border-radius: 50%;
  border: 1px solid rgba(239,230,210,0.07);
}
/* Where you are looking. The dial never rotates (hud.js turns the blips), so
   this wedge is what makes "up" mean "in front of me" instead of "north". */
.rd-fov {
  position: absolute; inset: 1px; border-radius: 50%;
  background: conic-gradient(from 328deg at 50% 50%,
    rgba(255,179,64,0.11) 0deg, rgba(255,179,64,0.045) 20deg, rgba(255,179,64,0) 64deg);
}
#radarBlips { position: absolute; inset: 0; width: 100%; height: 100%; }
/* The player: a chevron, not a dot — a dot at the centre of a ring of dots is
   the one mark you cannot find in a hurry. */
.rd-self {
  position: absolute; left: 50%; top: 50%; width: 0; height: 0;
  transform: translate(-50%, -55%);
  border-left: 3.5px solid transparent; border-right: 3.5px solid transparent;
  border-bottom: 6px solid rgba(255,179,64,0.85);
  filter: drop-shadow(0 0 3px rgba(0,0,0,0.9));
}

/* killfeed */
#feed { position: absolute; right: var(--gut); top: 84px; display: flex; flex-direction: column; gap: 4px; align-items: flex-end; }
.fi {
  font-size: 12.5px; letter-spacing: 0.04em; padding: 4px 11px 4px 10px;
  background: rgba(5,8,12,0.55); border-left: 3px solid var(--amber);
  border-radius: 0 2px 2px 0; animation: fadein 0.25s cubic-bezier(.16,1,.3,1);
  max-width: 320px; text-align: right; text-shadow: var(--tshadow-lite);
}
@keyframes fadein { from { opacity: 0; transform: translateX(14px); } }

/* Chat sits directly above #blockLeft, which is perks + points + round +
   next-wave stacked off --gut-b. 178 px cleared the old block and ran straight
   through the perk row once the round rule was added; --chat-b is measured off
   the tallest that stack gets (perk 40 + 12, points 38, round 66, wave 16). */
#chatLog { position: absolute; left: var(--gut); bottom: calc(var(--gut-b) + 208px); display: flex; flex-direction: column; gap: 4px; max-width: 380px; }
.cm { font-size: 12.5px; text-shadow: var(--tshadow-lite); }
.cm b { font-weight: 700; }
#chatInput {
  position: absolute; left: var(--gut); bottom: calc(var(--gut-b) + 176px); width: 380px; pointer-events: auto;
  background: rgba(5,8,12,0.88); border: 1px solid rgba(255,179,64,0.4); color: var(--paper);
  padding: 8px 11px; font-family: var(--ui); font-size: 14px; border-radius: 3px; outline: none;
}

#roomInfo {
  position: absolute; right: var(--gut); top: 26px; text-align: right;
  font-size: 10.5px; letter-spacing: 0.18em; opacity: 0.5; text-shadow: var(--tshadow-lite);
}
#roomCode {
  display: block; font-family: var(--num); font-size: 19px; letter-spacing: 0.26em;
  opacity: 0.85; margin-bottom: 1px;
}

#missionInfo {
  position: absolute; left: 50%; top: 22px; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  text-align: center; text-transform: uppercase; text-shadow: var(--tshadow-lite);
}
#areaName { font-family: var(--num); font-size: 15px; letter-spacing: 0.22em; color: var(--paper); }
#objectiveText { font-size: 9.5px; letter-spacing: 0.24em; color: var(--amber); opacity: 0.72; }
#objectiveHint {
  max-width: min(680px, 72vw); font-size: 8.5px; line-height: 1.35; letter-spacing: 0.08em;
  color: var(--paper); opacity: 0.48; text-wrap: balance;
}

/* downed */
#downed {
  position: absolute; left: 50%; top: 62%; transform: translateX(-50%); text-align: center;
}
.d-title {
  font-family: var(--num); font-size: 44px; color: #d8242c; letter-spacing: 0.24em;
  text-shadow: 0 0 30px rgba(200,32,42,0.55), 0 3px 8px #000;
}
.d-sub { font-size: 12.5px; opacity: 0.72; margin: 8px 0 12px; letter-spacing: 0.20em; text-transform: uppercase; }
.d-bar {
  width: 300px; height: 4px; background: rgba(255,255,255,0.13); margin: 0 auto;
  border-radius: 2px; overflow: hidden; box-shadow: 0 0 0 1px rgba(0,0,0,0.5);
}
#bleedFill, #revFill { height: 100%; background: linear-gradient(90deg, #c8202a, #ff7a4a); transition: width 0.15s linear; }
#beingRevived { position: absolute; left: 50%; top: 74%; transform: translateX(-50%); text-align: center; font-size: 14px; }
#revFill { background: linear-gradient(90deg, #39d0ff, #7ddc6a); }

/* Scoreboard. Numeric columns are right-aligned on tabular figures so the
   digits form real columns — the one thing that separates a score table from a
   list of strings that happen to be numbers. */
#scoreboard {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%);
  background: rgba(5,8,12,0.92); border: 1px solid rgba(255,179,64,0.22);
  padding: 20px 26px 14px; border-radius: 4px; min-width: 540px;
  box-shadow: 0 24px 70px rgba(0,0,0,0.7);
}
.sb-head, .sb-row { display: grid; grid-template-columns: 1.7fr 0.7fr 0.7fr 1fr 0.7fr; gap: 14px; font-size: 13.5px; }
.sb-head {
  opacity: 0.42; letter-spacing: 0.22em; font-size: 10px; padding-bottom: 9px;
  border-bottom: 1px solid rgba(255,255,255,0.14); margin-bottom: 4px;
}
.sb-head span + span, .sb-row span + span { text-align: right; }
.sb-row { padding: 6px 0; border-bottom: 1px solid rgba(255,255,255,0.05); }
.sb-row:last-child { border-bottom: none; }
.sb-row span:first-child { display: flex; align-items: center; gap: 9px; letter-spacing: 0.06em; }

#boxRoll {
  position: absolute; left: 50%; top: 44%; transform: translate(-50%,-50%);
  font-family: var(--num); font-size: 40px; letter-spacing: 0.12em; color: var(--amber);
  text-shadow: 0 0 26px rgba(255,179,64,0.55), 0 3px 8px #000;
}

/* ============================ MENUS ============================ */
#menu, #lobby, #pause, #gameover {
  position: absolute; inset: 0; z-index: 40; display: flex; align-items: center; justify-content: center;
  overflow-y: auto; padding: max(16px, env(safe-area-inset-top)) max(12px, env(safe-area-inset-right))
    max(16px, env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left));
  background: radial-gradient(ellipse at 50% 40%, #12161f 0%, #05070a 70%);
}
#pause { background: rgba(4,6,9,0.86); backdrop-filter: blur(4px); }
.bg-grain {
  position: absolute; inset: 0; opacity: 0.4; pointer-events: none;
  background-image:
    repeating-linear-gradient(0deg, rgba(255,255,255,0.02) 0 1px, transparent 1px 3px),
    radial-gradient(ellipse at 50% 0%, rgba(200,32,42,0.10), transparent 60%);
}
.menu-card {
  position: relative; z-index: 2; flex: 0 0 auto; width: min(560px, 94vw); padding: 28px 30px 22px;
  max-height: calc(100dvh - 32px); overflow-y: auto; overscroll-behavior: contain;
  background: rgba(8,11,16,0.82); border: 1px solid rgba(255,179,64,0.18);
  border-radius: 8px; box-shadow: 0 26px 70px rgba(0,0,0,0.7);
  text-align: center;
}
.menu-card.wide { width: min(620px, 94vw); }
.menu-card--deploy { width: min(880px, 96vw); }
.deploy-layout { display: grid; grid-template-columns: minmax(300px, .92fr) minmax(340px, 1.08fr); gap: 26px; align-items: start; }
.deploy-controls { min-width: 0; }
/* The title is a two-part lockup, so the two lines have to be optically
   locked: same tracking, tight leading, and the accent line carries the colour
   rather than a second size. */
.menu-card h1 {
  font-family: var(--num); font-size: 46px; line-height: 0.92; letter-spacing: 0.14em;
  font-weight: 700; margin-bottom: 10px; text-indent: 0.14em;
}
.menu-card h1 span { display: block; color: var(--blood); text-shadow: 0 0 30px rgba(200,32,42,0.5); }
.menu-card h1.dead { color: var(--blood); font-size: 54px; text-shadow: 0 0 40px rgba(200,32,42,0.6); }
.menu-card h2 { font-family: var(--num); font-size: 30px; letter-spacing: 0.18em; margin-bottom: 14px; }
#pauseTitle { font-size: 24px; letter-spacing: 0.10em; line-height: 1.2; }
/* balance stops "they never stop / coming" hanging one word on its own line */
.tag {
  font-size: 11.5px; opacity: 0.52; letter-spacing: 0.16em; margin-bottom: 24px;
  line-height: 1.7; text-transform: uppercase; text-wrap: balance;
  max-width: 34ch; margin-left: auto; margin-right: auto;
}
.hint { font-size: 12.5px; opacity: 0.55; margin: 12px 0; letter-spacing: 0.1em; text-wrap: balance; }

.field { display: block; text-align: left; margin-bottom: 16px; }
.field > span, .field > legend { display: block; font-size: 11px; letter-spacing: 0.24em; opacity: 0.55; margin-bottom: 5px; }
.map-field { border: 0; min-width: 0; width: 100%; }
.operator-field { min-width: 0; margin: 0; border: 0; }
.operator-select { display: grid; grid-template-columns: 44px 1fr 44px; gap: 8px; align-items: center; }
.operator-stage {
  position: relative; min-height: 294px; overflow: hidden; border: 1px solid rgba(255,179,64,.34);
  border-radius: 5px; background:
    linear-gradient(rgba(255,255,255,.025) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.025) 1px, transparent 1px),
    radial-gradient(ellipse at 50% 62%, rgba(212,69,31,.22), rgba(8,11,16,.96) 68%);
  background-size: 16px 16px, 16px 16px, auto;
}
#operatorCanvas {
  display: block; width: 210px; height: 294px; max-width: 100%; margin: 0 auto;
  image-rendering: pixelated; image-rendering: crisp-edges;
}
.operator-lock {
  position: absolute; left: 0; right: 0; bottom: 0; display: flex; justify-content: space-between;
  align-items: center; gap: 10px; padding: 9px 11px; background: rgba(5,7,10,.86);
  border-top: 1px solid rgba(255,179,64,.2); font-family: var(--num); letter-spacing: .14em;
}
#operatorName { font-size: 20px; color: var(--paper); }
#operatorState { font-size: 9px; color: var(--amber); }
.turn-button {
  width: 44px; height: 54px; border: 1px solid rgba(255,179,64,.34); color: var(--amber);
  background: rgba(255,179,64,.09); font-size: 30px; line-height: 1;
}
.turn-button:hover { background: rgba(255,179,64,.18); }
.operator-pick { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 7px; margin-top: 9px; }
.operator-card {
  position: relative; min-width: 0; padding: 4px; overflow: hidden; background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.14); color: var(--paper); text-align: center;
}
.operator-card img { display: block; width: 100%; aspect-ratio: 1; object-fit: cover; image-rendering: pixelated; filter: saturate(.75) brightness(.78); }
.operator-card .operator-card-name { display: block; padding: 5px 1px 3px; font-size: 9px; letter-spacing: .12em; white-space: nowrap; }
.operator-card.sel { border-color: var(--amber); box-shadow: 0 0 0 2px rgba(255,179,64,.14); background: rgba(255,179,64,.10); }
.operator-card.sel img { filter: saturate(1) brightness(1); }
.operator-card.locked { cursor: not-allowed; opacity: .48; }
.operator-card.locked .locked-face { display: grid; place-items: center; aspect-ratio: 1; background: repeating-linear-gradient(135deg, #1c2026 0 8px, #12151a 8px 16px); font: 700 36px var(--num); color: rgba(255,255,255,.25); }
.operator-hint { margin: 8px 0 0; text-align: center; color: rgba(239,230,210,.48); font-size: 9px; line-height: 1.5; letter-spacing: .12em; text-transform: uppercase; }
.sr-only { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
input[type=text], input:not([type]), #codeInput, #nameInput {
  width: 100%; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.16);
  color: var(--paper); padding: 10px 12px; font-family: var(--ui); font-size: 15px;
  border-radius: 4px; outline: none; letter-spacing: 0.08em;
}
input:focus { border-color: var(--amber); box-shadow: 0 0 0 2px rgba(255,179,64,0.14); }
#codeInput { text-transform: uppercase; text-align: center; letter-spacing: 0.35em; font-family: var(--num); font-size: 20px; }

button {
  font-family: var(--ui); cursor: pointer; border: none; border-radius: 4px;
  letter-spacing: 0.16em; font-weight: 700; transition: transform 0.08s, background 0.15s, box-shadow 0.15s;
}
button:active { transform: translateY(1px); }
button:focus-visible, input:focus-visible, .mapcard:focus-visible {
  outline: 2px solid var(--amber); outline-offset: 3px;
}
button:disabled { cursor: wait; opacity: 0.55; }
.big {
  display: block; width: 100%; padding: 14px; font-size: 16px; margin-bottom: 10px;
  background: linear-gradient(180deg, #d4451f, #98230f); color: #fff;
  box-shadow: 0 6px 20px rgba(200,50,20,0.32), inset 0 1px 0 rgba(255,255,255,0.22);
}
.big:hover { background: linear-gradient(180deg, #e8562c, #ae2d14); }
.ghost {
  display: block; width: 100%; padding: 11px; font-size: 14px; margin-top: 8px;
  background: rgba(255,255,255,0.06); color: var(--paper); border: 1px solid rgba(255,255,255,0.16);
}
.ghost:hover { background: rgba(255,255,255,0.12); }
.row { display: flex; gap: 8px; margin-bottom: 10px; }
.row input { flex: 1; }
.row button { padding: 0 20px; background: rgba(255,179,64,0.14); color: var(--amber); border: 1px solid rgba(255,179,64,0.45); font-size: 14px; }
.row button:hover { background: rgba(255,179,64,0.24); }
.menu-foot { display: flex; justify-content: center; gap: 18px; margin-top: 14px; }
.link { background: none; color: rgba(239,230,210,0.55); font-size: 12px; letter-spacing: 0.18em; }
.link:hover { color: var(--amber); }
.err { color: #ff6a5a; font-size: 13px; margin-top: 12px; min-height: 18px; }

.panel {
  position: relative; z-index: 3; width: min(760px, 94vw); padding: 30px;
  background: rgba(8,11,16,0.95); border: 1px solid rgba(255,179,64,0.2); border-radius: 8px;
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  max-height: calc(100dvh - 32px); overflow-y: auto;
}
.panel h2 { font-family: var(--num); font-size: 28px; letter-spacing: 0.18em; margin-bottom: 18px; text-align: center; }
.panel h3 { font-size: 12px; letter-spacing: 0.24em; color: var(--amber); margin-bottom: 10px; }
.cols { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; }
.panel ul { list-style: none; }
.panel li { font-size: 13.5px; line-height: 1.85; opacity: 0.82; }
.panel li b { color: var(--amber); font-weight: 700; }
.close { display: block; margin: 22px auto 0; padding: 10px 30px; background: rgba(255,255,255,0.08); color: var(--paper); border: 1px solid rgba(255,255,255,0.18); font-size: 13px; }
.slider { display: block; margin-bottom: 16px; }
.slider span { display: block; font-size: 11px; letter-spacing: 0.2em; opacity: 0.6; margin-bottom: 6px; }
.slider b { color: var(--amber); float: right; }
.slider input { width: 100%; accent-color: var(--amber); }
.check { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; font-size: 13px; letter-spacing: 0.12em; opacity: 0.85; cursor: pointer; }
.check input { accent-color: var(--amber); width: 16px; height: 16px; }

/* lobby */
.codebox { background: rgba(255,179,64,0.07); border: 1px dashed rgba(255,179,64,0.4); border-radius: 6px; padding: 16px; margin-bottom: 18px; }
.codelabel { font-size: 10px; letter-spacing: 0.3em; opacity: 0.6; }
#lobbyCode { font-family: var(--num); font-size: 46px; letter-spacing: 0.3em; color: var(--amber); margin: 4px 0 10px; text-shadow: 0 0 24px rgba(255,179,64,0.4); }
#btnCopy { min-height: 44px; padding: 9px 18px; font-size: 12px; background: rgba(255,179,64,0.16); color: var(--amber); border: 1px solid rgba(255,179,64,0.45); }
.copied { font-size: 12px; color: var(--green); margin-top: 8px; }
.invite-fallback {
  width: 100%; margin-top: 9px; user-select: text; -webkit-user-select: text;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px;
}
#lobbyCount {
  margin: -2px 0 9px; color: rgba(239,230,210,.58); font-family: var(--num);
  font-size: 11px; letter-spacing: .18em; text-align: left;
}
#lobbyTitle:focus-visible { outline: 2px solid var(--amber); outline-offset: 4px; border-radius: 2px; }
#lobbyPlayers { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; }
.voice-lobby { margin: 10px 0 8px; padding: 10px; border: 1px solid rgba(125,220,106,.18); background: rgba(125,220,106,.045); border-radius: 4px; }
.voice-lobby #btnVoice { margin: 0; min-height: 42px; }
#voiceLobbyStatus { margin: 7px 0 0; color: rgba(239,230,210,.58); font-size: 10px; line-height: 1.45; letter-spacing: .1em; }
.voice-badge { color: var(--green); border-color: rgba(125,220,106,.42); }
.voice-badge.talking { color: #fff; background: rgba(255,179,64,.18); border-color: var(--amber); }
.lp {
  display: grid; grid-template-columns: 10px 38px minmax(0, 1fr) auto; align-items: center;
  gap: 10px; min-width: 0; padding: 9px 13px; background: rgba(255,255,255,0.05);
  border-radius: 4px; font-size: 14px; text-align: left;
}
.lp .dot { width: 10px; height: 10px; border-radius: 50%; }
.lp .operator-mini { width: 38px; height: 38px; object-fit: cover; border-radius: 3px; image-rendering: pixelated; border: 1px solid rgba(255,255,255,.18); }
.lp-identity { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.lp-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lp .operator-label { color: var(--amber); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; }
.lp-badges { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
.lp-badge {
  flex: 0 0 auto; padding: 3px 5px; border: 1px solid rgba(255,255,255,.18);
  border-radius: 2px; color: rgba(239,230,210,.62); font-size: 9px;
  line-height: 1.2; letter-spacing: .14em; text-align: center; white-space: nowrap;
}
.lp-badge.creator { border-color: rgba(255,179,64,.52); color: var(--amber); background: rgba(255,179,64,.09); }
.slot-empty .lp-identity { grid-column: 2 / 4; }
.slot-empty { opacity: 0.32; font-style: italic; }
#lobbyHint { min-height: 1.5em; }
#btnStart, #btnLeave { min-height: 44px; }
#btnStart:disabled { cursor: default; background: rgba(255,255,255,.07); color: rgba(239,230,210,.55); box-shadow: none; }
#btnStart.is-starting:disabled { cursor: wait; color: var(--paper); background: rgba(212,69,31,.42); }

#goSummary { font-size: 15px; opacity: 0.85; margin-bottom: 16px; letter-spacing: 0.08em; }
#goSummary b { color: var(--amber); font-family: var(--num); font-size: 30px; }
#goProgress { margin: -6px 0 16px; display: grid; gap: 4px; font-size: 11px; letter-spacing: 0.09em; color: #9fb2bd; }
#goProgress .unlock { color: #6ee7ff; }
#goBoard { display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }
.gb { display: grid; grid-template-columns: 1.4fr 0.7fr 0.7fr 0.7fr 1fr; gap: 8px; font-size: 13px; padding: 7px 10px; background: rgba(255,255,255,0.05); border-radius: 4px; text-align: left; }
.gb.head { opacity: 0.45; font-size: 10px; letter-spacing: 0.16em; background: none; }
.gb span:first-child { display: flex; align-items: center; gap: 7px; }

#loading {
  position: absolute; inset: 0; z-index: 60; display: grid; place-items: center; gap: 16px;
  background: #05070a; align-content: center; font-size: 12px; letter-spacing: 0.32em; opacity: 0.8;
}
.spinner { width: 34px; height: 34px; border: 2px solid rgba(255,179,64,0.2); border-top-color: var(--amber); border-radius: 50%; animation: spin 0.9s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Bottom-left, not top-left: at top-left it sat on top of the squad list. */
#fps {
  position: absolute; left: 10px; top: auto; bottom: 8px; z-index: 50;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px; letter-spacing: 0.04em; color: var(--green); opacity: 0.55;
  text-shadow: 0 1px 3px #000;
}
#clickToPlay {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); z-index: 30;
  font-size: 14px; letter-spacing: 0.24em; padding: 14px 26px; background: rgba(6,9,13,0.7);
  border: 1px solid rgba(255,179,64,0.3); border-radius: 5px; pointer-events: none;
}

/* One variable moves every corner block in step; the scale is only there to
   keep the hero figures from eating a phone screen. */
@media (max-width: 1180px) { :root { --gut: 26px; --gut-b: 22px; } }
@media (max-width: 760px) {
  .deploy-layout { grid-template-columns: 1fr; gap: 18px; }
  .operator-stage { min-height: 230px; }
  #operatorCanvas { width: 164px; height: 230px; }
  :root { --gut: 14px; --gut-b: 14px; }
  #blockLeft, #blockRight { transform: scale(0.8); }
  #blockLeft { transform-origin: left bottom; }
  #blockRight { transform-origin: right bottom; }
  .cols { grid-template-columns: 1fr; }
}

/* damage direction indicator */
#dmgDir { position: absolute; left: 50%; top: 50%; width: 0; height: 0; }
.dmgarrow {
  position: absolute; left: -110px; top: -110px; width: 220px; height: 220px;
  animation: dmgfade 1.2s ease-out forwards; pointer-events: none;
}
.dmgarrow::before {
  content: ''; position: absolute; left: 50%; top: -6px; margin-left: -26px;
  border-left: 26px solid transparent; border-right: 26px solid transparent;
  border-bottom: 30px solid rgba(230,40,30,0.9);
  filter: drop-shadow(0 0 6px rgba(0,0,0,0.9));
}
@keyframes dmgfade {
  0% { opacity: 0; transform-origin: center; }
  12% { opacity: 1; }
  100% { opacity: 0; }
}

#nextWave {
  font-size: 10.5px; letter-spacing: 0.30em; color: rgba(255,179,64,0.8);
  margin-top: 5px; text-shadow: var(--tshadow-lite);
}

/* map picker */
#mapPick { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 7px; }
.mapcard {
  display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 8px 10px; cursor: pointer;
  background: rgba(255,255,255,0.045); border: 1px solid rgba(255,255,255,0.12);
  border-radius: 5px; transition: border-color 0.15s, background 0.15s; color: var(--paper);
  text-align: left; letter-spacing: normal; font-weight: 400;
}
.mapcard:hover { background: rgba(255,255,255,0.09); }
.mapcard.sel { border-color: var(--amber); background: rgba(255,179,64,0.12); }
.mapcard .mh { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; }
.mapcard .mn { font-family: var(--num); font-size: 19px; letter-spacing: 0.14em; }
.mapcard .mt { font-size: 11.5px; opacity: 0.62; letter-spacing: 0.04em; }
.mapcard .md { font-size: 10px; letter-spacing: 0.2em; color: var(--amber); opacity: 0.9; }
.map-badge {
  flex: 0 0 auto; padding: 2px 5px; border-radius: 999px;
  border: 1px solid rgba(125,220,106,0.5); background: rgba(125,220,106,0.11);
  color: #9ce88d; font-size: 7.5px; font-weight: 700; letter-spacing: 0.13em;
}

/* reconnect overlay — a server restart should not look like a crash */
#reconnect {
  position: absolute; inset: 0; z-index: 70; display: grid; place-items: center;
  background: rgba(4, 6, 9, 0.72); backdrop-filter: blur(3px);
}
#reconnect.hidden { display: none !important; }
.rc-box {
  display: flex; align-items: center; gap: 14px;
  padding: 18px 26px; border-radius: 6px;
  background: rgba(8, 11, 16, 0.95); border: 1px solid rgba(255, 179, 64, 0.28);
  font-size: 14px; letter-spacing: 0.14em;
}
#reconnect.fatal .rc-box { border-color: rgba(200, 32, 42, 0.5); flex-direction: column; }
.rc-spin {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid rgba(255, 179, 64, 0.25); border-top-color: var(--amber);
  animation: spin 0.85s linear infinite;
}
#rcRetry {
  margin-top: 4px; padding: 9px 24px; font-size: 12px;
  background: rgba(255, 179, 64, 0.16); color: var(--amber);
  border: 1px solid rgba(255, 179, 64, 0.45);
}

/* ==================== SETTINGS + CONTROLS OVERLAY ====================
   Sits above #menu (40) and #pause (40) both, because it is opened from
   either one and has to survive whichever is underneath it. */
#options {
  position: absolute; inset: 0; z-index: 50; display: grid; place-items: center;
  background: rgba(4,6,9,0.90); backdrop-filter: blur(5px);
}
.opt-card {
  position: relative; width: min(720px, 94vw); max-height: 88vh; padding: 26px 28px 20px;
  display: flex; flex-direction: column;
  background: rgba(8,11,16,0.96); border: 1px solid rgba(255,179,64,0.22);
  border-radius: 8px; box-shadow: 0 26px 70px rgba(0,0,0,0.7);
}
.opt-card h2 { font-family: var(--num); font-size: 28px; letter-spacing: 0.18em; text-align: center; margin-bottom: 14px; }
.opt-card h3 { font-size: 11px; letter-spacing: 0.24em; color: var(--amber); opacity: 0.8; margin: 18px 0 8px; }
.opt-tabs { display: flex; gap: 6px; margin-bottom: 16px; }
.opt-tab {
  flex: 1; padding: 9px 0; font-size: 12px; letter-spacing: 0.2em;
  background: rgba(255,255,255,0.05); color: rgba(239,230,210,0.6);
  border: 1px solid rgba(255,255,255,0.12);
}
.opt-tab:hover { background: rgba(255,255,255,0.1); color: var(--paper); }
.opt-tab.sel { background: rgba(255,179,64,0.14); color: var(--amber); border-color: rgba(255,179,64,0.5); }
.opt-body { overflow-y: auto; overflow-x: hidden; padding-right: 6px; flex: 1; }
.opt-body::-webkit-scrollbar { width: 8px; }
.opt-body::-webkit-scrollbar-thumb { background: rgba(255,179,64,0.28); border-radius: 4px; }
.opt-note { font-size: 12px; line-height: 1.6; opacity: 0.55; margin-bottom: 14px; }

/* controls list */
.bind-group { font-size: 10px; letter-spacing: 0.26em; color: var(--amber); opacity: 0.85; margin: 16px 0 6px; }
.bind-group:first-child { margin-top: 0; }
.bind-row {
  display: flex; align-items: center; gap: 12px; padding: 7px 10px; margin-bottom: 4px;
  background: rgba(255,255,255,0.04); border-radius: 4px; border: 1px solid transparent;
}
.bind-row .ba { flex: 1; font-size: 13.5px; opacity: 0.85; text-align: left; }
.bind-key {
  min-width: 68px; padding: 5px 10px; font-family: var(--num); font-size: 14px;
  letter-spacing: 0.1em; background: rgba(255,179,64,0.12); color: var(--amber);
  border: 1px solid rgba(255,179,64,0.4); border-radius: 4px;
}
.bind-key:hover { background: rgba(255,179,64,0.24); }
.bind-key.unset { color: #ff6a5a; border-color: rgba(200,32,42,0.5); background: rgba(200,32,42,0.1); }
.bind-row.listening { border-color: var(--amber); background: rgba(255,179,64,0.09); }
.bind-row.listening .bind-key { animation: bindpulse 0.9s ease-in-out infinite; }
.bind-row.clash { border-color: rgba(200,32,42,0.6); }
@keyframes bindpulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
.bind-fixed { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 14px; }
.bind-fixed div { display: flex; gap: 10px; font-size: 12.5px; opacity: 0.55; padding: 3px 0; }
.bind-fixed b { color: var(--amber); font-weight: 700; min-width: 62px; }
#bindWarn { min-height: 18px; font-size: 12.5px; text-align: center; }
#btnResetBinds { margin-top: 16px; }

@media (max-width: 760px) {
  #scoreboard { min-width: 0; width: calc(100vw - 20px); padding: 14px 10px 10px; overflow-x: auto; }
  .sb-head, .sb-row { grid-template-columns: minmax(120px, 1.7fr) repeat(4, minmax(52px, .7fr)); gap: 7px; font-size: 11px; }
  #chatLog { max-width: calc(100vw - 28px); }
  #chatInput { width: calc(100vw - 28px); }
  #missionInfo { top: 12px; width: min(58vw, 310px); }
  #areaName { font-size: 12px; }
  #objectiveText { font-size: 8px; }
  #objectiveHint { max-width: 68vw; font-size: 7.5px; }
}

@media (max-width: 520px) {
  #mapPick { grid-template-columns: 1fr; }
  .menu-card { width: calc(100vw - 18px); padding: 22px 18px 18px; }
  .menu-card h1 { font-size: 38px; }
  .operator-select { grid-template-columns: 40px 1fr 40px; }
  .turn-button { width: 40px; }
  #lobbyCode { font-size: 38px; }
  .lp { grid-template-columns: 8px 32px minmax(0, 1fr) auto; gap: 8px; padding: 8px 9px; }
  .lp .dot { width: 8px; height: 8px; }
  .lp .operator-mini { width: 32px; height: 32px; }
  .lp-badges { flex-direction: column; align-items: flex-end; gap: 3px; }
  .lp-badge { max-width: 86px; padding: 2px 4px; font-size: 8px; white-space: normal; }
  #roomHint { display: none; }
  #weaponSlots { max-width: 250px; margin-left: auto; }
  .weapon-slot { max-width: 76px; }
}

@media (max-height: 800px) {
  .menu-card { padding-top: 18px; padding-bottom: 16px; }
  .menu-card h1 { font-size: 36px; margin-bottom: 6px; }
  .tag { margin-bottom: 12px; line-height: 1.35; }
  .field { margin-bottom: 10px; }
  .mapcard .mt { display: none; }
  .mapcard { padding-top: 6px; padding-bottom: 6px; }
  .operator-stage { min-height: 210px; }
  #operatorCanvas { width: 150px; height: 210px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
}
.reduce-motion *, .reduce-motion *::before, .reduce-motion *::after {
  animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important; scroll-behavior: auto !important;
}
.reduce-motion #dmgflash, .reduce-motion #effectTint, .reduce-motion #lowhp { display: none; }
