/* SHATTERGRID — HUD. Every value here comes from
   _src/design_handoff_shattergrid_game_screen/tokens.json. */

:root {
  --color-bg: #08050f;
  --color-bg-secondary: #06040c;
  --color-panel: linear-gradient(100deg, #1a0a2c, #08050f 70%);
  --color-panel-deep: linear-gradient(260deg, #1a0a2c, #08050f 70%);
  --color-field: #050718;

  --color-primary: #ff3fd0;   /* game voice */
  --color-secondary: #7df0ff; /* player systems */
  --color-accent: #b8f8ff;
  --color-accent-2: #4ad6f0;
  --color-danger: #ff2e4d;
  --color-danger-dim: #ff8fa0;

  --color-text: #ffffff;
  --color-title: #f4e8ff;
  --color-label: #c3a8dc;
  --color-text-dim: #a58cc0;
  --color-inactive: #5b4a72;

  --color-border: rgba(255, 63, 208, .30);
  --color-divider: rgba(255, 63, 208, .22);

  --glow-score: 0 0 36px rgba(255, 63, 208, .7);
  --glow-shield-numeral: 0 0 24px rgba(125, 240, 255, .75);
  --glow-shield-fill: 0 0 34px rgba(125, 240, 255, .8);
  --glow-chain: 0 0 26px rgba(255, 63, 208, .85);
  --glow-chain-pip: 0 0 12px rgba(255, 63, 208, .8);
  --glow-life-ice: 0 0 12px rgba(125, 240, 255, .8);
  --glow-life-red: 0 0 12px rgba(255, 46, 77, .85);
  --glow-recharge: 0 0 16px rgba(255, 63, 208, .75);

  --font-display: 'Chakra Petch', sans-serif;
  --font-mono: 'IBM Plex Mono', monospace;

  /* ── SURFACES AND INSETS. The M1/M2 blocks below were written with literal
     rgba() throughout; these are the tokens those literals were spelling out.
     Magenta is the game's voice, ice is the player's, red is reserved. */
  --color-primary-surface: rgba(255, 63, 208, .07);
  --color-primary-surface-hi: rgba(255, 63, 208, .14);
  --color-primary-inset: rgba(255, 63, 208, .42);
  --color-primary-inset-dim: rgba(255, 63, 208, .28);
  --color-primary-track: rgba(255, 63, 208, .12);

  --color-ice-surface: rgba(125, 240, 255, .07);
  --color-ice-surface-hi: rgba(125, 240, 255, .13);
  --color-ice-inset: rgba(125, 240, 255, .26);
  --color-ice-inset-hi: rgba(125, 240, 255, .45);

  --color-danger-surface: rgba(255, 46, 77, .06);
  --color-danger-inset: rgba(255, 46, 77, .28);
  --color-danger-glow: rgba(255, 46, 77, .30);

  --color-drained-inset: rgba(163, 140, 192, .14);
  --color-drained-hatch: repeating-linear-gradient(135deg, rgba(255, 255, 255, .03) 0 6px, transparent 6px 12px);

  --color-confirm-top: rgba(26, 8, 14, .98);
  --color-confirm-bottom: rgba(8, 5, 15, .99);

  /* ── GLOWS used by the M1/M2 components. */
  --glow-primary-soft: 0 0 14px rgba(255, 63, 208, .28);
  --glow-primary-mid: 0 0 16px rgba(255, 63, 208, .30);
  --glow-primary-hard: 0 0 26px rgba(255, 63, 208, .55);
  --glow-ice-soft: 0 0 16px rgba(125, 240, 255, .25);
  --glow-ice-mid: 0 0 26px rgba(125, 240, 255, .30);
  --glow-danger-dot: 0 0 12px rgba(255, 46, 77, .85);

  /* ── ANIMATION. Studio style-spec token names; the M1/M2 blocks had these
     durations written inline. */
  --anim-fast: .12s ease;
  --anim-normal: .2s cubic-bezier(.2, .7, .3, 1);
  --anim-slow: .3s cubic-bezier(.2, .7, .3, 1);
  --anim-easing: cubic-bezier(.2, .7, .3, 1);
}

*, *::before, *::after { box-sizing: border-box; }

/* Nothing in the game is selectable or draggable. Without this, holding LMB and
   moving selected the canvas, highlighted HUD text, and triggered the browser's
   native drag ghost — a translucent copy of the whole game field stuck to the
   cursor. */
html, body, #stage, #stage * {
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-tap-highlight-color: transparent;
}
#stage img, #stage canvas { pointer-events: none; }
/* Real controls stay clickable and keyboard-reachable. */
#stage button { pointer-events: auto; cursor: pointer; }

/* CENTERING COPIED FROM dead_ascent/style.css lines 49-52.
   Declarations byte-identical except --color-bg -> --color-bg-secondary and
   --font-primary -> --font-display for this game's tokens. Flexbox on
   html,body. No absolute positioning, no translate(-50%), no JS offset math. */
html,body{min-height:100%;height:100%;margin:0;background:var(--color-bg-secondary);color:var(--color-text);
  font-family:var(--font-display);
  -webkit-font-smoothing:antialiased;display:flex;align-items:center;justify-content:center;
  padding:0;overflow:hidden}
/* dead_ascent uses min-height:100% alone, which works there because its shell
   is SHORTER than the viewport. This shell is 1080 tall and always scales down,
   and min-height on body resolves against html's collapsed content height, so
   the flex box has no height to centre within and the stage sits low. An
   explicit height is the one deviation the taller content requires. */

/* The cursor is hidden ONLY over the arena, where the ship is the pointer.
   Hiding it globally leaves the player with no pointer at all over the panels
   and the overlay screens, which is how it read as "the mouse vanishes". */
#field { cursor: none; }
.screen { cursor: default; }
/* Modals get a normal pointer; their own rules set `pointer` on the things
   that are clickable. This works only because the interactive overlays now
   RECEIVE pointer events — see the .overlay split below. */
.overlay-draft, .overlay-pause, .overlay-confirm { cursor: default; }

/* SCALING COPIED FROM dead_ascent/style.css lines 58-59 (.game-wrapper).
   transform:scale(var(--scale,1)) with transform-origin:center center, sitting
   inside the flex-centered body above. The flexbox does the centering; the
   transform only does the scaling. */
/* In dead_ascent the scaled .game-wrapper and the fixed-size .cockpit are two
   DIFFERENT elements, and .cockpit carries flex-shrink:0. Here #stage is both,
   so it needs flex:none — as a flex item of the centering body it would
   otherwise be shrunk below 1920x1080 before the transform ran, and the whole
   arena would render squashed off-aspect. */
#stage{flex:none;width:1920px;height:1080px;max-width:none;
  transform:scale(var(--scale,1));transform-origin:center center;
  position:relative;overflow:hidden;background:var(--color-bg)}

.mono {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 2px;
  color: var(--color-label);
}

.label { letter-spacing: 4px; }

.tabular { font-variant-numeric: tabular-nums; }

/* ── TOP CHROME · 96px */
#top-chrome {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 96px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 26px;
  background: linear-gradient(180deg, #1c0930, #0a0614);
  border-bottom: 1px solid var(--color-border);
}

/* Wordmark, 1px rule, then the score group. One row, gap 22px. */
#brand-row { display: flex; align-items: center; gap: 22px; }

#wordmark {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 6px;
  color: var(--color-title);
  white-space: nowrap;
}

#brand-rule {
  width: 1px;
  height: 40px;
  flex: none;
  background: rgba(255, 63, 208, .55);
}

/* Baseline-aligned row per the handoff. The hero numeral's 0.8 line-height
   pulls its box tighter than the label's, so the row is aligned on the text
   baseline rather than the box edges. */
#score-block { display: flex; align-items: baseline; gap: 18px; }

#score {
  font-size: 68px;
  font-weight: 700;
  line-height: .8;
  letter-spacing: -1px;
  color: var(--color-text);
  text-shadow: var(--glow-score);
  font-variant-numeric: tabular-nums;
}

#score-delta {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 2px;
  color: var(--color-primary);
  min-width: 70px;
}

#top-right { display: flex; align-items: center; gap: 30px; }

#lives { display: flex; align-items: center; gap: 8px; margin-top: 6px; }

.life {
  width: 14px;
  height: 26px;
  background: var(--color-secondary);
  box-shadow: var(--glow-life-ice);
  clip-path: polygon(50% 0, 100% 100%, 50% 72%, 0 100%);
}
.life.last { background: var(--color-danger); box-shadow: var(--glow-life-red); }
.life.spent { background: rgba(125, 240, 255, .14); box-shadow: none; }

#last-tag {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 2px;
  color: var(--color-danger);
  margin-left: 4px;
}

.v-rule { width: 1px; height: 52px; background: rgba(255, 63, 208, .25); }

#level-wave {
  margin-top: 2px;
  font-size: 30px;
  font-weight: 700;
  line-height: 1;
  color: var(--color-secondary);
}
#level-wave .slash { color: var(--color-inactive); }

/* ── PANELS */
aside {
  position: absolute;
  top: 96px;
  bottom: 84px;
  width: 300px;
  padding: 24px;
  display: flex;
  flex-direction: column;
}

#left-panel {
  left: 0;
  gap: 22px;
  background: var(--color-panel);
  border-right: 1px solid var(--color-border);
}

#right-panel {
  right: 0;
  gap: 16px;
  background: var(--color-panel-deep);
  border-left: 1px solid var(--color-border);
}

.divider { height: 1px; background: var(--color-divider); }

/* ── SHIELD — hero widget */
#shield-widget { display: flex; flex-direction: column; gap: 14px; }

/* Label row across the top, so the meter and the numeral share a baseline. */
#shield-head { display: flex; flex-direction: column; gap: 3px; }
#shield-head .mono { font-size: 11px; color: var(--color-label); }
#shield-body { display: flex; gap: 20px; }

/* The column is a CANVAS of voxel cubes now, not a CSS fill: cubes are thrown
   off it when the barrier is struck, so it has to be drawable. */
/* The canvas is PADDED far past the bar so thrown cubes can fly out into the
   field instead of being clipped at the bar's edge. The element is sized to
   the padded backing store and pulled back by the pad, so the BAR still sits
   exactly where the layout expects. It must not eat clicks or take layout
   width, hence the absolute positioning and pointer-events. */
#shield-body { position: relative; }
#shield-canvas {
  position: absolute;
  left: -312px;              /* -padX: 52 * 6 */
  top: -107px;               /* -padY: 430 * .25 */
  width: 676px;              /* 52 + padX * 2 */
  height: 645px;             /* 430 + padY * 2 */
  pointer-events: none;
  z-index: 2;
  display: block;
  filter: drop-shadow(0 0 18px rgba(125, 240, 255, .35));
}
/* Reserves the bar's own footprint in the flex row, since the canvas is now
   absolutely positioned and contributes no width. */
#shield-body::before { content: ''; width: 52px; height: 430px; flex: none; display: block; }

/* Text column is position:relative so the numeral can be translated down the
   meter's height to sit at the fill line. Same height as the canvas, and the
   numeral starts at its TOP — the label now lives above the whole body. */
#shield-text { flex: 1; position: relative; height: 430px; }

.widget-title {
  font-size: 30px;
  font-weight: 700;
  line-height: 1;
  color: var(--color-text);
}

/* The numeral RIDES the fill line: game.js translates it down the column as
   integrity falls, so the reading always sits beside the level it describes.
   The tick legend is gone — the voxel meter is the readout. */
#shield-value {
  position: absolute;
  top: 0;
  left: 0;
  font-size: 56px;
  font-weight: 700;
  line-height: .9;
  color: var(--color-secondary);
  text-shadow: var(--glow-shield-numeral);
  font-variant-numeric: tabular-nums;
  transition: transform .18s cubic-bezier(.2,.7,.3,1);
  will-change: transform;
}
#shield-value.crit { color: var(--color-danger); text-shadow: 0 0 24px rgba(255, 46, 77, .75); }
#shield-value .unit { font-size: 22px; color: var(--color-text-dim); }

@media (prefers-reduced-motion: reduce) { #shield-value { transition: none; } }

/* ── RECHARGE */
#recharge-track {
  position: relative;
  height: 12px;
  margin-top: 10px;
  background: rgba(255, 63, 208, .14);
}
#recharge-fill {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  background: var(--color-primary);
  box-shadow: var(--glow-recharge);
}
.caption-row {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 2px;
  color: var(--color-text-dim);
}
.caption-row .value { color: var(--color-text); }

/* ── SHIP READOUT */
/* Takes the left panel's remaining slack so the column has no dead band. */
#ship-readout {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 2px;
  color: var(--color-label);
}
#ship-readout > div { display: flex; justify-content: space-between; }
#ship-readout .value { color: var(--color-text); font-variant-numeric: tabular-nums; }
#ship-readout .value.ice { color: var(--color-secondary); }

/* ── RIGHT PANEL */
#cards-header { display: flex; justify-content: space-between; align-items: baseline; }
#cards-header .title { font-size: 30px; font-weight: 700; letter-spacing: 4px; color: var(--color-title); }

/* PANEL SLACK. The two GROWABLE blocks take it now — the card stack gains a
   slot per CARD RACK perma and the perma list grows all run, so they are what
   needs the room. FLIGHT is a fixed set of six readouts and was taking slack
   it could never use, which is why the panel showed a dead band beneath the
   cards. NEXT DRAFT stays pinned to the floor. */
#cards-block { display: flex; flex-direction: column; min-height: 0; flex: 1.4; }
#perma-block { display: flex; flex-direction: column; min-height: 0; flex: 1; }
#card-slots, #perma-list { overflow-y: auto; min-height: 0; }
/* Thin scrollbars: an overflowing stack must not push the panel wider. */
#card-slots, #perma-list { scrollbar-width: thin; scrollbar-color: var(--color-border) transparent; }

#flight-readout-block, #run-readout-block {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: none;
}

/* LEFT panel: the ship readout and CONTROLS sit fixed, and a spacer pushes
   NEXT DRAFT to the floor. */
.hud-spacer { flex: 1; min-height: 0; }
#left-panel #controls-block { flex: none; }

#flight-readout, #run-readout {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  margin-top: 12px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 2px;
  color: var(--color-label);
}
#flight-readout > div, #run-readout > div { display: flex; justify-content: space-between; }
#flight-readout .value, #run-readout .value {
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
}
#flight-readout .value.ice, #run-readout .value.ice { color: var(--color-secondary); }
#run-readout .value.crit { color: var(--color-danger); }

#next-draft {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px;
  background: var(--color-secondary-surface, rgba(125, 240, 255, .07));
  box-shadow: inset 0 0 0 1px rgba(125, 240, 255, .28);
}
#next-draft .plate-label {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: 1px;
  color: var(--color-text);
}

.pending {
  padding: 13px 14px;
  background: repeating-linear-gradient(135deg, rgba(255, 255, 255, .03) 0 6px, transparent 6px 12px);
  box-shadow: inset 0 0 0 1px rgba(163, 140, 192, .14);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 2px;
  color: var(--color-inactive);
}

/* ── FIELD */
#field {
  position: absolute;
  left: 300px; right: 300px; top: 96px; bottom: 84px;
  overflow: hidden;
  background: var(--color-field);
  box-shadow: inset 0 0 0 1px var(--color-border);
}

/* Host is wider than the container so the arena crops — the player must never
   see a plane edge. Buffer canvases stretch to fill it. */
/* Scaled up a hair so a screen shake never pulls the field off its own top or
   bottom edge and exposes the container behind it. The horizontal crop already
   has slack; this buys ~9px of vertical overscan without distorting the 1600x900
   buffer, since the canvases stretch to the host and the ratio is preserved. */
#field-host {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  transform-origin: 50% 50%;
  width: 1600px;
  height: 900px;
  scale: 1.02;
}
#field-host canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
#scene { opacity: 0; }

/* ── TWO KINDS OF OVERLAY, AND THEY WERE SHARING ONE CLASS.
 *
 * `.overlay` was written for the DECORATIVE layers — scanlines, vignette —
 * which must never intercept a click. The draft, pause and purge MODALS then
 * reused the same class, inheriting `pointer-events: none`, so the pointer fell
 * straight through them to the field canvas beneath. That canvas carries
 * `cursor: none`, because over the arena the ship IS the pointer — so the
 * cursor vanished across the whole modal and came back only over the handful of
 * elements that re-enable pointer events on their own: buttons and draft cards.
 *
 * Setting `cursor` on the overlay cannot fix this. The browser takes cursor
 * style from the element actually under the pointer, and that element was the
 * canvas, not the modal.
 *
 * SAME ROOT CAUSE AS SG-032, where the purge dialog's buttons could not be
 * clicked at all. That was fixed by adding `pointer-events: auto` to the
 * confirm overlay alone — one symptom, not the class. Split properly here so
 * there is no third instance.
 */
.overlay { position: absolute; inset: 0; pointer-events: none; }
/* Anything the player is meant to READ AND CLICK takes the pointer. */
.overlay-draft, .overlay-pause, .overlay-confirm { pointer-events: auto; }
#scanlines { background: repeating-linear-gradient(180deg, rgba(0, 0, 0, .16) 0 1px, transparent 1px 3px); }
#vignette { box-shadow: inset 0 0 140px rgba(10, 0, 20, .7); }

/* ── BOTTOM CHROME · 84px */
#bottom-chrome {
  position: absolute;
  bottom: 0; left: 0; right: 0;
  height: 84px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 26px;
  background: linear-gradient(0deg, #1c0930, #0a0614);
  border-top: 1px solid var(--color-border);
}

#chain-block { display: flex; align-items: center; gap: 18px; }
#chain-block .title { font-size: 30px; font-weight: 700; letter-spacing: 5px; color: var(--color-title); }

#chain-value {
  font-size: 44px;
  font-weight: 700;
  line-height: 1;
  color: var(--color-primary);
  text-shadow: var(--glow-chain);
  font-variant-numeric: tabular-nums;
  min-width: 74px;
}

#chain-pips { display: flex; gap: 5px; margin-left: 8px; }

/* Reset is a hard cut — no transition on the pips, by design. */
.chain-pip {
  width: 20px;
  height: 30px;
  background: rgba(255, 63, 208, .16);
  clip-path: polygon(0 0, 60% 0, 100% 50%, 60% 100%, 0 100%, 40% 50%);
}
.chain-pip.lit { background: var(--color-primary); box-shadow: var(--glow-chain-pip); }

#bottom-right {
  display: flex;
  align-items: center;
  gap: 24px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 2px;
  color: var(--color-label);
}
#bottom-right > span { display: flex; align-items: center; gap: 8px; }

.lamp { width: 9px; height: 9px; }
.lamp.ice { background: var(--color-secondary); box-shadow: 0 0 9px var(--color-secondary); }
.lamp.ice.off { background: rgba(125, 240, 255, .18); box-shadow: none; }
.lamp.red { background: var(--color-danger); box-shadow: 0 0 9px var(--color-danger); }
.lamp.red.off { background: rgba(255, 46, 77, .18); box-shadow: none; }

#flight-status { color: var(--color-text); }

/* ── MENU SCREENS ───────────────────────────────────────────────────────────
   Built to ShatterGrid Menu Screens.dc.html. Title has NO arena behind it;
   game over keeps the arena running but pushed well down. */

.screen {
  position: absolute;
  inset: 0;
  z-index: 10;
  overflow: hidden;
}
.screen.hidden { display: none; }

.menu-scanlines {
  position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(180deg, rgba(0,0,0,.20) 0 1px, transparent 1px 3px);
}
.menu-vignette {
  position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 260px rgba(4,2,9,.92);
}

/* ── TITLE ──────────────────────────────────────────────────────────────────
   860x1000 is the WHOLE SCREEN, not a column inside 1920x1080. The stage is
   resized to it while TITLE is up. One left edge; 4/8 rhythm in three tiers. */
.title-screen {
  /* The arena's own shallow tilted floor, knocked back and desaturated, so the
     menu sits in the same world as the game. Rows compress toward the horizon
     the way the projection does. No plaid. */
  background:
    radial-gradient(620px 380px at 30% 34%, rgba(255,63,208,.10), rgba(0,0,0,0) 72%),
    repeating-linear-gradient(180deg,
      rgba(140,160,255,.030) 0 1px, rgba(0,0,0,0) 1px 7px),
    linear-gradient(180deg,
      rgba(26,22,74,.30) 0%,
      rgba(20,26,86,.42) 34%,
      rgba(8,12,44,.72) 100%),
    linear-gradient(180deg, #0a0716 0%, #07050f 62%, #050409 100%);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The tilted plane is now the GAME FIELD's own pixelated floor, rendered by
   Arena into #title-field rather than faked with CSS gradients. Explicit
   z-index on all three layers: the canvas paints first, then scanlines and
   vignette over it. Without this the later-positioned siblings covered it and
   the title showed scanlines on flat colour. */
.title-field {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
  opacity: .58;
}
.title-screen .menu-scanlines { z-index: 1; }
.title-screen .menu-vignette { z-index: 2; }

.title-inner {
  position: relative;
  /* Above the field canvas, the scanlines and the vignette. */
  z-index: 3;
  width: 100%;
  height: 100%;
  padding: 64px 60px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
}

.title-inner > * { animation: titleRise .42s cubic-bezier(.2,.7,.3,1) both; }
.title-inner > *:nth-child(1) { animation-delay: .02s; }
.title-inner > *:nth-child(2) { animation-delay: .08s; }
.title-inner > *:nth-child(3) { animation-delay: .14s; }
.title-inner > *:nth-child(4) { animation-delay: .20s; }
.title-inner > *:nth-child(5) { animation-delay: .26s; }
.title-inner > *:nth-child(6) { animation-delay: .34s; }
.title-inner > *:nth-child(7) { animation-delay: .42s; }
@keyframes titleRise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .title-inner > * { animation: none; } }

/* ── TITLE CORNER MARKS. A positioning box for the four STOLENCOW brackets,
   nothing more. It sits ABOVE the field canvas, scanlines and vignette but
   BELOW .title-inner, so the type never has a mark drawn over it. Purely
   decorative: pointer-events off. */
.title-chrome {
  position: absolute;
  inset: 18px;
  z-index: 2;
  pointer-events: none;
  /* NO FRAME. The full outline boxed the title in and fought the field's own
     vignette for the same edge. The brackets alone mark the corners, which is
     what the studio mark actually is. */
}

/* STOLENCOW corner brackets — an L of two 1px rules per corner, in the magenta
   channel. With the frame gone they are the only chrome on the screen, so they
   carry a little more length and opacity than they did as an accent inside it,
   and they sit on the box edge rather than inset within a border that is no
   longer drawn. */
.tc-bracket {
  position: absolute;
  width: 34px;
  height: 34px;
  border-color: var(--color-primary);
  opacity: .7;
}
.tc-bracket.tl { top: 0;    left: 0;  border-top: 1px solid; border-left: 1px solid; }
.tc-bracket.tr { top: 0;    right: 0; border-top: 1px solid; border-right: 1px solid; }
.tc-bracket.bl { bottom: 0; left: 0;  border-bottom: 1px solid; border-left: 1px solid; }
.tc-bracket.br { bottom: 0; right: 0; border-bottom: 1px solid; border-right: 1px solid; }

.title-eyebrow {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 6px;
  color: var(--color-label);
  /* Pulled up off the wordmark: the credit line was sitting too close to
     SHATTER and reading as part of it. */
  margin-top: -20px;
}

.wordmark-wrap { position: relative; margin-top: 24px; }
.title-word {
  margin: 0;
  font-size: 104px;
  font-weight: 700;
  line-height: .86;
  letter-spacing: 5px;
  color: var(--color-text);
  text-shadow: 0 0 62px rgba(255,63,208,.60), 0 0 22px rgba(255,255,255,.26);
}
.wordmark-scan {
  position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(180deg, rgba(8,5,15,.32) 0 1px, transparent 1px 4px);
}

.title-tag { display: flex; align-items: center; gap: 16px; margin-top: 24px; }
.tag-rule { width: 68px; height: 1px; background: rgba(255,63,208,.55); }
.tag-text {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 7px;
  color: #ff9ae0;
}

/* ROSTER — every enemy that spawns, at arena scale. Canvases are sized and
   drawn by game.js using the same voxel primitive the field uses. */
.title-roster {
  display: flex;
  flex-wrap: wrap;
  /* Feet on one line: the canvases differ in height and each one now carries
     bounce headroom on top, so the row aligns on its baseline, not its centre. */
  align-items: flex-end;
  gap: 16px 20px;
  margin-top: 40px;
  width: 100%;
}
.roster-chip { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 6px; }
.roster-chip canvas { display: block; }
.roster-chip .rname {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 1.5px;
  color: var(--color-inactive);
}

.title-brief {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 40px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 2px;
}
.brief-line { display: flex; gap: 20px; }
.bk { width: 96px; flex: none; color: var(--color-label); }
.bv { color: var(--color-text); }
.bv.warn { color: var(--color-danger-dim); }

/* A real button: its own surface, hover and press states, keyboard focus. */
.title-enter {
  margin-top: 32px;
  padding: 16px 34px;
  font-family: var(--font-mono);
  font-size: 14px;
  letter-spacing: 7px;
  color: var(--color-secondary);
  background: rgba(125,240,255,.07);
  border: 0;
  box-shadow: inset 0 0 0 1px rgba(125,240,255,.42);
  cursor: pointer;
  transition: background .12s, box-shadow .12s, color .12s;
}
.title-enter:hover {
  background: rgba(125,240,255,.15);
  box-shadow: inset 0 0 0 1px rgba(125,240,255,.80);
  color: var(--color-text);
}
.title-enter:active { background: rgba(125,240,255,.22); }
.title-enter:focus-visible { outline: 2px solid var(--color-secondary); outline-offset: 3px; }

.title-stamp {
  display: flex;
  align-items: center;
  gap: 18px;
  width: 100%;
  margin-top: 40px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 3px;
  color: var(--color-label);
}
.stamp-status { display: flex; align-items: center; gap: 10px; }
.stamp-dot {
  width: 8px; height: 8px; flex: none;
  background: var(--color-secondary);
  box-shadow: 0 0 9px var(--color-secondary);
  animation: stampPulse 2.4s ease-in-out infinite;
}
@keyframes stampPulse { 0%, 100% { opacity: .5; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .stamp-dot { animation: none; opacity: 1; } }
.stamp-rule { flex: 1; height: 1px; background: rgba(255,63,208,.18); }
.stamp-ver { color: var(--color-inactive); }
/* ── GAME OVER / CONTINUE ── */
.over-screen { background: transparent; }

/* NOTES item 1: .90 -> .95. The arena keeps running, just further down. It
   should read as depth, not as content. */
.over-scrim { position: absolute; inset: 0; background: rgba(6,4,12,.95); }

/* NOTES item 2: two low additive blooms, magenta behind the numeral and a
   cooler one under the plates so Continue sits in its own light. Both <= .18 —
   this screen must stay quieter than the in-game HUD. */
.over-bloom-warm {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(720px 420px at 50% 30%, rgba(255,63,208,.16), transparent 70%);
}
.over-bloom-cool {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(900px 380px at 50% 78%, rgba(125,240,255,.07), transparent 72%);
}
.over-vignette {
  position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 240px rgba(10,0,20,.92);
}

.over-column {
  position: absolute;
  left: 0; right: 0; top: 60px;
  display: flex; flex-direction: column; align-items: center;
}

/* NOTES item 4: GAME OVER becomes the largest type and the score demotes. The
   magenta bloom behind it does the work, not a heavy text-shadow. The score is
   still the largest numeral inside the running game; this is terminal. */
.over-heading {
  font-size: 148px; font-weight: 700;
  line-height: .84; letter-spacing: 8px; text-indent: 8px;
  color: var(--color-text);
}

.over-score-row { display: flex; align-items: baseline; gap: 20px; margin-top: 22px; }
.over-score-row > .tabular {
  font-size: 96px; font-weight: 700;
  line-height: .82; letter-spacing: -2px;
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
}
.over-final {
  font-family: var(--font-mono);
  font-size: 14px; letter-spacing: 4px;
  color: var(--color-label);
}

/* NOTES item 3: the strip and the plate row share one width (1062px) and the
   cells divide it with grid rather than hand-computed widths. */
.stats-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  width: 1062px;
  margin-top: 44px;
  box-shadow: inset 0 0 0 1px rgba(255,63,208,.22);
}
.stat-cell { padding: 22px 24px; border-right: 1px solid rgba(255,63,208,.18); }
.stat-cell:last-child { border-right: none; }
.stat-label {
  font-family: var(--font-mono);
  font-size: 12px; letter-spacing: 4px;
  color: var(--color-label);
}
.stat-value {
  margin-top: 8px;
  font-size: 38px; font-weight: 700; line-height: 1;
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
}
.stat-value.ice { color: var(--color-secondary); }
.stat-value.magenta { color: var(--color-primary); }
.stat-value .slash { color: var(--color-inactive); }

.over-plates { display: flex; gap: 22px; margin-top: 48px; align-items: stretch; }

.over-plate {
  width: 520px;
  display: flex; flex-direction: column; gap: 22px;
  padding: 26px 28px;
  cursor: pointer;
  transition: background .12s, box-shadow .12s;
}
.over-continue {
  background: rgba(125,240,255,.09);
  box-shadow: inset 0 0 0 1px rgba(125,240,255,.40);
  border-left: 4px solid var(--color-secondary);
}
.over-continue:hover {
  background: rgba(125,240,255,.16);
  box-shadow: inset 0 0 0 1px rgba(125,240,255,.70);
}
.over-newgame {
  background: repeating-linear-gradient(135deg, rgba(255,255,255,.028) 0 7px, transparent 7px 14px);
  box-shadow: inset 0 0 0 1px rgba(163,140,192,.20);
}
.over-newgame:hover { box-shadow: inset 0 0 0 1px rgba(163,140,192,.42); }

.over-plate-head { display: flex; align-items: baseline; justify-content: space-between; }
.over-plate-name {
  font-size: 34px; font-weight: 700; letter-spacing: 3px;
  color: var(--color-text);
}
.over-plate-name.dim { color: var(--color-label); }
.over-plate-tag {
  font-family: var(--font-mono);
  font-size: 12px; letter-spacing: 3px;
}
.over-plate-tag.ice { color: var(--color-secondary); }
/* NOTES item 5: HOLD is live informational text, not an inactive state. */
.over-plate-tag.guard { color: var(--color-label); }

.over-plate-lines {
  display: flex; flex-direction: column; gap: 9px;
  font-family: var(--font-mono);
  font-size: 13px; letter-spacing: 1px;
}
.over-plate-lines > div { display: flex; gap: 10px; }
.over-plate-lines .k { flex: none; }
.over-plate-lines .k.ice { color: var(--color-secondary); }
.over-plate-lines .k.dim { color: var(--color-text-dim); }
/* Red names the destructive line only; it is never a button colour here. */
.over-plate-lines .k.danger { color: var(--color-danger-dim); }
.over-plate-lines .v { color: var(--color-text); }
.over-plate-lines .v.dim2 { color: var(--color-label); }
.over-plate-lines .guard-track { display: block; }
/* ── AMMO PIPS. Segmented counter: one pip per bullet in flight. */
#ammo-pips { display: flex; gap: 3px; margin-top: 10px; }
#ammo-pips > div {
  flex: 1;
  height: 12px;
  background: rgba(255, 63, 208, .14);
}
#ammo-pips > div.lit {
  background: var(--color-primary);
  box-shadow: var(--glow-recharge);
}


/* ── 3-2-1-GO. Over the visible board, per Dead Ascent. */
#countdown {
  position: absolute;
  inset: 0;
  z-index: 12;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
#countdown.hidden { display: none; }
.count-num {
  font-size: 200px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 10px;
  color: var(--color-text);
  text-shadow: 0 0 80px rgba(255, 63, 208, .8), 0 0 24px rgba(255, 255, 255, .5);
}
.count-num.go {
  color: var(--color-secondary);
  text-shadow: 0 0 80px rgba(125, 240, 255, .85), 0 0 24px rgba(255, 255, 255, .5);
}
/* ══════════════════════════════════════════════════════════════════════════
 * M1 — CONTROLS, PAUSE, CARDS, DRAFT, WAVE TITLE
 * Control-button and pause-overlay STRUCTURE copied from lodestar/style.css
 * (.ctrl-grid, .ctrl-btn, .ctrl-btn .kc, .overlay-pause, .pause-box,
 * .pause-word, .pause-hint, .pause-cta), re-skinned to ShatterGrid tokens:
 * magenta is the game voice, ice is player systems.
 * ══════════════════════════════════════════════════════════════════════════ */

.ctrl-row { display: flex; justify-content: space-between; align-items: baseline; }
.ctrl-row .eyebrow { font-size: 9px; letter-spacing: 2px; color: var(--color-inactive); }
.ctrl-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 10px; }
.ctrl-btn {
  display: flex; align-items: center; gap: 8px; padding: 9px 10px; cursor: pointer;
  border: 1px solid var(--color-border); background: var(--color-primary-surface);
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .18em;
  color: var(--color-text-dim);
  transition: border-color var(--anim-fast), color var(--anim-fast), box-shadow var(--anim-fast);
}
.ctrl-btn:hover { border-color: var(--color-primary); color: var(--color-title); box-shadow: 0 0 14px var(--color-primary-inset-dim); }
.ctrl-btn:focus-visible { outline: 2px solid var(--color-secondary); outline-offset: 2px; }
.ctrl-btn.active { border-color: var(--color-primary); color: var(--color-primary); box-shadow: 0 0 14px var(--color-primary-inset); }
.ctrl-btn .kc {
  border: 1px solid var(--color-primary-inset); padding: 1px 6px; color: var(--color-text);
  font-size: 10px; letter-spacing: .1em;
}
#controls-block #run-readout { margin-top: 12px; }

/* ── PAUSE ─────────────────────────────────────────────────────────────── */
.overlay-pause {
  position: absolute; inset: 0; z-index: 50;
  background: radial-gradient(130% 100% at 50% 42%, rgba(8,5,15,.955), rgba(3,2,8,.995));
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  animation: pauseIn .18s cubic-bezier(.2,.7,.3,1);
}
.overlay-pause.hidden { display: none; }
@keyframes pauseIn { from { opacity: 0; } to { opacity: 1; } }
.pause-box { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.pause-word {
  font-family: var(--font-display); font-weight: 700; font-size: 76px;
  letter-spacing: .2em; color: var(--color-primary); text-shadow: var(--glow-score);
}
.pause-hint { font-family: var(--font-mono); font-size: 11px; letter-spacing: .28em; color: var(--color-text-dim); }
.pause-cta {
  display: flex; align-items: center; gap: 10px; margin-top: 6px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .26em; color: var(--color-secondary);
}
.pause-cta .keycap {
  border: 1px solid var(--color-ice-inset-hi); padding: 2px 8px; color: var(--color-accent);
}

/* ── CARD SLOTS. Ice is the player's channel, so cards are ice. ───────── */
#card-slots { display: flex; flex-direction: column; gap: 7px; margin-top: 12px; }
.card-slot {
  position: relative; display: grid; grid-template-columns: auto 1fr auto; gap: 2px 8px;
  align-items: center;
  padding: 9px 10px; cursor: pointer; text-align: left; width: 100%;
  border: 1px solid var(--color-ice-inset); background: var(--color-ice-surface);
  font-family: var(--font-mono); color: var(--color-text-dim);
  transition: border-color var(--anim-fast), box-shadow var(--anim-fast), background var(--anim-fast);
}
.card-slot:hover:not(.drained):not(.locked) {
  border-color: var(--color-secondary); background: var(--color-ice-surface-hi);
  box-shadow: var(--glow-ice-soft);
}
.card-slot:focus-visible { outline: 2px solid var(--color-secondary); outline-offset: 2px; }
.card-slot .cs-name {
  font-family: var(--font-display); font-weight: 600; font-size: 12px;
  letter-spacing: .1em; color: var(--color-accent);
}
.card-slot .cs-cost {
  font-size: 11px; letter-spacing: .06em; color: var(--color-primary);
  text-shadow: 0 0 10px var(--color-primary-inset); align-self: center;
}
/* Hotkey badge. Same keycap language as the CONTROLS buttons, in the player's
   ice channel because cards are the player's. */
.card-slot .cs-key {
  grid-row: 1 / 3;
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px;
  border: 1px solid var(--color-ice-inset-hi);
  font-size: 10px; letter-spacing: 0; color: var(--color-accent);
}
.card-slot.drained .cs-key { border-color: var(--color-drained-inset); color: var(--color-inactive); }
.card-slot.locked .cs-key { border-color: var(--color-danger-inset); color: var(--color-danger-dim); }
.card-slot .cs-desc { grid-column: 2 / -1; font-size: 9px; letter-spacing: .04em; color: var(--color-inactive); }
/* Unaffordable uses the reserved danger channel. */
.card-slot.locked { cursor: not-allowed; border-color: var(--color-danger-inset); background: var(--color-danger-surface); }
.card-slot.locked .cs-cost { color: var(--color-danger); text-shadow: none; }
.card-slot.drained {
  cursor: default; border-color: var(--color-drained-inset); background: transparent;
  background-image: var(--color-drained-hatch);
}
.card-slot.drained .cs-name, .card-slot.drained .cs-cost { color: var(--color-inactive); text-shadow: none; }
@keyframes cardFire { 0% { box-shadow: 0 0 0 rgba(125,240,255,0); } 30% { box-shadow: 0 0 30px rgba(125,240,255,.85); } 100% { box-shadow: 0 0 0 rgba(125,240,255,0); } }
.card-slot.firing { animation: cardFire .42s ease-out; }

/* ── UTILITY BAR. Deliberately NOT card-shaped: it sits in the magenta spend
   channel rather than the player's ice channel, spans the full width, has no
   hotkey badge and no two-line body. Nothing about it should read as a weapon
   sitting in the card stack. */
.util-bar {
  display: flex; align-items: center; gap: 10px; width: 100%;
  margin-top: 9px; padding: 8px 10px; cursor: pointer;
  border: 1px dashed var(--color-primary-inset);
  background: var(--color-primary-surface);
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .18em;
  color: var(--color-label);
  transition: border-color var(--anim-fast), color var(--anim-fast), box-shadow var(--anim-fast);
}
.util-bar:hover:not(.locked) {
  border-color: var(--color-primary); color: var(--color-text);
  box-shadow: 0 0 14px var(--color-primary-inset-dim);
}
.util-bar:focus-visible { outline: 2px solid var(--color-secondary); outline-offset: 2px; }
.util-bar.locked { cursor: not-allowed; opacity: .42; }
.util-bar .util-icon { font-size: 13px; line-height: 1; color: var(--color-primary); }
.util-bar .util-label { flex: 1; text-align: left; }
.util-bar .util-cost { color: var(--color-primary); }
.util-bar.locked .util-cost { color: var(--color-danger); }

#perma-list { display: flex; flex-direction: column; gap: 5px; margin-top: 12px; }
.perma-row {
  display: flex; justify-content: space-between; gap: 8px;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em;
  color: var(--color-text-dim);
}
.perma-row .pv { color: var(--color-secondary); }

/* ── DRAFT ─────────────────────────────────────────────────────────────── */
.overlay-draft {
  position: absolute; inset: 0; z-index: 60;
  background: radial-gradient(120% 100% at 50% 45%, rgba(26,10,44,.9), rgba(3,2,8,.97));
  display: flex; align-items: center; justify-content: center;
  animation: pauseIn .2s cubic-bezier(.2,.7,.3,1);
}
.overlay-draft.hidden { display: none; }
.draft-box {
  width: min(980px, 92%); padding: 30px 32px 26px;
  background: linear-gradient(180deg, rgba(26,10,44,.98), rgba(8,5,15,.99));
  border: 1px solid var(--color-border); box-shadow: 0 0 60px var(--color-primary-track);
}
.draft-head { display: flex; justify-content: space-between; align-items: baseline; }
.draft-eyebrow {
  display: flex; align-items: center; gap: 9px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .3em; color: var(--color-primary);
}
.draft-dot { width: 7px; height: 7px; background: var(--color-primary); box-shadow: var(--glow-chain-pip); }
.draft-bank { font-size: 11px; letter-spacing: .2em; color: var(--color-label); }
.draft-bank .tabular { color: var(--color-text); font-variant-numeric: tabular-nums; }
.draft-offers { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-top: 22px; }
.draft-offer {
  display: flex; flex-direction: column; gap: 10px; padding: 20px 18px 18px; cursor: pointer;
  text-align: left; border: 1px solid var(--color-ice-inset); background: var(--color-ice-surface);
  transition: border-color var(--anim-fast), box-shadow var(--anim-fast), transform var(--anim-fast);
  animation: draftRise .3s cubic-bezier(.2,.7,.3,1) both;
}
.draft-offer:nth-child(1) { animation-delay: .04s; }
.draft-offer:nth-child(2) { animation-delay: .10s; }
.draft-offer:nth-child(3) { animation-delay: .16s; }
@keyframes draftRise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.draft-offer:hover:not(.locked) {
  border-color: var(--color-secondary); box-shadow: var(--glow-ice-mid);
  transform: translateY(-2px);
}
.draft-offer:focus-visible { outline: 2px solid var(--color-secondary); outline-offset: 3px; }
.draft-offer.locked { cursor: not-allowed; border-color: var(--color-danger-inset); background: var(--color-danger-surface); }
.draft-offer .do-name {
  font-family: var(--font-display); font-weight: 700; font-size: 17px;
  letter-spacing: .08em; color: var(--color-accent);
}
.draft-offer .do-desc {
  font-family: var(--font-mono); font-size: 10px; line-height: 1.6; letter-spacing: .04em;
  color: var(--color-text-dim); flex: 1;
}
.draft-offer .do-cost {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: .14em;
  color: var(--color-primary); text-shadow: 0 0 12px rgba(255,63,208,.55);
}
.draft-offer.locked .do-cost { color: var(--color-danger); text-shadow: none; }
.draft-foot { display: flex; gap: 12px; margin-top: 22px; }
.draft-btn {
  display: flex; align-items: center; gap: 10px; padding: 11px 16px; cursor: pointer;
  border: 1px solid var(--color-border); background: var(--color-primary-surface);
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .18em; color: var(--color-label);
  transition: border-color var(--anim-fast), color var(--anim-fast), box-shadow var(--anim-fast);
}
.draft-btn:hover { border-color: var(--color-primary); color: var(--color-text); box-shadow: var(--glow-primary-mid); }
.draft-btn:focus-visible { outline: 2px solid var(--color-secondary); outline-offset: 2px; }
.draft-btn.locked { cursor: not-allowed; opacity: .4; }
.draft-btn.skip { margin-left: auto; }
.draft-btn .kc { border: 1px solid var(--color-primary-inset); padding: 1px 6px; color: var(--color-text); font-size: 10px; }
.draft-btn .draft-cost { color: var(--color-primary); }

@media (prefers-reduced-motion: reduce) {
  .overlay-pause, .overlay-draft, .draft-offer, .card-slot.firing { animation: none; }
  .draft-offer:hover { transform: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
 * M2 — BOSS HP BAND. Across the top of the field, above the plane and under
 * the overlays. Colours follow the boss body: CORE magenta when the core is
 * open, LOCK grey when it cannot be damaged. The sheets are explicit that the
 * field and the HUD must always agree.
 * ══════════════════════════════════════════════════════════════════════════ */
/* IN THE CHROME, not over the field. It was absolutely positioned at top:96px
   across the arena at z-index 12, so a boss that rides the back wall — the
   Mirror's reflector — drew behind its own health bar. A readout belongs in
   the readout bar. As a flex child of #top-chrome it takes the middle space
   between the score group and the lives, and disappears cleanly when hidden. */
#boss-band {
  /* Fixed to the bar's own width, not flex:1 — the canvas is a fixed-size
     backing store and a stretching container would not match it. */
  flex: none;
  width: 820px;
  margin: 0 28px;
  padding: 0;
  background: none;
  pointer-events: none;
  animation: bossIn .3s cubic-bezier(.2,.7,.3,1);
}
#boss-band.hidden { display: none; }
@keyframes bossIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: none; } }

#boss-band-head {
  display: flex; align-items: baseline; gap: 10px;
  font-family: var(--font-display); font-weight: 700;
  font-size: 12px; letter-spacing: .18em; color: var(--color-title);
  white-space: nowrap;
}
/* Terminal-style prefix. Magenta is the game's own voice, so the machine is
   announcing the boss rather than the boss labelling itself. */
.boss-tag {
  font-family: var(--font-mono); font-weight: 500;
  font-size: 11px; letter-spacing: .1em; color: var(--color-primary);
}
/* Trailing descriptor, subordinate to the name it describes. */
.boss-sub {
  font-family: var(--font-mono); font-weight: 400;
  font-size: 9px; letter-spacing: .22em; color: var(--color-inactive);
}
/* The percentage sits in the header now. It used to ride the bar's drain edge
   into the field below — there is no field below it any more, and a number
   that moves is harder to read at a glance than one that holds still. */
#boss-band-head .tabular {
  margin-left: auto;
  font-family: var(--font-mono); font-weight: 500;
  font-size: 12px; letter-spacing: .06em; color: var(--color-primary);
}
#boss-band-head .mono {
  font-family: var(--font-mono); font-weight: 400;
  font-size: 10px; letter-spacing: .2em; color: var(--color-label);
}
#boss-lamp { align-self: center; }
#boss-lamp {
  width: 10px; height: 10px; flex: none;
  background: var(--color-inactive);                       /* LOCK — core cannot be damaged */
  box-shadow: none;
  transition: background var(--anim-fast), box-shadow var(--anim-fast);
}
#boss-lamp.open {
  background: var(--color-primary);
  box-shadow: 0 0 14px rgba(255,63,208,.9);
}

#boss-track {
  position: relative; margin-top: 5px; height: 50px;
}
/* The bar is a CANVAS of voxel cubes now — same machinery as the BARRIER
   column, turned on its side, so a boss losing HP throws real cubes the way
   the player's barrier does. NO TICK MARKS: the phase cuts announce themselves
   by colour (magenta > 50% > amber > 25% > red), which reads faster than a
   notch you have to measure against. */
/* PADDED like the barrier column, so cubes knocked off the bar fall DOWN into
   the field rather than being clipped at the band's edge. The element is
   offset by the pad so the bar itself still sits at the top of the band. */
/* Offset back by the pad so the BAR still sits at the track's origin while the
   canvas around it carries room for thrown cubes. Sized to the chrome's middle
   column rather than to the field. */
#boss-canvas {
  position: absolute;
  left: -40px;               /* -padX */
  top: -18px;                /* -padY */
  width: 900px;              /* barW 820 + padX * 2 */
  height: 86px;              /* barH 50 + padY * 2 */
  pointer-events: none;
  display: block;
  filter: drop-shadow(0 0 12px rgba(255, 63, 208, .45));
}

/* The percentage rides the DRAIN EDGE, following the bar down the way the
   barrier numeral follows its fill. */
/* Sits in the header row now, so it is a normal flex item: no absolute
   positioning, no travel. Only its colour changes, tracking the bar's own
   integrity steps. */
#boss-pct {
  transition: color var(--anim-fast);
  white-space: nowrap;
}
#boss-pct.mid { color: #ffca3a; }
#boss-pct.low { color: var(--color-danger); }
#boss-pct.locked { color: var(--color-inactive); }

@media (prefers-reduced-motion: reduce) {
  #boss-band { animation: none; }
  #boss-pct { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
 * TITLE ACTIONS · RESUME · PURGE CONFIRM
 * Confirm-modal structure copied from dead_ascent (overlay-confirm /
 * confirm-box / confirm-scan / confirm-eyebrow / confirm-actions), restyled
 * to ShatterGrid's channels. Every colour here is a TOKEN: red is reserved
 * for danger, so the purge action is the only red thing on the screen.
 * ══════════════════════════════════════════════════════════════════════════ */

.title-actions { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }

.title-resume {
  display: flex; align-items: baseline; gap: 12px;
  padding: 10px 18px; cursor: pointer;
  border: 1px solid var(--color-ice-inset);
  background: var(--color-ice-surface);
  font-family: var(--font-display); font-weight: 600;
  font-size: 13px; letter-spacing: .2em; color: var(--color-accent);
  transition: border-color var(--anim-fast), box-shadow var(--anim-fast), color var(--anim-fast);
}
.title-resume.hidden { display: none; }
.title-resume:hover { border-color: var(--color-secondary); color: var(--color-text); box-shadow: var(--glow-ice-soft); }
.title-resume:focus-visible { outline: 2px solid var(--color-secondary); outline-offset: 3px; }
.title-resume .tr-detail { font-size: 10px; letter-spacing: .16em; color: var(--color-text-dim); }

/* Purge sits in the stamp row, deliberately small and last: destructive
   actions are separated from the primary path, never adjacent to it. */
.stamp-wipe {
  margin-left: auto; padding: 4px 10px; cursor: pointer;
  border: 1px solid var(--color-danger-inset);
  background: transparent;
  font-family: var(--font-mono); font-size: 9px; letter-spacing: .2em;
  color: var(--color-danger-dim);
  transition: border-color var(--anim-fast), color var(--anim-fast), background var(--anim-fast);
}
.stamp-wipe.hidden { display: none; }
.stamp-wipe:hover { border-color: var(--color-danger); color: var(--color-danger); background: var(--color-danger-surface); }
.stamp-wipe:focus-visible { outline: 2px solid var(--color-danger); outline-offset: 2px; }

/* ── CONFIRM MODAL ─────────────────────────────────────────────────────── */
.overlay-confirm {
  /* pointer-events is granted with the other interactive overlays at the
     `.overlay` split — it used to be re-enabled here alone, which fixed this
     dialog's buttons (SG-032) while leaving the draft and pause modals with
     the same defect. */
  position: absolute; inset: 0; z-index: 80;
  display: flex; align-items: center; justify-content: center;
  /* Scrim heavy enough to isolate the foreground — the modal is a decision
     point, not a layer you can read past. */
  background: radial-gradient(120% 100% at 50% 45%, rgba(28, 6, 12, .86), rgba(3, 2, 8, .96));
  backdrop-filter: blur(3px);
  animation: confirmIn var(--anim-normal) var(--anim-easing);
}
.overlay-confirm.hidden { display: none; }
@keyframes confirmIn { from { opacity: 0; } to { opacity: 1; } }

.confirm-box {
  position: relative; overflow: hidden;
  width: min(520px, 86%); padding: 34px 34px 28px;
  background: linear-gradient(180deg, var(--color-confirm-top), var(--color-confirm-bottom));
  border: 1px solid var(--color-danger);
  box-shadow: 0 0 60px var(--color-danger-glow);
  animation: confirmRise var(--anim-normal) var(--anim-easing) both;
}
@keyframes confirmRise { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }

/* The travelling red scan line is GONE. On a destructive dialog a moving red
   bar reads as a process already running, which is exactly the wrong thing to
   say while the player is still deciding. The box keeps its red border and
   glow; nothing on it moves. */
.confirm-scan { display: none; }

.confirm-eyebrow {
  display: flex; align-items: center; gap: 9px;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .3em;
  color: var(--color-danger-dim);
}
.confirm-dot {
  width: 7px; height: 7px; flex: none;
  background: var(--color-danger);
  box-shadow: var(--glow-danger-dot);
}

.confirm-title {
  margin: 18px 0 0;
  font-family: var(--font-display); font-weight: 700;
  font-size: 26px; letter-spacing: .04em; color: var(--color-text);
}
.confirm-body {
  margin: 12px 0 0;
  font-family: var(--font-mono); font-size: 11px; line-height: 1.7;
  letter-spacing: .04em; color: var(--color-text-dim);
  max-width: 44ch;
}

.confirm-actions { display: flex; gap: 12px; margin-top: 26px; }
.confirm-btn {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 18px; cursor: pointer;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .18em;
  transition: border-color var(--anim-fast), color var(--anim-fast), box-shadow var(--anim-fast), background var(--anim-fast);
}
.confirm-btn:focus-visible { outline: 2px solid var(--color-secondary); outline-offset: 3px; }
/* Cancel is the SAFE default and gets the calm channel. */
.confirm-btn.cancel {
  border: 1px solid var(--color-ice-inset);
  background: var(--color-ice-surface);
  color: var(--color-accent);
}
.confirm-btn.cancel:hover { border-color: var(--color-secondary); color: var(--color-text); box-shadow: var(--glow-ice-soft); }
/* Danger is visually subordinate in weight but unmistakable in colour, and it
   sits to the RIGHT, away from the reflexive first click. */
.confirm-btn.danger {
  margin-left: auto;
  border: 1px solid var(--color-danger);
  background: var(--color-danger-surface);
  color: var(--color-danger);
}
.confirm-btn.danger:hover { background: var(--color-danger); color: var(--color-bg); box-shadow: 0 0 22px var(--color-danger-glow); }
.confirm-btn .keycap {
  border: 1px solid currentColor; padding: 1px 6px;
  font-size: 10px; letter-spacing: .08em; opacity: .8;
}

@media (prefers-reduced-motion: reduce) {
  .overlay-confirm, .confirm-box, .confirm-scan { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
 * M3 — THE ARENA RECORD AND THE RUN COMPLETE WAGER
 *
 * The board is a DATA TABLE, so it is built like one: tabular figures so the
 * score column cannot jitter between rows, a fixed four-column grid so ranks
 * and dates line up down the whole list, and the cleared state stated in TEXT
 * as well as in colour. Rank is the only decorative element.
 * ══════════════════════════════════════════════════════════════════════════ */

.tb-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 12px;
}
.tb-title {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: .32em;
  color: var(--color-label);
}
.tb-rule { flex: 1; height: 1px; background: var(--color-divider); }
.tb-note { font-size: 10px; letter-spacing: .22em; color: var(--color-inactive); }

.score-table {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
}

/* Four columns, fixed: rank / score / reach / date. The score column is the
   widest and right-aligned so digits stack against a common edge. */
.score-row {
  display: grid;
  grid-template-columns: 30px 1fr 108px 84px;
  align-items: baseline;
  gap: 14px;
  padding: 6px 10px;
  border-left: 2px solid transparent;
  background: rgba(255, 255, 255, .015);
  transition: background var(--anim-fast), border-color var(--anim-fast);
}
.score-row:nth-child(odd) { background: rgba(255, 255, 255, .035); }

.sr-rank {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: .1em;
  color: var(--color-inactive);
}
.sr-score {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 1px;
  color: var(--color-title);
  text-align: right;
}
.sr-wave { font-size: 10px; letter-spacing: .14em; color: var(--color-text-dim); }
.sr-date { font-size: 10px; letter-spacing: .1em; color: var(--color-inactive); text-align: right; }

/* A run that beat the campaign. Marked in ice AND spelled out as CLEARED in
   the reach column, so the distinction never rests on colour alone. */
.score-row.cleared .sr-wave { color: var(--color-secondary); }

/* The run just banked. It has to be findable in a ten-row list at a glance,
   so it takes the magenta edge and a one-shot flash — not a loop, which would
   keep pulling the eye long after it has done its job. */
.score-row.fresh {
  border-left-color: var(--color-primary);
  background: var(--color-primary-surface);
  animation: scoreFresh .9s ease-out 1;
}
.score-row.fresh .sr-score { color: var(--color-primary); text-shadow: var(--glow-score); }
.score-row.fresh .sr-rank { color: var(--color-primary); }
@keyframes scoreFresh {
  0%   { background: var(--color-primary-surface-hi); box-shadow: inset 0 0 30px rgba(255, 63, 208, .30); }
  100% { background: var(--color-primary-surface); box-shadow: none; }
}

.score-empty {
  padding: 14px 10px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .24em;
  color: var(--color-inactive);
  background: rgba(255, 255, 255, .02);
}

/* Title board: sits in the column on the same left edge as everything else.
   Capped in height and scrollable so a full table can never push the stamp
   footer off the bottom of the screen. */
/* THREE ROWS, and no scroll. The limit is applied to the ROWS, not to the
   container's height. A pixel max-height has to GUESS the row height, and a
   guess even slightly low slices the last row in half — which reads as a
   rendering fault, not as an invitation to scroll. Hiding row 4 onward lets
   the rows set the height themselves, so a whole number of rows is the only
   thing that can be drawn. The deeper board is on the game over screen, where
   the player is already reading their result. */
.title-board { width: 100%; max-width: 620px; margin-top: 34px; }
.title-board .score-row:nth-child(n + 4) { display: none; }
/* 1062px, NOT 100%. .over-column is left:0/right:0 across the whole 1920 stage
   and only CENTRES its children, so every row sets its own measure: the stats
   strip is 1062 and the plate pair is 520 + 22 + 520 = 1062. At 100% the board
   ran the full stage width and broke the column's one alignment rule. */
.over-board { width: 1062px; margin-top: 30px; }
/* SIX rows, by the same row-count rule rather than a guessed pixel height.
   Ten would push the plate pair off the bottom of the 1080 frame: the column
   already runs from top:60 through a 124px heading, the score row and the
   stats strip before the board starts. */
.over-board .score-row:nth-child(n + 7) { display: none; }

/* ── RUN COMPLETE ─────────────────────────────────────────────────────────
 * Deliberately NOT the game-over palette. Game over is red and cool; this is
 * the ice/victory channel, and red appears exactly once — on the line that
 * says what PUSH ON risks. */
.rc-screen { background: transparent; }
.rc-scrim { position: absolute; inset: 0; background: rgba(5, 3, 12, .95); }
/* One bloom, ice, high and centred: the arena reads as lit rather than as
   burning, which is what separates this screen from the death one. */
.rc-bloom {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 46% at 50% 34%, rgba(125, 240, 255, .16), transparent 70%);
}
/* Same shape as .over-column: full stage width, children centred, each row
   setting its own 1062px measure. Giving this column a hard 1062 AND 24px of
   padding would have squeezed the 1062px stats strip inside it by 48px, since
   box-sizing is border-box globally. */
.rc-column {
  position: relative;
  z-index: 3;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
/* Every direct child shares the column measure the plates and strip define. */
.rc-eyebrow, .rc-heading, .rc-score-row, .rc-plates { width: 1062px; }
.rc-column > * { animation: rcRise .5s cubic-bezier(.2,.7,.3,1) both; }
.rc-column > *:nth-child(1) { animation-delay: .04s; }
.rc-column > *:nth-child(2) { animation-delay: .12s; }
.rc-column > *:nth-child(3) { animation-delay: .20s; }
.rc-column > *:nth-child(4) { animation-delay: .28s; }
.rc-column > *:nth-child(5) { animation-delay: .38s; }
@keyframes rcRise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

.rc-eyebrow {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: .3em;
  color: var(--color-secondary);
}
.rc-dot {
  width: 7px; height: 7px; flex: none;
  background: var(--color-secondary);
  box-shadow: 0 0 10px var(--color-secondary);
}
.rc-heading {
  margin-top: 16px;
  font-size: 78px;
  font-weight: 700;
  line-height: .9;
  letter-spacing: 6px;
  color: var(--color-text);
  text-shadow: 0 0 60px rgba(125, 240, 255, .55), 0 0 20px rgba(255, 255, 255, .3);
}

.rc-score-row { display: flex; align-items: baseline; gap: 18px; margin-top: 20px; }
#rc-score {
  font-family: var(--font-display);
  font-size: 62px;
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--color-primary);
  text-shadow: var(--glow-score);
}
.rc-banked {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: .34em;
  color: var(--color-label);
}
.rc-stats { margin-top: 26px; }

/* The two plates. Equal weight visually — this is a genuine choice, so
   neither is styled as the default. They differ in CHANNEL, not in size. */
/* STACKED, at the column measure. Two side-by-side plates at `width: 100%`
   overrode the 1062px set above (same specificity, later rule) and ran the
   whole 1920 stage, which is why they did not line up with the stats strip
   above them. Every other row in this column is one full-width block at 1062;
   these are too. */
.rc-plates {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 1062px;
  margin-top: 28px;
}
/* Wide and short now rather than tall and narrow: the name sits left, its
   consequences run beside it, and the tag holds the right edge. */
.rc-plate {
  display: flex;
  align-items: center;
  gap: 40px;
  padding: 16px 22px;
  cursor: pointer;
  border: 1px solid var(--color-border);
  background: var(--color-primary-surface);
  transition: border-color var(--anim-fast), background var(--anim-fast), box-shadow var(--anim-fast);
}
.rc-plate.claim { border-color: var(--color-ice-inset); background: var(--color-ice-surface); }
.rc-plate.claim:hover {
  border-color: var(--color-secondary);
  background: var(--color-ice-surface-hi);
  box-shadow: 0 0 26px rgba(125, 240, 255, .22);
}
.rc-plate.push:hover {
  border-color: var(--color-primary);
  background: var(--color-primary-surface-hi);
  box-shadow: 0 0 26px rgba(255, 63, 208, .22);
}
/* Fixed column so both plates' names and tags align down the stack. */
.rc-plate-head {
  display: flex; align-items: baseline; justify-content: space-between;
  width: 250px; flex: none; gap: 16px;
}
.rc-plate-name {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 4px;
  color: var(--color-text);
}
.rc-plate-tag {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .22em;
  padding: 2px 7px;
  border: 1px solid currentColor;
}
.rc-plate-tag.ice { color: var(--color-secondary); }
.rc-plate-tag.warn { color: var(--color-primary); }
.rc-plate-lines {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: .06em;
}
.rc-plate-lines > div { display: flex; gap: 12px; }
.rc-plate-lines .k { width: 68px; flex: none; letter-spacing: .16em; }
.rc-plate-lines .k.ice { color: var(--color-secondary); }
.rc-plate-lines .k.warnk { color: var(--color-primary); }
.rc-plate-lines .k.danger { color: var(--color-danger); }
.rc-plate-lines .v { color: var(--color-text-dim); }

/* GAME OVER, when it is reached by CLAIMING a cleared run rather than by
   dying. Only the heading colour changes: the layout is identical, so the
   player is reading a familiar screen. */
.over-screen.cleared .over-heading {
  color: var(--color-secondary);
  text-shadow: 0 0 54px rgba(125, 240, 255, .55);
}

@media (prefers-reduced-motion: reduce) {
  .rc-column > *, .score-row.fresh { animation: none; }
}

/* Every perma is fitted — the draft has nothing left to sell. Stated plainly
   rather than shown as an empty box, and the reroll locks with it. */
.draft-empty {
  padding: 34px 20px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: .28em;
  color: var(--color-inactive);
  border: 1px solid var(--color-drained-inset);
  background: var(--color-drained-hatch);
}
