/* --- vendored display faces (SIL OFL 1.1 — see assets/fonts/LICENSES.md) ---
   No CDN: the repo vendors three.js for the same reason, and an installed
   PWA must not reach the network for its typeface. DotGothic16 is a text=
   SUBSET (latin + all kana + the kanji this app uses) — a new kanji falls
   back to a system face until the subset is rebuilt. */
@font-face {
  font-family: 'VT323';
  src: url('assets/fonts/VT323.woff2?v=6a984b93') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Press Start 2P';
  src: url('assets/fonts/PressStart2P.woff2?v=6a984b93') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Share Tech Mono';
  src: url('assets/fonts/ShareTechMono.woff2?v=6a984b93') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'DotGothic16';
  src: url('assets/fonts/DotGothic16.woff2?v=6a984b93') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}

/* The five knobs a font pack sets (src/fonts.js writes them onto <html>).
   These defaults are the `crt` pack — the shipped default — so the
   stylesheet stands up alone if the module never runs. */
:root {
  --font-shout: "VT323", "DotGothic16", ui-monospace, Menlo, monospace;
  --font-ui: "VT323", "DotGothic16", ui-monospace, Menlo, monospace;
  /* SIZE IS RELATIVE. Every message size below is a ratio of these two
     units, never a pixel count — a shout sized in px is either shouting on
     a desktop or whispering on a phone. vmin is the intent; the clamp on
     each end is the promise. src/fonts.js writes them, and the type tuner
     in the TD panel moves them live. */
  /* SHOUT tier — the callouts and the ram ladder. Arcade, everything on. */
  --font-shout: "Press Start 2P", "DotGothic16", ui-monospace, Menlo, monospace;
  --shout-unit: clamp(20px, 1.98vmin, 34px);
  --shout-scale: 0.72;
  --shout-halo: -1.8px 0 0 #000, 1.8px 0 0 #000, 0 -1.8px 0 #000, 0 1.8px 0 #000,
    0 2.10px 6px rgba(0, 0, 0, 0.22), -4px 0 0 rgba(255, 70, 70, 0.45),
    4px 0 0 rgba(70, 150, 255, 0.45), 0 0 12px currentColor;
  /* BANNER tier — the wave card, toasts, the defeat card. The same face as
     the readout, and only the cast shadow: text you have to READ does not
     want a misconverged beam or a hard outline through it. */
  --font-banner: "VT323", "DotGothic16", ui-monospace, Menlo, monospace;
  --banner-unit: clamp(20px, 3.52vmin, 34px);
  --banner-scale: 1.28;
  --banner-halo: 0 2.10px 6px rgba(0, 0, 0, 0.22);
  --ui-unit: clamp(13px, 0.80vmin, 19px);
  --shout-track: 0.06em;
  --ui-adjust: 0.53;
}

/* --- DESKTOP HAS A KEYBOARD ---------------------------------------------
   The thumb pads — the two steer zones and the two fire buttons — are dead
   weight on a laptop, where W/A/S/D, Space and Shift already do the job
   (operator, 2026-08-31). Hidden behind THREE clauses on purpose: a fine
   pointer AND real hover AND a wide viewport. Any one alone gets it wrong —
   a touchscreen laptop reports a fine pointer for its mouse, and a width
   test alone would strip the pads off a tablet that has nothing else.

   The THROTTLE stays. It is a drag control that works fine with a mouse,
   and on desktop it is the only visible thing that says the tank drives. */
@media (hover: hover) and (pointer: fine) and (min-width: 900px) {
  #td-pad-left, #td-pad-right, #td-pad-fire, #td-pad-laser { display: none; }
}

/* the victory lap's way back — a single chip, bottom centre, out of the way
   of a board you are there to look at */
#td-lap {
  position: absolute;
  bottom: 18px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 14;
  font-family: var(--font-ui);
  font-size: calc(var(--ui-unit) * 0.92);
  font-size-adjust: var(--ui-adjust, none);
  letter-spacing: 0.1em;
  color: var(--p-green-hot);
  background: rgba(4, 14, 9, 0.86);
  border: 1px solid var(--p-green-dim);
  border-radius: 999px;
  padding: 8px 18px;
  cursor: pointer;
}
#td-lap:hover { color: var(--p-white); border-color: var(--p-green); }
#td-lap.hidden { display: none; }

/* the record page's count line: two facts on one row, not run together */
.rec-count { display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px; }
.rec-count b { font-size: 1.4em; color: var(--p-green-hot); }
.rec-sub { color: #5f7180; font-size: 0.92em; }

/* the run's new achievements, inline on a debrief card */
.rec-inline { margin: 10px 0 4px; }

/* --- ISAO SPEAKING -------------------------------------------------------
   Bottom-left, out of the thumb zone and out of the HUD's corner. It does
   not pause the game, so it must be readable at a glance and never in the
   way of the thing being talked about. */
#td-brief {
  position: absolute;
  left: max(12px, env(safe-area-inset-left));
  /* ABOVE THE RADAR, never over it (2026-09-13: the landing-site triangles were hidden under a comms card): the disc is
     min(240px, 32% of the short side) at 14px from the corner, the same rule td-tab sizes it by */
  bottom: calc(max(14px, env(safe-area-inset-bottom)) + min(240px, 32vmin) + 10px);
  z-index: 15;
  display: flex;
  gap: 12px;
  align-items: stretch;
  width: min(280px, calc(100vw - 24px));   /* a comms card, not a banner (operator: much shorter) */
  padding: 8px 10px;
  background: rgba(4, 14, 9, 0.9);
  border: 1px solid var(--p-green-dim);
  border-left: 3px solid var(--p-green);
  border-radius: 10px;
  cursor: pointer;
  animation: wave-pop 0.25s ease-out;
}
#td-brief.hidden { display: none; }
@media (max-width: 700px) { #td-brief { bottom: calc(max(14px, env(safe-area-inset-bottom)) + min(138px, 23vmin) + 10px); } }
#td-brief-face {
  width: 72px; height: 72px;
  flex: 0 0 auto;
  border: 1px solid rgba(47, 187, 96, 0.4);
  border-radius: 4px;
  image-rendering: pixelated;
}
#td-brief-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1; }
#td-brief-title {
  font-family: var(--font-banner);
  font-size: calc(var(--banner-unit) * 0.44);
  letter-spacing: 0.2em;
  color: var(--p-green);
}
#td-brief-line {
  margin: 0;
  font-family: var(--font-ui);
  font-size: calc(var(--ui-unit) * 0.95);
  font-size-adjust: var(--ui-adjust, none);
  line-height: 1.45;
  color: var(--p-green-hot);
}
#td-brief-foot {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: auto;
}
#td-brief-dots { font-size: 8px; letter-spacing: 3px; color: var(--p-green-dim); }
/* The line's remaining time, draining left. It is the affordance that says the
   panel will leave on its own — without it a player who learned to tap keeps
   tapping and the auto-advance buys nothing. */
#td-brief-track {
  height: 2px;
  margin-top: 5px;
  background: rgba(47, 187, 96, 0.18);
  border-radius: 1px;
  overflow: hidden;
}
#td-brief-bar {
  height: 100%;
  width: 100%;
  background: var(--p-green);
  opacity: 0.75;
}
#td-brief-next {
  font: bold 14px ui-monospace, Menlo, monospace;
  color: var(--p-white);
  background: rgba(51, 255, 119, 0.14);
  border: 1px solid var(--p-green-dim);
  border-radius: 4px;
  padding: 1px 12px;
  cursor: pointer;
}
@media (pointer: coarse), (max-width: 700px) {
  #td-brief { bottom: 150px; width: calc(100vw - 24px); padding: 8px 10px; gap: 9px; }
  #td-brief-face { width: 54px; height: 54px; }
}

/* --- THE RECORD (achievements) ------------------------------------------
   A list read between runs, so it is set for reading rather than for
   glancing: one row per line, the mark and the name doing the scanning and
   the note only present on what has actually been earned. */
.rec { text-align: left; }
.rec-group {
  font-family: var(--font-banner);
  font-size: calc(var(--banner-unit) * 0.44);
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--p-green-dim);
  margin: 14px 0 6px;
  border-bottom: 1px solid rgba(47, 187, 96, 0.22);
  padding-bottom: 4px;
}
.rec-group i { font-style: normal; float: right; color: #4a5a68; }
.rec-row {
  display: grid;
  grid-template-columns: 18px minmax(0, 11em) 1fr;
  gap: 8px;
  align-items: baseline;
  padding: 3px 0;
  font-family: var(--font-ui);
  font-size: calc(var(--ui-unit) * 0.86);
  font-size-adjust: var(--ui-adjust, none);
  color: #4a5a68;
}
.rec-row.got { color: var(--p-green-hot); }
.rec-mark { color: #33465a; }
.rec-row.got .rec-mark { color: var(--p-white); }
.rec-name { letter-spacing: 0.08em; }
.rec-row.got .rec-name { color: var(--p-white); }
.rec-note { color: #5f7180; }
.rec-row.got .rec-note { color: var(--p-green-dim); }
@media (max-width: 560px) {
  .rec-row { grid-template-columns: 16px 1fr; }
  .rec-note { grid-column: 2; }
}

/* --- THE TYPE BENCH (units tab) -----------------------------------------
   Every message the game shouts, on screen at once, in the real markup and
   the real classes, while the sliders move. It exists because the message
   layer cannot be judged in play: a streak shout lives 1.2 seconds and the
   defeat card only appears when you lose (operator, 2026-08-31).

   The stage paints its own dark ground rather than inheriting the viewer's,
   so what you are judging is contrast against the GAME's backdrop, not
   against whatever the turntable happens to be showing. */
#units-fonts {
  /* fixed, for the reason #units-tuner documents below: its parent
     #units-chrome is a bottom-anchored absolute strip, so an absolute panel
     hangs off the CONTROLS instead of the stage and a percentage max-height
     resolves against a few dozen pixels. Left side, opposite the sliders,
     so the specimens and the knobs are both in view at once — which is the
     entire reason this bench exists. */
  position: fixed;
  top: 58px;
  left: 14px;
  width: min(430px, calc(100vw - 28px));
  max-height: calc(100vh - 170px);
  display: flex;
  flex-direction: column;
  z-index: 24;
  background: rgba(6, 12, 9, 0.94);
  border: 1px solid #2fbb60;
  border-radius: 10px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
  pointer-events: auto;
}
#units-fonts.fonts-hidden { display: none; }

#units-fonts header,
#units-fonts footer {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  font: 11px ui-monospace, Menlo, monospace;
  color: #9dffbe;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  flex: 0 0 auto;
}
#units-fonts header { border-bottom: 1px solid rgba(47, 187, 96, 0.35); }
#units-fonts footer { border-top: 1px solid rgba(47, 187, 96, 0.35); }
#units-fonts .fb-title { font-weight: bold; }
#units-fonts .fb-pack { margin-left: auto; display: flex; align-items: center; gap: 6px; }
#units-fonts select {
  font: 11px ui-monospace, Menlo, monospace;
  color: #d6ffdf;
  background: #0a1a10;
  border: 1px solid rgba(47, 187, 96, 0.5);
  border-radius: 3px;
  padding: 3px 6px;
}
#units-fonts header button,
#units-fonts footer button {
  font: 11px ui-monospace, Menlo, monospace;
  letter-spacing: 0.08em;
  color: #9dffbe;
  background: transparent;
  border: 1px solid rgba(47, 187, 96, 0.5);
  border-radius: 3px;
  padding: 4px 9px;
  cursor: pointer;
}
#units-fonts header button:hover,
#units-fonts footer button:hover { color: #eafff2; border-color: #33ff77; }
#units-fonts header button { margin-left: 0; padding: 2px 8px; font-size: 14px; }

#units-fonts-stage {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 14px 16px 18px;
  /* the game's own ground, so contrast is judged against the real thing */
  background:
    repeating-linear-gradient(to bottom,
      rgba(255, 255, 255, 0.012) 0 1px, transparent 1px 3px),
    radial-gradient(120% 130% at 40% 0%, #0a1712 0%, #04080a 70%);
}
.fb-block {
  padding: 10px 0 14px;
  border-bottom: 1px dashed rgba(47, 187, 96, 0.22);
  /* the shout tier can be WIDER than the panel — an 8x8 bitmap face at
     shout size is a long word. Scroll rather than clip: a cropped specimen
     hides exactly the problem you would want the bench to show you. */
  overflow-x: auto;
}
.fb-block:last-child { border-bottom: 0; }
.fb-cap {
  margin: 0 0 8px;
  font: 9px ui-monospace, Menlo, monospace;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #4fae63;
}
/* the callouts run their entrance animation on a loop in here, because the
   thing being judged is partly how they READ while moving */
#units-fonts .callout { animation: co-pop 2.6s ease-out infinite; }
.fb-ladder { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px; }
.fb-wavebox {
  display: inline-block;
  padding: 8px 16px 10px;
  text-align: center;
  color: #9ff8ff;
  background: linear-gradient(180deg, rgba(14, 8, 8, 0.9), rgba(8, 4, 4, 0.9));
  border: 1px solid #9ff8ff;
  border-radius: 10px;
}
.fb-toast {
  display: inline-block;
  padding: 8px 16px;
  text-align: center;
  font-family: var(--font-ui);
  font-size: calc(var(--ui-unit) * 1.0);
  font-size-adjust: var(--ui-adjust, none);
  line-height: 1.5;
  color: var(--p-green-hot);
  background: rgba(4, 14, 9, 0.84);
  border: 1px solid var(--p-green-dim);
  border-radius: 12px;
  text-shadow: 0 0 5px rgba(157, 255, 190, 0.35);
}
.fb-hud {
  font-family: var(--font-ui);
  font-size: calc(var(--ui-unit) * 0.92);
  font-size-adjust: var(--ui-adjust, none);
  line-height: 1.7;
  color: #4fae63;
}

@media (max-width: 560px) {
  /* on a phone the two panels cannot share the screen, so the bench takes
     it and the sliders scroll under: the specimens are the point */
  #units-fonts { left: 10px; right: 10px; width: auto; top: 48px;
    max-height: calc(50vh); }
}
@media (prefers-reduced-motion: reduce) {
  #units-fonts .callout { animation: none; opacity: 1; }
}

/* --- CRT PHOSPHOR PALETTE ------------------------------------------------
   Operator's call (2026-08-31): the message layer sticks to what a cathode
   ray tube can actually make — P1 green, white, and the blue. Which means
   escalation has to be carried by BRIGHTNESS rather than by hue, because
   that is how a phosphor behaves: drive it harder and it whitens. So the
   ram ladder runs dim green -> green -> hot green -> white -> blue-white,
   and never reaches for the red no green screen ever had.

   The channel split that falls out of it is worth keeping: GREEN is you and
   your board, WHITE is the loudest thing on screen right now, BLUE is a
   subsystem that is not you (Bobby) or a state that is over (the death
   card). Three phosphors, three meanings. */
:root {
  --p-green: #33ff77;
  --p-green-dim: #2fbb60;
  --p-green-hot: #9dffbe;
  --p-white: #eafff2;
  --p-blue: #66b8ff;
  --p-blue-hot: #cfe6ff;
}

/* kill iOS double-tap zoom EVERYWHERE: touch-action does NOT inherit, so
   a html/body rule never reaches the game canvases — the universal
   selector does. (OrbitControls sets inline touch-action:none on its own
   canvas, which still wins, as it should.) double-tap is a GAME gesture
   (cruise); a surprise zoom mid-battle is never wanted. */
* {
  touch-action: manipulation;
}

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: #10131a;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  overscroll-behavior: none;
  -webkit-tap-highlight-color: transparent;
  /* no long-press callout / accidental text selection on the play surface */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* reading surfaces get selection back */
.mdview, .mdview * {
  -webkit-user-select: text;
  user-select: text;
}

.tab {
  position: fixed;
  inset: 0;
}

.tab-hidden {
  display: none;
}

#app, #maze-app, #o-app, #b-app, #h-app, #td-app, #tank-app, #tank2-app, #tank3-app {
  position: absolute;
  inset: 0;
}

#app canvas, #maze-app canvas, #o-app canvas, #b-app canvas, #h-app canvas, #td-app canvas, #tank-app canvas, #tank2-app canvas, #tank3-app canvas {
  display: block;
}

#stats, #maze-stats, #o-stats, #b-stats, #h-stats, #td-stats {
  position: absolute;
  top: 46px;
  left: 14px;
  color: #9aa7bd;
  /* the instrument panel takes the pack's UI face. font-size-adjust keeps
     a narrow face (VT323) from quietly shrinking the whole readout — the
     rows are sized for three reading distances and that spacing is load
     bearing. */
  font-family: var(--font-ui);
  font-size: calc(var(--ui-unit) * 0.92);
  font-size-adjust: var(--ui-adjust, none);
  line-height: 1.7;
  white-space: pre;
  pointer-events: none;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
  z-index: 5;
}

#hint, #maze-hint, #o-hint, #b-hint, #h-hint, #td-hint {
  position: absolute;
  bottom: 12px;
  right: 14px;
  color: #55607a;
  font-size: 11px;
  pointer-events: none;
  z-index: 5;
}

/* --- maze HUD --- */
#dpad, .dpad {
  position: absolute;
  bottom: max(26px, env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

.dpad .dpad-row {
  display: flex;
  gap: 4px;
}

.dpad button {
  width: 46px;
  height: 40px;
  background: rgba(35, 43, 64, 0.92);
  color: #cdd7f0;
  border: 1px solid #4a5a8a;
  border-radius: 8px;
  font-size: 16px;
  cursor: pointer;
  touch-action: manipulation;
}

.dpad button:active {
  background: #2d3a5e;
  color: #fff;
}

/* --- heart tab: maneuver zones + fire buttons ---------------------------
   Steering/driving are large FLAT areas — visibly interactive (subtle
   panel, faint chevron, press-glow) but unlabeled: the player discovers
   the maneuvering. Fire controls are explicit buttons. */
.tzone {
  position: absolute;
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(28, 38, 58, 0.28);
  border: 1px solid rgba(120, 150, 210, 0.22);
  border-radius: 18px;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
  cursor: pointer;
}
.tzone > span {
  font-size: 26px;
  color: rgba(213, 226, 245, 0.4);
  pointer-events: none;
}
.tzone.pressed {
  background: rgba(90, 140, 230, 0.35);
  border-color: rgba(140, 180, 250, 0.55);
}
.tzone.pressed > span { color: rgba(235, 244, 255, 0.9); }
/* side zones: tall flat strips hugging the lower edges */
.tzone-side {
  bottom: max(12px, env(safe-area-inset-bottom));
  width: 82px;
  height: min(26vh, 230px);
}
/* clears the minimap, which owns the bottom-left corner; the offset is the
   map's own sizing expression so the two travel together */
.tzone-l { left: calc(max(10px, env(safe-area-inset-left)) + min(240px, 32vmin) + 18px); }
.tzone-r { right: max(10px, env(safe-area-inset-right)); }
/* drive zones: forward over back, centered between the sides */
.tzone-mid {
  position: absolute;
  bottom: max(12px, env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  z-index: 6;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.tzone-drive {
  position: relative;
  width: 128px;
  height: 60px;
}
/* fire buttons: primary (shell) LARGE on the left, secondary smaller right */
.tfire {
  position: absolute;
  z-index: 6;
  border-radius: 50%;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}
/* RIGHT-hand side, both of them: the left edge belongs to the throttle and
   the left steering strip, and three controls in one corner is how the lever
   ended up sitting on top of the fire button. Left side moves, right side
   shoots. */
.tfire-primary {
  right: max(16px, env(safe-area-inset-right));
  bottom: calc(max(12px, env(safe-area-inset-bottom)) + min(26vh, 230px) + 18px);
  width: 78px;
  height: 78px;
  font-size: 30px;
  background: rgba(24, 52, 48, 0.5);
  color: #aefce8;
  border: 1px solid rgba(53, 255, 208, 0.5);
}
.tfire-secondary {
  right: calc(max(16px, env(safe-area-inset-right)) + 92px);
  bottom: calc(max(12px, env(safe-area-inset-bottom)) + min(26vh, 230px) + 18px);
  width: 54px;
  height: 54px;
  font-size: 21px;
  background: rgba(22, 46, 58, 0.5);
  color: #9ff8ff;
  border: 1px solid rgba(125, 249, 255, 0.45);
}
/* the third pad. A TAP, not a hold — laying is a discrete act — so it sits
   in the same column as the laser but one step up the thumb's arc, out of
   the reach it uses for the two it holds down. */
.tfire-tertiary {
  right: calc(max(16px, env(safe-area-inset-right)) + 92px);
  bottom: calc(max(12px, env(safe-area-inset-bottom)) + min(26vh, 230px) + 80px);
  width: 50px;
  height: 50px;
  font-size: 20px;
  background: rgba(46, 30, 26, 0.5);
  color: #ffc9a8;
  border: 1px solid rgba(255, 138, 92, 0.5);
}
.tfire:active, .tfire.pressed {
  background: rgba(90, 200, 180, 0.5);
  color: #fff;
}
/* utility pair: small text buttons, centered above the drive zones */
.tc-util {
  position: absolute;
  /* Top of the screen: these are mode switches, not thumb controls, and at
     the bottom they competed with the throttle and the fire buttons for the
     same reach. Clear of the tab bar, which is the only other thing up here
     when the chrome is showing. */
  top: 52px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 6;
  display: flex;
  gap: 8px;
}
.tc-util button {
  padding: 8px 12px;
  font: 11px ui-monospace, Menlo, monospace;
  letter-spacing: 0.1em;
  background: rgba(30, 40, 60, 0.4);
  color: #aab8d5;
  border: 1px solid rgba(120, 150, 210, 0.3);
  border-radius: 10px;
  cursor: pointer;
  touch-action: manipulation;
  opacity: 0.85;
}
.tc-util button:active { background: rgba(90, 140, 230, 0.5); color: #fff; }

/* --- TD shop: RADIAL menu around the tapped cell (HokorobiTawaa) --------- */
#td-shop {
  position: absolute;
  z-index: 9;
  width: 0;
  height: 0;
}
#td-shop.hidden { display: none; }
#td-shop .radial-center {
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(-50%, -50%);
  width: 62px;
  height: 62px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font: 11px ui-monospace, Menlo, monospace;
  letter-spacing: 0.06em;
  color: #7fe8d0;
  background: rgba(8, 18, 22, 0.9);
  border: 1px solid rgba(53, 255, 208, 0.4);
  pointer-events: none;
  line-height: 1.35;
}
#td-shop .radial-item {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 60px;
  height: 60px;
  border-radius: 50%;
  font: 10px ui-monospace, Menlo, monospace;
  line-height: 1.3;
  text-align: center;
  background: rgba(22, 34, 48, 0.88);
  color: #cfe2ee;
  border: 1px solid rgba(120, 150, 210, 0.45);
  cursor: pointer;
  touch-action: manipulation;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
#td-shop .radial-item:disabled { opacity: 0.32; cursor: default; }
#td-shop .radial-item:not(:disabled):hover,
#td-shop .radial-item:not(:disabled):active { background: rgba(60, 100, 130, 0.85); color: #fff; }
#td-shop .radial-item.locked {
  opacity: 0.4;
  filter: grayscale(0.6);
  border-color: rgba(120, 150, 210, 0.3) !important;
  cursor: not-allowed;
}
#td-shop .shop-sell { border-color: rgba(255, 170, 90, 0.55); color: #ffd9a8; }
#td-shop .shop-close { width: 44px; height: 44px; font-size: 15px; }
#td-shop .shop-note {
  position: absolute;
  left: 0;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 11px;
  color: #ff8f80;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
}

/* BOBBY's row takes the BLUE phosphor. He is a subsystem, not you — and on
   a screen where green is your board, a second colour is what says the
   sentence is about somebody else. */
.hud-bobby {
  color: var(--p-blue);
  letter-spacing: 0.04em;
}

/* the biomass line is the LOUD line — hot green, the way a terminal shows
   the number you are spending. White is kept back for the alert row. */
.hud-biomass {
  color: var(--p-green-hot);
  font-size: 15px;
  font-weight: bold;
  text-shadow: 0 0 7px rgba(157, 255, 190, 0.5);
}

/* GAMEPLAY tips block — lives in the briefing and pause modals only */
.tips-head {
  margin-top: 10px;
  font-size: 10px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  opacity: 0.8;
}
.tips {
  margin: 4px 0 10px;
  font-size: 12px;
  line-height: 1.7;
  color: #c7d4dc;
}

/* sector-breach flash: the world opens in a blink of white (HK reveal) */
#td-flash {
  position: fixed;
  inset: 0;
  background: #fff;
  opacity: 0;
  pointer-events: none;
  z-index: 30;
}
#td-flash.on { animation: sector-flash 1.1s ease-out; }
@keyframes sector-flash {
  0% { opacity: 0; }
  10% { opacity: 1; }
  100% { opacity: 0; }
}

/* build mode is the planning instrument: driving controls step aside */
/* ONE MODE: orbit is a camera, not an instrument swap. Nothing hides —
   steering, throttle, fire and the view cycle all stay, because every
   capability works under every camera now. (This rule once hid the fight
   controls in "build mode"; the mode is gone.) */
#td-pad-build.holding {
  color: #061018;
  background: #ffd75e;
  border-color: #ffd75e;
}

/* touch devices: 44pt+ hit targets (Apple HIG), roomier gaps */
/* Thumb-sized controls: a coarse pointer OR a phone-shaped viewport. The
   width clause is not cosmetic — a touch-only query cannot be exercised
   by any desktop browser, so every rule in here was unverifiable and one
   round of mobile layout fixes silently did nothing. */
@media (pointer: coarse), (max-width: 560px) {
  /* COMPACT + FAINT. The controls were eating the board: the side strips ran
     26vh, the fire buttons were 78px, and every surface sat at 0.28-0.5
     alpha. They are the frame, not the picture — small enough to leave the
     sphere room, faint enough to read as glass, and they brighten on touch
     (.pressed / :active) which is when feedback actually matters. */
  .tzone-side { width: 62px; height: min(19vh, 168px); }
  .tzone-drive { width: 104px; height: 46px; }
  .tzone { background: rgba(28, 38, 58, 0.13); border-color: rgba(120, 150, 210, 0.13); }
  .tzone > span { font-size: 20px; color: rgba(213, 226, 245, 0.26); }
  .dpad button {
    width: 44px;
    height: 38px;
    font-size: 16px;
  }
  .dpad {
    gap: 5px;
  }
  .dpad .dpad-row {
    gap: 5px;
  }
  /* --- phone thumb layout ------------------------------------------------
     LEFT THUMB MOVES, RIGHT THUMB SHOOTS. Three controls had piled into the
     bottom-left corner — the primary fire button, the left steering strip,
     and the throttle — so the lever sat on top of the control it was meant
     to sit beside. Both fire buttons move right, which is where the hand
     that is not driving already is.

       left edge   throttle
       next to it  < steer
       right edge  > steer, with both fire buttons above it            */
  .tfire-primary {
    width: 58px; height: 58px; font-size: 23px;
    bottom: calc(max(12px, env(safe-area-inset-bottom)) + min(19vh, 168px) + 12px);
    background: rgba(24, 52, 48, 0.28);
    border-color: rgba(53, 255, 208, 0.34);
  }
  .tfire-secondary {
    width: 44px; height: 44px; font-size: 16px;   /* 42 was under the 44 px thumb rule (--phone, landscape) */
    right: calc(max(14px, env(safe-area-inset-right)) + 68px);
    bottom: calc(max(12px, env(safe-area-inset-bottom)) + min(19vh, 168px) + 20px);
    background: rgba(22, 46, 58, 0.28);
    border-color: rgba(125, 249, 255, 0.3);
  }
  .tfire-tertiary {
    width: 44px; height: 44px; font-size: 16px;   /* 42 was under the 44 px thumb rule (--phone, landscape) */
    right: calc(max(14px, env(safe-area-inset-right)) + 68px);
    bottom: calc(max(12px, env(safe-area-inset-bottom)) + min(19vh, 168px) + 76px);
    background: rgba(46, 30, 26, 0.28);
    border-color: rgba(255, 138, 92, 0.34);
  }
  /* the left strip steps aside for the lever rather than sitting under it */
  /* STEERING KEEPS THE EDGES, MIRRORED. Moving the left strip inboard to
     make room put the lever where the thumb expects to steer, which is worse
     than the overlap it solved — a control you reach for by feel must not
     move. Left and right sit at their own edges as a pair; the lever stacks
     ABOVE the left one, the fire buttons above the right. Movement left,
     shooting right, same shape both sides. */
  .tzone-l { left: max(8px, env(safe-area-inset-left)); }
  .tzone-r { right: max(8px, env(safe-area-inset-right)); }
  .throttle {
    left: max(8px, env(safe-area-inset-left));
    /* clears the strip below it, using the strip's OWN height expression so
       the two cannot drift apart */
    bottom: calc(max(12px, env(safe-area-inset-bottom)) + min(19vh, 168px) + 10px);
  }

  /* --- phone top bands ---------------------------------------------------
     The top was three things fighting for one line: the mode row centred,
     the HUD block left, the minimap right. They now STACK — modes on the
     first line, HUD and map sharing the second — so nothing is written over
     anything else on a 390px screen. */
  .tc-util {
    top: max(8px, env(safe-area-inset-top));
    gap: 5px;
  }
  .tc-util button {
    padding: 5px 9px;
    font-size: 10px;
    background: rgba(30, 40, 60, 0.24);
    border-color: rgba(120, 150, 210, 0.18);
    opacity: 0.62;
  }
  .tc-util button:active { opacity: 1; }
  #hint, #maze-hint, #o-hint, #b-hint, #h-hint, #td-hint {
    display: none; /* collides with the cb badge on narrow screens; hints are in button titles */
  }
}

/* narrow screens: keep the centered column but shift right of the minimap */
@media (max-width: 520px) {
  .dpad {
    left: 58%;
  }
}

#maze-msg, #o-msg, #b-msg, #h-msg, #td-msg {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 8;
  /* fixed head + scrolling body + always-visible footer: the modal keeps
     overflow:hidden (below) to clip the hologram sweep, so the briefing's
     inner .msg-scroll does the scrolling and .msg-foot (the Play button)
     is never clipped on short phones */
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 300px;
  max-width: 88vw;
  /* tall content (briefing/glossaries) must fit ANY phone and scroll —
     100dvh tracks iOS's real viewport, unlike 100vh */
  max-height: min(86vh, calc(100dvh - 28px));
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 14px 26px 18px;
  color: #aefce8;
  background: linear-gradient(180deg, rgba(4, 22, 24, 0.92), rgba(2, 11, 13, 0.92));
  border: 1px solid #35ffd0;
  border-radius: 12px;
  box-shadow: 0 0 26px rgba(53, 255, 208, 0.35), inset 0 0 34px rgba(53, 255, 208, 0.08);
  overflow: hidden;
  font-size: 15px;
  text-align: center;
  line-height: 1.8;
  text-shadow: 0 0 8px rgba(53, 255, 208, 0.45);
  animation: holo-flicker 3.5s infinite;
  pointer-events: auto;
}

/* CRT scanlines */
#maze-msg::after, #o-msg::after, #b-msg::after, #h-msg::after, #td-msg::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(0deg,
    rgba(0, 0, 0, 0) 0px, rgba(0, 0, 0, 0) 2px,
    rgba(0, 14, 12, 0.45) 3px, rgba(0, 0, 0, 0) 4px);
  background-size: 100% 4px;
  animation: holo-roll 0.5s steps(4) infinite;
}

/* hologram sweep */
#maze-msg::before, #o-msg::before, #b-msg::before, #h-msg::before, #td-msg::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0; height: 45%;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(53, 255, 208, 0.12), rgba(53, 255, 208, 0));
  transform: translateY(-120%);
  animation: holo-sweep 2.8s ease-in-out infinite;
}

@keyframes holo-flicker {
  0%, 88%, 100% { opacity: 1; }
  90% { opacity: 0.82; }
  92% { opacity: 1; }
  95% { opacity: 0.9; }
  96% { opacity: 1; }
}
@keyframes holo-roll { from { background-position: 0 0; } to { background-position: 0 4px; } }
@keyframes holo-sweep { 0% { transform: translateY(-120%); } 55%, 100% { transform: translateY(320%); } }

.msg-head {
  font-family: var(--font-banner);
  font-size: calc(var(--banner-unit) * 0.48);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  opacity: 0.85;
  margin-bottom: 8px;
}

.msg-regen {
  display: inline-block;
  margin-top: 12px;
  padding: 7px 18px;
  background: rgba(53, 255, 208, 0.08);
  color: #aefce8;
  border: 1px solid #35ffd0;
  border-radius: 8px;
  font: 13px ui-monospace, Menlo, monospace;
  letter-spacing: 0.08em;
  cursor: pointer;
  touch-action: manipulation;
  position: relative;
  z-index: 1;
}
.msg-regen:hover { background: rgba(53, 255, 208, 0.22); color: #fff; }

#maze-msg.hidden, #o-msg.hidden, #b-msg.hidden, #h-msg.hidden, #td-msg.hidden {
  display: none;
}

/* --- wave announcement banner (heart tab): the "New Threat" card ------- */
/* border/text color are set inline per enemy tint by announceWave() */
#h-wave, #td-wave {
  position: absolute;
  top: 27%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 7;
  min-width: 260px;
  max-width: 82vw;
  padding: 10px 22px 12px;
  background: linear-gradient(180deg, rgba(14, 8, 8, 0.9), rgba(8, 4, 4, 0.9));
  border: 1px solid;
  border-radius: 10px;
  text-align: center;
  font-family: var(--font-ui);
  font-size: calc(var(--ui-unit) * 1.0);
  font-size-adjust: var(--ui-adjust, none);
  pointer-events: none;
  animation: wave-pop 0.35s ease-out;
}
#h-wave.hidden, #td-wave.hidden { display: none; }
#h-wave .wave-num, #td-wave .wave-num, .wavebox .wave-num {
  font-family: var(--font-banner);
  --r: 0.48;
  font-size: calc(var(--banner-unit) * var(--r));
  letter-spacing: 0.32em;
  text-transform: uppercase;
  opacity: 0.8;
}
#h-wave .wave-name, #td-wave .wave-name, .wavebox .wave-name {
  font-family: var(--font-banner);
  --r: 0.90;
  font-size: calc(var(--banner-unit) * var(--r));
  letter-spacing: var(--shout-track);
  margin: 4px 0 2px;
  text-shadow: var(--banner-halo);
}
#h-wave .wave-role, #td-wave .wave-role, .wavebox .wave-role {
  font-size: 12px;
  opacity: 0.85;
  color: #d8e2ea;
}
#h-wave .wave-sprite, #td-wave .wave-sprite {
  display: block;
  margin: 4px auto 2px;
  width: 96px;
  height: 96px;
}
#h-wave .wave-ram, #td-wave .wave-ram, .wavebox .wave-ram {
  font-family: var(--font-banner);
  --r: 0.52;
  font-size: calc(var(--banner-unit) * var(--r));
  letter-spacing: 0.18em;
  margin-top: 5px;
  text-shadow: var(--banner-halo);
}

/* --- tower-unlock toast (#td-tower) — own element so it never clobbers   */
/* the enemy "NEW THREAT" waveEl card on the same wave. Offset below it.   */
#td-tower {
  position: absolute;
  top: calc(16% + 200px); /* below #td-wave (top:16%) with room for its content */
  left: 50%;
  transform: translateX(-50%);
  z-index: 7;
  min-width: 220px;
  max-width: 72vw;
  padding: 8px 18px 10px;
  background: linear-gradient(180deg, rgba(4, 14, 22, 0.92), rgba(4, 10, 18, 0.92));
  border: 1px solid #7fdfff;
  border-radius: 10px;
  text-align: center;
  font-family: var(--font-ui);
  font-size: calc(var(--ui-unit) * 1.0);
  font-size-adjust: var(--ui-adjust, none);
  color: #7fdfff;
  pointer-events: none;
  animation: wave-pop 0.35s ease-out;
}
#td-tower.hidden { display: none; }
#td-tower .wave-num {
  font-family: var(--font-banner);
  --r: 0.48;
  font-size: calc(var(--banner-unit) * var(--r));
  letter-spacing: 0.32em;
  text-transform: uppercase;
  opacity: 0.8;
}
#td-tower .wave-name {
  font-family: var(--font-banner);
  --r: 0.81;
  font-size: calc(var(--banner-unit) * var(--r));
  letter-spacing: var(--shout-track);
  margin: 4px 0 2px;
  text-shadow: var(--banner-halo);
}
#td-tower .wave-role {
  font-size: calc(var(--ui-unit) * 0.92);
  opacity: 0.85;
}

/* --- briefing & glossaries (heart tab): one card per element ------------ */
.gcards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(235px, 1fr));
  gap: 6px;
  width: min(620px, 80vw);
  margin: 10px auto 12px;
  text-align: left;
}
.gcard {
  display: grid;
  grid-template-columns: 48px 1fr;
  grid-template-rows: auto auto;
  column-gap: 9px;
  padding: 6px 10px;
  background: rgba(18, 38, 42, 0.6);
  border: 1px solid rgba(53, 255, 208, 0.22);
  border-radius: 8px;
}
/* the icon IS the thing: a snapshot of the real half-dotted object */
.gcard .gicon {
  grid-row: 1 / span 2;
  align-self: center;
  width: 46px;
  height: 46px;
}
.gcard .gname {
  font-size: 12px;
  letter-spacing: 0.08em;
  text-shadow: 0 0 6px currentColor;
}
.gcard .gdesc {
  font-size: 11.5px;
  color: #c7d4dc;
  line-height: 1.45;
}
/* briefing body scrolls; footer (glossary + Play buttons) stays pinned so
   the Play button is always reachable even when the card list overflows */
.msg-scroll {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
.msg-foot {
  flex: 0 0 auto;
  width: 100%;
  padding-top: 6px;
}

/* (the glossary/record/lap buttons share the Play button's look — their
   selector list had been cut off from this rule by the .msg-scroll block
   inserted between them, and they rendered as full-width bars) */
.msg-glenemy, .msg-glfriend, .msg-back, .msg-glachv, .msg-lap,
.msg-next, .msg-begin {
  display: inline-block;
  margin-top: 12px;
  padding: 7px 18px;
  background: rgba(53, 255, 208, 0.08);
  color: #aefce8;
  border: 1px solid #35ffd0;
  border-radius: 8px;
  font: 13px ui-monospace, Menlo, monospace;
  letter-spacing: 0.08em;
  cursor: pointer;
  touch-action: manipulation;
  position: relative;
  z-index: 1;
}
.msg-glenemy:hover, .msg-glfriend:hover, .msg-back:hover, .msg-glachv:hover, .msg-lap:hover,
.msg-next:hover, .msg-begin:hover { background: rgba(53, 255, 208, 0.22); color: #fff; }
.msg-glenemy, .msg-glfriend { margin-top: 4px; padding: 5px 13px; font-size: 12px; }
.msg-back { margin-top: 8px; }
@keyframes wave-pop {
  from { opacity: 0; transform: translateX(-50%) scale(0.85); }
  to   { opacity: 1; transform: translateX(-50%) scale(1); }
}

/* lil-gui inside a tab wrapper, below the navigation shell's bar, which owns the top right corner */
.tab .lil-gui.root {
  position: absolute;
  top: calc(max(8px, env(safe-area-inset-top)) + 46px);
  max-height: calc(100vh - max(8px, env(safe-area-inset-top)) - 54px);   /* lil-gui scrolls its own children once capped */
  right: 0;
  z-index: 10;
}

/* --- dev log overlay (click the cache-bust badge) --- */
#devlog-overlay {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(8, 10, 16, 0.78);
  backdrop-filter: blur(3px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

#devlog-overlay.hidden {
  display: none;
}

#devlog-panel {
  position: relative;
  width: min(72ch, 100%);
  max-height: min(86vh, 100%);
  overflow-y: auto;
  background: #141824;
  border: 1px solid #2a3350;
  border-radius: 12px;
  padding: 28px 34px 36px;
  color: #c3cde2;
  font: 14px/1.65 ui-monospace, "SF Mono", Menlo, monospace;
  overscroll-behavior: contain;
}

#devlog-close {
  position: sticky;
  top: 0;
  float: right;
  margin: -12px -18px 0 0;
  width: 34px;
  height: 34px;
  background: #1b2130;
  color: #8a96b8;
  border: 1px solid #2a3350;
  border-radius: 8px;
  font-size: 18px;
  cursor: pointer;
  touch-action: manipulation;
}

#devlog-close:hover {
  color: #fff;
  background: #2d3a5e;
}

/* --- doc tabs (how it works / tech stack): full-page article --- */
/* both scrollers, both articles — the .mdview block near the end of this
   file owns every typographic decision for all three documents */
#how-scroll, #stack-scroll, #roadmap-scroll, #log-scroll, #record-scroll {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overflow-x: hidden;   /* the reading column never pans; wide tables scroll themselves */
  padding: 64px 24px 96px;
}

/* --- circular minimap (dedicated canvas; CSS clips the square to a disc) --- */
.minimap {
  position: absolute;
  left: max(14px, env(safe-area-inset-left));
  bottom: max(14px, env(safe-area-inset-bottom));
  border-radius: 50%;
  border: 1px solid #2a3350;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.45);
  z-index: 5;
}

/* --- cache badge: top-left, clear of the navigation shell's bar (its own styles are inline) --- */
#cb-badge {
  top: max(10px, env(safe-area-inset-top)) !important;
  left: max(12px, env(safe-area-inset-left)) !important;
  right: auto !important;
  bottom: auto !important;
}

/* narrow screens: HUD stats drop below the badge */
@media (max-width: 640px) {
  #stats, #maze-stats, #o-stats, #b-stats, #h-stats, #td-stats {
    top: 118px;
  }
}

/* --- mobile play mode: chrome hides, minimap moves clear of thumbs ------- */
@media (pointer: coarse), (max-width: 700px) {
  /* the badge stays hidden on a phone, and in any narrow window */
  body #cb-badge {
    display: none !important;
  }
  /* ACTIVE PLAY hides the panels and the badge */
  body.playing #cb-badge,
  body.playing .tab .lil-gui.root {
    display: none !important;
  }
  /* the hint line fights the thumb clusters for the bottom edge, and the
     briefing already teaches everything it says */
  #h-hint, #td-hint { display: none !important; }
  /* minimap top-right: bottom corners belong to thumbs. Second band, level
     with the HUD, so it clears the mode row above it. */
  .minimap {
    left: auto;
    bottom: auto;
    right: max(12px, env(safe-area-inset-right));
    top: calc(max(8px, env(safe-area-inset-top)) + 42px);
  }
  /* HUD sits in the second band, beside the map and never under it. The
     width cap is what keeps the wave line — the longest — out of the map's
     corner instead of running beneath it. */
  #stats, #maze-stats, #o-stats, #b-stats, #h-stats, #td-stats {
    top: calc(max(8px, env(safe-area-inset-top)) + 42px);
    left: max(12px, env(safe-area-inset-left));
    font-size: 11px;
    line-height: 1.45;
    /* the map is min(240px, 32vmin) wide plus its 12px inset; leave that
       and a gap, so the wave line stops rather than running underneath */
    max-width: calc(100vw - min(240px, 32vmin) - 36px);
  }
}

/* lab lil-gui panels are DEV-only furniture: hide them on a real phone, but a
   narrow desktop window with a mouse still needs to open them */
@media (pointer: coarse) {
  body .tab .lil-gui.root {
    display: none !important;
  }
}

/* tank / tank2 tabs: shared Combat-style HUD classes */
.combat-score { position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
  font: 700 42px/1 ui-monospace, monospace; letter-spacing: .5em; pointer-events: none; z-index: 5; }
.combat-score .ts-red { color: #d23b2f; }
.combat-score .ts-blue { color: #3556d2; }
.combat-msg { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font: 700 34px ui-monospace, monospace; color: #fff; background: rgba(0,0,0,.45);
  z-index: 6; cursor: pointer; text-align: center; }
.combat-msg.hidden { display: none; }
.combat-hint { position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%);
  font: 12px ui-monospace, monospace; color: #cfd6e4; opacity: .7; pointer-events: none; z-index: 5; }

/* tank3 (battle/tron skin): score digits in the Tron player/enemy tints */
.combat-score.tron .ts-red { color: #9ff8ff; }
.combat-score.tron .ts-blue { color: #ff2d6f; }

/* --- TD tutorial: non-freezing callouts + button pulse ------------------- */
#td-tut {
  position: absolute;
  /* Top of the screen, below BOTH the mode row and the four-line HUD block.
     At 16% it sat over the middle distance — exactly where the thing the
     message is telling you to look at tends to be, so reading the instruction
     meant not watching the board. */
  top: 134px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 9;
  max-width: min(84vw, 520px);
  text-align: center;
  font: 14px ui-monospace, Menlo, monospace;
  line-height: 1.5;
  color: #cfe9ff;
  background: rgba(6, 14, 22, 0.82);
  border: 1px solid #35ffd0;
  border-radius: 12px;
  padding: 12px 18px;
  text-shadow: 0 0 8px rgba(53, 255, 208, 0.4);
  pointer-events: none;
}
#td-tut.hidden { display: none; }
#td-toast {
  position: absolute;
  top: 27%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  max-width: min(80vw, 480px);
  text-align: center;
  font-family: var(--font-ui);
  font-size: calc(var(--ui-unit) * 1.0);
  font-size-adjust: var(--ui-adjust, none);
  line-height: 1.5;
  color: var(--p-green-hot);
  background: rgba(4, 14, 9, 0.84);
  border: 1px solid var(--p-green-dim);
  border-radius: 12px;
  padding: 10px 18px;
  text-shadow: 0 0 5px rgba(157, 255, 190, 0.35);
  pointer-events: none;
}
#td-toast.hidden { display: none; }
/* a toast only takes taps when it is offering one — otherwise it is a sign
   hanging over a board you need to be able to tap through */
#td-toast.actionable { pointer-events: auto; }
.toast-yes {
  display: block;
  margin: 9px auto 0;
  font-family: var(--font-ui);
  font-size: calc(var(--ui-unit) * 0.92);
  letter-spacing: 0.1em;
  color: var(--p-white);
  background: rgba(51, 255, 119, 0.14);
  border: 1px solid var(--p-green);
  border-radius: 4px;
  padding: 6px 14px;
  cursor: pointer;
}
.toast-yes:hover { background: rgba(51, 255, 119, 0.26); }

#td-tut.tut-flash {
  top: 134px;
  font: 700 30px ui-monospace, monospace;
  letter-spacing: 0.08em;
  color: #ff6a88;
  border-color: #ff6a88;
  text-shadow: 0 0 16px rgba(255, 106, 136, 0.6);
}
@keyframes tutorial-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(53, 255, 208, 0); }
  50% { box-shadow: 0 0 0 6px rgba(53, 255, 208, 0.5), 0 0 18px rgba(53, 255, 208, 0.7); }
}
.tutorial-pulse { animation: tutorial-pulse 1.1s ease-in-out infinite; }

/* build mode: the app owns the pinch (zoom the board, not the page). Driving
   thumb-zones are hidden in build mode, so nothing else needs the gesture. */
#tab-td.build #td-app { touch-action: none; }

/* ── next-wave preview strip ─────────────────────────────────────── */
#td-next {
  position: fixed;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(0, 0, 0, 0.62);
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: 6px;
  padding: 5px 10px 6px;
  font-family: monospace;
  font-size: 11px;
  color: #cde;
  pointer-events: none;
  z-index: 30;
  max-width: 96vw;
  text-align: center;
}
.nx-head {
  font-weight: bold;
  letter-spacing: 0.04em;
  margin-bottom: 3px;
}
.nx-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  justify-content: center;
}
.nx-chip {
  font-size: 10px;
  opacity: 0.9;
}
.wave-icon {
  display: block;
  width: 48px;
  height: 48px;
  margin: 4px auto;
}

/* --- units tab: one unit at a time, big, turnable ----------------------- */
/* NOTE: no position override here — .tab is already `position: fixed;
   inset: 0`, and re-declaring it as relative collapses the tab to zero
   height, which renders a perfectly black screen with no error. */
/* --- the beam lab ------------------------------------------------------- */
#beam-app { position: absolute; inset: 0; }
#beam-app canvas { display: block; width: 100%; height: 100%; }
/* the little double-square: copy the whole preset to the clipboard. Sits on
   the stage rather than in the panel so it is reachable without scrolling the
   controls, which is where it was buried before. */
#beam-copy {
  /* LEFT, not right: the lil-gui panel owns the right edge and swallowed it
     there — the button existed and could not be clicked. */
  position: absolute; left: 12px; bottom: 40px; z-index: 7;
  width: 40px; height: 40px; padding: 0;
  font: 600 18px ui-monospace, Menlo, monospace; line-height: 1;
  color: #9fdcff; background: rgba(12, 26, 38, 0.85);
  border: 1px solid #2b6b96; border-radius: 8px; cursor: pointer;
}
#beam-copy:hover { background: rgba(22, 50, 74, 0.95); color: #dff4ff; }
#beam-copy:active { transform: translateY(1px); }
#beam-hud.flash { color: #66ff88; text-shadow: 0 0 10px rgba(102, 255, 136, 0.6); }
#beam-hud {
  position: absolute; left: 12px; bottom: 12px; z-index: 6;
  font: 600 12px ui-monospace, Menlo, monospace; letter-spacing: 0.08em;
  color: #9fdcff; text-shadow: 0 0 8px rgba(0, 180, 255, 0.5);
  pointer-events: none;
  /* the drop-off readout is three lines: where the beam ends, what it
     burned, and what it never reached. Without this they run off the edge
     as one line and the last two are simply not there. */
  white-space: pre-line; max-width: 60vw; line-height: 1.5;
}

/* THE SHOOTING LAB'S TWO PANELS. lil-gui pins itself to the top-right by
   default, which is where forty controls became one unreadable column. The
   STAGE goes left and the WEAPON stays right, and both get a max height with
   their own scroll so neither can run off the bottom of the screen again —
   that was the actual complaint, and a split alone would not have fixed it on
   a short window. */
/* SPECIFICITY, twice over. These have to out-rank `.tab .lil-gui.root`
   (0,3,0) up at line 1170, which pins every lab panel to top:0 right:0 —
   a `.lil-gui.lab-gui-left` rule (0,2,0) lost to it silently, and the symptom
   was the panel moving LEFT while its top stayed at 0, still under the shell bar.
   Losing half a rule is a good deal harder to read than losing all of it. */
.tab .lil-gui.root.lab-gui-left,
.tab .lil-gui.root.lab-gui-right {
  position: absolute; overflow-y: auto;
}
/* the LEFT one starts BELOW the top strip: the navigation shell's bar lives
   there and sat straight on the first control, which is how the sentry
   selector ended up unreadable behind it */
.tab .lil-gui.root.lab-gui-left {
  left: 8px; right: auto; top: 62px; max-height: calc(100vh - 100px);
}
.tab .lil-gui.root.lab-gui-right {
  right: 0; left: auto; top: 0; max-height: 100vh;
}

/* THE IMPACT LAB. Same furniture as the beam lab — the copy button on the
   LEFT because lil-gui owns the right edge and swallowed it there. */
#impact-app { position: absolute; inset: 0; }
#impact-app canvas { display: block; width: 100%; height: 100%; }
#impact-copy {
  position: absolute; left: 12px; bottom: 40px; z-index: 7;
  width: 40px; height: 40px; padding: 0;
  font: 600 18px ui-monospace, Menlo, monospace; line-height: 1;
  color: #ffc98a; background: rgba(34, 20, 12, 0.85);
  border: 1px solid #96622b; border-radius: 8px; cursor: pointer;
}
#impact-copy:hover { background: rgba(60, 36, 20, 0.95); color: #fff0dd; }
#impact-copy:active { transform: translateY(1px); }
#impact-hud {
  position: absolute; left: 12px; bottom: 12px; z-index: 6;
  font: 600 12px ui-monospace, Menlo, monospace; letter-spacing: 0.08em;
  color: #ffc98a; text-shadow: 0 0 8px rgba(255, 150, 40, 0.5);
  pointer-events: none;
  white-space: pre-line; max-width: 60vw; line-height: 1.5;
}

#units-app { position: absolute; inset: 0; }
#units-app canvas { display: block; width: 100%; height: 100%; }
#units-chrome {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 12px 12px 16px; pointer-events: none;
}
#units-chrome > * { pointer-events: auto; }
#units-groups { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }
#units-groups button, #units-opts button, #units-stage button {
  font: 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .08em; text-transform: uppercase;
  color: #9fdcff; background: rgba(10, 16, 26, .72);
  border: 1px solid rgba(159, 220, 255, .28); border-radius: 8px;
  padding: 7px 12px; cursor: pointer;
}
#units-groups button.on, #units-opts button.on {
  color: #061018; background: #6fe6ff; border-color: #6fe6ff;
}
/* The arrows must not move as you step through units. They used to sit
   either side of a block with only a min-width, so a long caption pushed
   them apart and every unit put them somewhere new — you had to re-aim
   between clicks. A fixed stage width pins both to its edges instead. */
#units-stage {
  position: relative; display: flex; align-items: center; justify-content: center;
  width: min(660px, 92vw); min-height: 112px;
}
/* THE ARROWS NEVER MOVE (owner, 2026-10-02): pinned to the stage's edges, independent of the name and the note between them */
#units-stage button { font-size: 20px; padding: 2px 14px; line-height: 1.1; }
#units-prev, #units-next { position: absolute; top: 50%; transform: translateY(-50%); }
#units-prev { left: 0; } #units-next { right: 0; }
#units-id { text-align: center; flex: 1; min-width: 0; padding: 0 64px; }
/* ANIMATION is the bench's main switch */
#units-sweep { font-size: 14px; padding: 8px 20px; border-color: rgba(111, 230, 255, .6); color: #bfe6ff; }
#units-sweep.on { color: #061018; background: #6fe6ff; border-color: #6fe6ff; }
#units-link {
  font: 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .1em; text-transform: uppercase;
  color: #7f93ab; background: transparent;
  border: 1px solid rgba(159, 220, 255, .2); border-radius: 6px;
  padding: 3px 8px; margin: 3px auto 0; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
}
#units-link:hover { color: #9fdcff; border-color: rgba(159, 220, 255, .45); }
#units-link.ok { color: #33ff77; border-color: rgba(51, 255, 119, .5); }
#units-link.fail { color: #ffb000; border-color: rgba(255, 176, 0, .5); }
#units-name {
  font: 600 15px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .12em; text-transform: uppercase; color: #eaf6ff;
}
/* two lines reserved: the longest captions wrap, and a block that changes
   height would move the arrows vertically instead of horizontally */
#units-note {
  font: 11px/1.4 ui-monospace, monospace; color: #7f93ab; margin-top: 3px;
  min-height: 2.8em;
}
#units-count { font: 11px/1.4 ui-monospace, monospace; color: #4d6076; margin-top: 2px; }
#units-opts { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: center; }
#units-opts label {
  font: 11px/1 ui-monospace, monospace; color: #7f93ab;
  display: flex; align-items: center; gap: 6px;
}
#units-opts select {
  font: 11px/1 ui-monospace, monospace; color: #9fdcff;
  background: rgba(10, 16, 26, .72);
  border: 1px solid rgba(159, 220, 255, .28); border-radius: 6px; padding: 5px 6px;
}
#units-hint { font: 10px/1 ui-monospace, monospace; color: #43566b; letter-spacing: .06em; }
#units-look-wrap.hidden { display: none; }

/* the viewer's sound row: tuning aid and unit lore in the same control */
#units-sounds { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; max-width: 92vw; }
#units-sounds button {
  font: 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .06em; color: #cfe4ff;
  background: rgba(10, 16, 26, .72);
  border: 1px solid rgba(159, 220, 255, .22); border-radius: 999px;
  padding: 6px 12px; cursor: pointer;
}
#units-sounds button:hover { border-color: rgba(159, 220, 255, .5); }
#units-sounds button.on { color: #061018; background: #6fe6ff; border-color: #6fe6ff; }
/* the row keeps its height when empty, so the arrows above it never move (owner, 2026-10-02) */
#units-sounds { min-height: 30px; }
#units-sounds.hidden { visibility: hidden; pointer-events: none; }

/* ============================================================
   .mdview — the reading surface for DEVLOG.md, HOW-IT-WORKS.md
   and TECH-STACK.md.

   Direction: a spec plate. The type is split by what the words
   ARE, not for decoration — prose runs in the system's
   proportional face because it is meant to be read, and
   monospace is reserved for things that are machine-shaped
   (identifiers, table cells, labels, headings). When everything
   was monospace the distinction was free-floating and nothing
   stood out; now the face itself says "this is a literal".

   No webfont on purpose: a Google Fonts request would contradict
   the one thing TECH-STACK.md claims about this project.

   Layout is a prose column with the table breaking out wide —
   the third column used to wrap to shreds inside a 72ch measure.
   ============================================================ */

.mdview {
  --md-ink: #c9d6ec;          /* body */
  --md-dim: #8fa0be;          /* secondary, table prose */
  --md-bright: #eef4ff;       /* headings, strong */
  --md-neon: #6fe6ff;         /* structure: spines, bars, chips */
  --md-soft: #9fdcff;         /* labels, table heads */
  --md-code: #9fd6c8;         /* literals */
  --md-rule: rgba(159, 220, 255, .15);
  --md-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;
  /* measure for anything read line by line. em, not ch: the sans fallback's
     "0" is far wider than its average letter, so 66ch measured out at 83
     characters a line — well past the point where the eye loses the return. */
  --md-prose: 35em;

  color: var(--md-ink);
  font: 400 16px/1.7 ui-sans-serif, -apple-system, BlinkMacSystemFont,
        "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* wider than the prose measure on purpose: the prose column sits at the left
   of this box and the table breaks out across the whole of it */
#how-content, #stack-content, #roadmap-content, #log-content {
  display: block;
  max-width: 800px;
  margin: 0 auto;
}

/* --- title block --- */
.mdview h1 {
  margin: 0 0 10px;
  font: 600 30px/1.15 var(--md-mono);
  letter-spacing: -.01em;
  color: var(--md-bright);
}
/* the sentence under the title is a dek, not a paragraph */
.mdview h1 + p {
  max-width: var(--md-prose);
  margin: 0 0 6px;
  font-size: 17px;
  color: var(--md-dim);
}

/* --- sections. the short cyan bar is the section mark; DEVLOG's own
   "---" rules sit above it and the two read as one divider --- */
.mdview h2 {
  max-width: var(--md-prose);
  margin: 46px 0 14px;
  font: 600 18px/1.45 var(--md-mono);
  letter-spacing: -.005em;
  color: var(--md-bright);
}
.mdview h2::before {
  content: "";
  display: block;
  width: 26px;
  height: 2px;
  margin-bottom: 14px;
  background: var(--md-neon);
  box-shadow: 0 0 10px rgba(111, 230, 255, .55);
}
.mdview h1 + p + h2 { margin-top: 34px; }

/* --- verdict chips. TECH-STACK's "will it fit?" answers are a
   classification, not a sequence, so they are labelled, not numbered --- */
.mdview h3 {
  display: inline-block;
  margin: 34px 0 0;
  padding: 6px 13px;
  font: 600 11.5px/1 var(--md-mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--md-neon);
  background: rgba(111, 230, 255, .07);
  border: 1px solid rgba(111, 230, 255, .38);
  border-radius: 999px;
}
.mdview h3 + p { margin-top: 12px; }

/* --- the one load-bearing sentence, stamped --- */
.mdview blockquote {
  position: relative;
  max-width: 30em;       /* its own, in its own larger em */
  margin: 30px 0 40px;
  padding: 14px 0 14px 26px;
  font: 600 20px/1.5 var(--md-mono);
  letter-spacing: -.01em;
  color: var(--md-bright);
  text-wrap: balance;
}
/* the spine glows outward, the way everything else in this project does */
.mdview blockquote::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--md-neon);
  box-shadow: 0 0 14px rgba(111, 230, 255, .7);
}

.mdview p {
  max-width: var(--md-prose);
  margin: 14px 0;
}

.mdview ul {
  max-width: var(--md-prose);
  margin: 16px 0 22px;
  padding: 0;
  list-style: none;
}
.mdview li {
  position: relative;
  margin: 12px 0;
  padding-left: 20px;
}
.mdview li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: .62em;
  width: 5px;
  height: 5px;
  background: var(--md-soft);
  opacity: .75;
}

.mdview hr {
  max-width: var(--md-prose);
  margin: 40px 0;
  border: none;
  border-top: 1px solid var(--md-rule);
}

/* --- the plate. breaks out of the prose measure; column 3 is prose so it
   is set in the prose face, columns 1-2 are literals so they are not --- */
.mdview table {
  width: 100%;
  table-layout: fixed;   /* so the column widths below are exact */
  margin: 20px 0 30px;
  border-collapse: collapse;
  border-top: 1px solid rgba(159, 220, 255, .28);
  border-bottom: 1px solid rgba(159, 220, 255, .28);
  background: rgba(140, 190, 255, .022);
}
.mdview thead th {
  padding: 11px 14px;
  text-align: left;
  vertical-align: bottom;
  font: 600 10.5px/1.3 var(--md-mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--md-soft);
  border-bottom: 1px solid var(--md-rule);
  white-space: nowrap;
}
.mdview tbody td {
  padding: 13px 14px;
  vertical-align: top;
  border-bottom: 1px solid rgba(120, 150, 210, .1);
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--md-ink);
}
.mdview tbody tr:last-child td { border-bottom: 0; }
.mdview tbody td:nth-child(1) {
  font: 600 13px/1.5 var(--md-mono);
  letter-spacing: -.01em;
  color: var(--md-soft);
}
.mdview tbody td:nth-child(2) {
  font: 400 13px/1.6 var(--md-mono);
  color: var(--md-ink);
}
.mdview th:nth-child(1) { width: 16%; }
.mdview th:nth-child(2) { width: 33%; }
.mdview th:nth-child(3) { width: 51%; }

/* --- inline --- */
.mdview code {
  padding: 1.5px 5px;
  font-family: var(--md-mono);
  font-size: .875em;
  color: var(--md-code);
  background: rgba(120, 200, 190, .08);
  border: 1px solid rgba(120, 200, 190, .16);
  border-radius: 4px;
  white-space: nowrap;   /* an identifier split over two lines is unreadable */
}
.mdview td code, .mdview h2 code, .mdview blockquote code {
  border: none;
  background: rgba(120, 200, 190, .1);
}
.mdview strong { font-weight: 600; color: var(--md-bright); }
/* italics stay in the body ink — tinted, they read as links in dense prose */
.mdview em { font-style: italic; }
.mdview a { color: #7fc7ff; text-underline-offset: 3px; }
.mdview a:hover { color: var(--md-neon); }

@media (max-width: 700px) {
  #how-scroll, #stack-scroll, #roadmap-scroll, #log-scroll, #record-scroll { padding: 92px 18px 96px; }
  .mdview { font-size: 15.5px; }
  .mdview h1 { font-size: 25px; }
  .mdview h2 { font-size: 16.5px; margin-top: 38px; }
  .mdview blockquote { font-size: 17px; padding-left: 18px; }

  /* The plate keeps its shape and scrolls sideways instead of collapsing to
     three shredded columns. The scroll has to come from the CELLS being wider
     than the box — a min-width on the <table> would size the box itself, and
     then the whole article pans sideways instead of just the table. */
  .mdview table {
    display: block;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    table-layout: auto;
  }
  .mdview th:nth-child(1), .mdview td:nth-child(1) { width: auto; min-width: 88px; }
  .mdview th:nth-child(2), .mdview td:nth-child(2) { width: auto; min-width: 172px; }
  .mdview th:nth-child(3), .mdview td:nth-child(3) { width: auto; min-width: 232px; }
}

/* --- copy the whole stack: top-right of the tech-stack tab, parked
   against the fixed tab so the article scrolls under it --- */
#stack-copy, #roadmap-copy {
  position: absolute;
  top: max(12px, env(safe-area-inset-top));
  right: max(12px, env(safe-area-inset-right));
  z-index: 12;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 14px 9px 12px;
  font: 600 11px/1 ui-monospace, "SF Mono", Menlo, monospace;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #cfe4ff;
  background: rgba(10, 16, 26, .84);
  border: 1px solid rgba(159, 220, 255, .26);
  border-radius: 999px;
  cursor: pointer;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  transition: color .15s, background .15s, border-color .15s;
}
#stack-copy .glyph, #roadmap-copy .glyph { font-size: 15px; line-height: 1; }
#stack-copy:hover, #roadmap-copy:hover { color: #eaf6ff; border-color: rgba(159, 220, 255, .6); }
#stack-copy:focus-visible, #roadmap-copy:focus-visible { outline: 2px solid #6fe6ff; outline-offset: 3px; }
#stack-copy.ok, #roadmap-copy.ok {
  color: #061018;
  background: #6fe6ff;
  border-color: #6fe6ff;
  box-shadow: 0 0 16px rgba(111, 230, 255, .45);
}
#stack-copy.fail, #roadmap-copy.fail { color: #ffcfa2; border-color: rgba(255, 176, 102, .6); }

@media (prefers-reduced-motion: reduce) {
  #stack-copy, #roadmap-copy { transition: none; }
}

/* ── HOME LAUNCHER ─────────────────────────────────────────────────────── */

#home-scroll {
  position: absolute; inset: 0;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: 76px max(20px, env(safe-area-inset-left)) 60px;
}
#home-head { max-width: 720px; margin: 0 auto 34px; }
#home-head h1 {
  margin: 0 0 10px;
  font: 600 26px/1.15 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .04em; color: #eaf6ff;
}
#home-head p {
  margin: 0; max-width: 46em;
  font: 14px/1.7 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  color: #8ea3bb;
}
#home-grid { max-width: 720px; margin: 0 auto; }

.home-group { margin: 0 0 30px; }
.home-group-head {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding-bottom: 8px; margin-bottom: 14px;
  border-bottom: 1px solid rgba(120, 150, 210, .16);
}
.home-group-head h2 {
  margin: 0;
  font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .18em; text-transform: uppercase; color: #6fe6ff;
}
.home-group-head span {
  font: 12px/1.4 -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  color: #667e99;
}
.home-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(216px, 1fr)); gap: 10px; }
.home-card {
  display: flex; flex-direction: column; gap: 7px;
  padding: 14px 15px; text-align: left; cursor: pointer;
  background: rgba(14, 20, 32, .72);
  border: 1px solid rgba(120, 150, 210, .18);
  border-radius: 12px;
  transition: border-color .12s, background .12s, transform .12s;
}
/* THE MISSION YOU ARE IN. A card that navigates to where you already are is
   a card that reloads the board for nothing, so it says so instead. */
.home-card.current {
  border-color: rgba(255, 180, 94, 0.7);
  box-shadow: inset 0 0 0 1px rgba(255, 180, 94, 0.25);
  cursor: default;
}
.home-card.current .home-card-title::after { content: ' · RUNNING'; color: #ffb45e; font-size: 0.72em; letter-spacing: 0.12em; }
.home-card:hover { border-color: rgba(111, 230, 255, .55); background: rgba(18, 27, 42, .9); transform: translateY(-1px); }
.home-card:active { transform: translateY(0); }
.home-card-title {
  font: 600 14px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .06em; color: #cdf3ff;
}
.home-card-line {
  font: 12.5px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  color: #8296ad;
}
@media (pointer: coarse), (max-width: 700px) {
  #home-scroll { padding-top: 66px; }
  .home-cards { grid-template-columns: 1fr; }
}

/* ── THROTTLE ────────────────────────────────────────────────────────────
   One lever in place of the ▲/▼ pair. It HOLDS where you leave it, so
   setting it is what cruise used to be — there is no mode to engage.
   Reverse is the same lever continued below zero, and its travel is
   deliberately SHORTER than forward's: the control tells you that backing
   up is slower before you try it. */
.throttle {
  position: absolute;
  /* Left-aligned, stacked ABOVE the minimap. Three things want the bottom
     left corner — minimap, left steering pad, throttle — so the lever takes
     the space above the disc rather than the centre of the screen, where it
     sat on top of the road ahead. The offset is the minimap's own sizing
     expression (JS: min(240, min(w,h) * 0.32)) so the two move together. */
  bottom: calc(max(14px, env(safe-area-inset-bottom)) + min(240px, 32vmin) + 14px);
  left: max(14px, env(safe-area-inset-left));
  z-index: 7;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 8px 10px 6px;
  border-radius: 20px;
  background: rgba(28, 38, 58, 0.16);
  border: 1px solid rgba(120, 150, 210, 0.16);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  touch-action: none;           /* we own the drag; no scroll hijack */
  -webkit-user-select: none; user-select: none;
  cursor: ns-resize;
}
.throttle-track {
  position: relative;
  width: 34px; height: min(30vh, 210px);
  border-radius: 17px;
  background: rgba(10, 16, 26, 0.5);
  border: 1px solid rgba(120, 150, 210, 0.18);
  overflow: hidden;
}
.throttle-fill {
  position: absolute; left: 0; right: 0;
  background: linear-gradient(180deg, rgba(111, 230, 255, .5), rgba(111, 230, 255, .22));
}
.throttle.rev .throttle-fill {
  background: linear-gradient(180deg, rgba(255, 176, 0, .22), rgba(255, 176, 0, .5));
}
/* the zero detent, drawn where the lever rests */
.throttle-zero {
  position: absolute; left: 4px; right: 4px;
  top: calc(100% / 1.4);        /* 1 / (1 + reverse ceiling) */
  height: 1px; margin-top: -0.5px;
  background: rgba(200, 220, 255, .35);
}
.throttle-handle {
  position: absolute; left: -3px; right: -3px;
  height: 12px; margin-top: -6px;
  border-radius: 6px;
  background: rgba(207, 228, 255, .92);
  box-shadow: 0 0 10px rgba(111, 230, 255, .5);
}
.throttle.rev .throttle-handle { box-shadow: 0 0 10px rgba(255, 176, 0, .55); }
.throttle.idle .throttle-handle { background: rgba(160, 180, 205, .7); box-shadow: none; }
.throttle.pressed { border-color: rgba(140, 180, 250, 0.5); }
.throttle-read {
  font: 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: rgba(213, 226, 245, .55); letter-spacing: .06em;
  min-height: 12px;
}
.throttle.rev .throttle-read { color: rgba(255, 200, 120, .8); }

/* width clause as well as touch: a coarse-pointer-only query cannot be
   exercised by any desktop browser, so its rules are unverifiable. */
@media (pointer: coarse), (max-width: 560px) {
  /* Cosmetic only. This block used to carry the lever's POSITION too, and
     being later in the file it quietly won — so the placement set above was
     overwritten and the lever went back to the corner it had just left.
     Position lives in one place now; this one only makes it compact. */
  .throttle { padding: 6px 8px 5px; background: rgba(28, 38, 58, 0.13); }
  /* shorter than it was: the lever now stacks above the left steering
     strip rather than owning the corner, so it shares the column */
  .throttle-track { width: 26px; height: min(14vh, 118px); }
  .throttle-read { font-size: 10px; }
}

/* the unit viewer's tank bench: engine, wreck and health in one row */
#units-bench { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: center; }
/* Reserved, not removed. The bench row only exists for units with a hover
   split, so `display: none` made the whole chrome stack shorter for towers
   and pickups — which moved the < > arrows UP. They were pinned
   horizontally and still landing somewhere new. A little dead space below
   the caption is the cheaper of the two. */
#units-bench.hidden { visibility: hidden; pointer-events: none; }
#units-bench button {
  font: 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .06em; color: #cfe4ff;
  background: rgba(10, 16, 26, .72);
  border: 1px solid rgba(159, 220, 255, .22); border-radius: 999px;
  padding: 6px 14px; cursor: pointer;
}
#units-bench button.on { color: #061018; background: #6fe6ff; border-color: #6fe6ff; }
#units-bench button:hover { border-color: rgba(159, 220, 255, .5); }
#units-bench label {
  display: flex; align-items: center; gap: 7px;
  font: 11px/1 ui-monospace, monospace; color: #7f93ab;
}
#units-bench input[type=range] { width: 108px; accent-color: #6fe6ff; }

/* --- unit-viewer tuning modal --------------------------------------------
   A floating panel rather than a real modal: you have to SEE the tank while
   you drag, so it must never cover the middle of the stage or take the
   pointer away from the orbit controls. */
#units-tuner {
  /* fixed, not absolute: its parent #units-chrome is a bottom-anchored
     absolute strip, so an absolute panel would hang off the CONTROLS rather
     than the stage, and a percentage max-height would resolve against that
     strip's few dozen pixels and collapse the list to nothing. Both of which
     it did. The viewport is the only sane containing block here, and
     #units-chrome > * already restores pointer events. */
  position: fixed;
  top: 58px;
  right: 14px;
  z-index: 6;
  width: 300px;
  max-height: calc(100vh - 170px);
  display: flex;
  flex-direction: column;
  background: rgba(8, 13, 20, .92);
  border: 1px solid rgba(159, 220, 255, .28);
  border-radius: 10px;
  backdrop-filter: blur(6px);
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: #cfe6f5;
}
#units-tuner.tuner-hidden { display: none; }
#units-tuner header,
#units-tuner footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
}
#units-tuner header {
  border-bottom: 1px solid rgba(159, 220, 255, .18);
  letter-spacing: .08em;
  text-transform: uppercase;
  font-size: 11px;
  color: #9fdcff;
}
#units-tuner footer { border-top: 1px solid rgba(159, 220, 255, .18); }
#units-tuner-knobs { overflow-y: auto; padding: 6px 10px 8px; }
.tuner-group {
  margin: 8px 0 4px;
  color: #6fe6ff;
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  opacity: .8;
}
.tuner-row {
  display: grid;
  grid-template-columns: 1fr 88px 44px;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
}
.tuner-row .tuner-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tuner-row input[type=range] { width: 100%; accent-color: #6fe6ff; }
.tuner-row output { text-align: right; color: #9fdcff; font-variant-numeric: tabular-nums; }
#units-tuner button {
  background: rgba(20, 32, 44, .8);
  color: #cfe6f5;
  border: 1px solid rgba(159, 220, 255, .3);
  border-radius: 6px;
  padding: 4px 10px;
  font: inherit;
  cursor: pointer;
}
#units-tuner button:hover { color: #eaf6ff; border-color: rgba(159, 220, 255, .6); }
#units-tune-close { padding: 0 7px; font-size: 15px; line-height: 1.4; }
#units-tune-copy { display: inline-flex; align-items: center; gap: 6px; }
#units-tune-copy.ok { color: #9effc4; border-color: rgba(120, 255, 180, .6); }
#units-tune-copy.fail { color: #ffcfa2; border-color: rgba(255, 176, 102, .6); }
#units-tune.on { color: #6fe6ff; border-color: rgba(111, 230, 255, .7); }
@media (max-width: 560px) {
  #units-tuner { left: 12px; right: 12px; width: auto; top: 52px;
    max-height: calc(100vh - 240px); }
  .tuner-row { grid-template-columns: 1fr 84px 38px; }
}

/* --- blueprint callouts ---------------------------------------------------
   A projected overlay, not sprites: HTML text stays crisp at any zoom and
   never picks up the bloom pass, which would smear the one thing that has to
   stay readable. Pointer-transparent so it never steals an orbit drag. */
#units-callouts {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 4;
  overflow: hidden;
}
#units-callouts.hidden { display: none; }
#units-callouts .callout {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  flex-direction: column;
  padding: 2px 6px;
  margin-top: -13px;              /* transform y is the label's CENTRE */
  border-left: 1px solid rgba(111, 230, 255, .75);
  background: rgba(6, 11, 18, .72);
  font: 10px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
  transition: opacity .12s linear;
}
/* labels on the left of the model hang off to the left, so their text ends
   where the leader begins rather than running back over the tank */
#units-callouts .callout.left {
  border-left: 0;
  border-right: 1px solid rgba(111, 230, 255, .75);
  text-align: right;
}
/* The leader starts at the label's TRANSFORM POINT, because that is the
   point the JS measured the angle from. For a right-hand label that point is
   its left edge; for a left-hand one, translateX(-100%) puts it at the right
   edge. Anchoring to the other edge (as this did) draws every leader away
   from the tank instead of towards it — which is why they all ended in empty
   space off the side of the frame. */
#units-callouts .callout s {
  position: absolute;
  top: 50%;
  left: 0;
  height: 1px;
  background: rgba(111, 230, 255, .42);
  transform-origin: 0 50%;
  pointer-events: none;
}
#units-callouts .callout.left s { left: 100%; }
#units-callouts .cdot {
  position: absolute;
  top: 0;
  left: 0;
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: #6fe6ff;
  box-shadow: 0 0 6px rgba(111, 230, 255, .9);
  transition: opacity .12s linear;
}
#units-callouts .callout b { color: #cfe6f5; font-weight: 500; letter-spacing: .04em; }
#units-callouts .callout i {
  color: #6fe6ff;
  font-style: normal;
  opacity: .62;
  font-size: 9px;
}
#units-labels.on { color: #6fe6ff; border-color: rgba(111, 230, 255, .7); }

/* --- dev log + roadmap, one slot ------------------------------------------
   Two readings of the same document — what happened, and what is meant to —
   so they sit behind one tab rather than lengthening a nav bar that is
   already long. The build token rides along because "which build am I
   looking at" is the first question anyone opening a change log has. */
#log-bar {
  position: absolute;
  top: 46px;
  left: 0;
  right: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 8px 14px;
  background: linear-gradient(rgba(5, 8, 14, .92), rgba(5, 8, 14, .72) 70%, transparent);
  pointer-events: none;
}
#log-tabs { display: flex; gap: 6px; pointer-events: auto; }
#log-tabs button {
  font: 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #9fdcff;
  background: rgba(10, 16, 26, .72);
  border: 1px solid rgba(159, 220, 255, .28);
  border-radius: 8px;
  padding: 7px 14px;
  cursor: pointer;
}
#log-tabs button:hover { color: #eaf6ff; border-color: rgba(159, 220, 255, .6); }
#log-tabs button.active {
  color: #061018;
  background: #6fe6ff;
  border-color: #6fe6ff;
}
#log-build {
  font: 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .06em;
  color: #6fe6ff;
  opacity: .62;
}
@media (max-width: 560px) {
  #log-bar { top: 40px; gap: 8px; padding: 6px 10px; }
  #log-tabs button { padding: 6px 10px; font-size: 11px; }
  #log-build { font-size: 10px; }
}

/* There is no global .hidden in this sheet — every use is scoped, so a new
   one has to declare itself or it silently does nothing. The roadmap pane
   showed through behind the dev log for exactly that reason. */
#log-scroll.hidden, #roadmap-scroll.hidden { display: none; }

/* a choice knob is a list, not a range: the select spans both control cells */
.tuner-row.choice { grid-template-columns: 1fr 136px; }
.tuner-row select {
  width: 100%;
  font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: #cfe6f5;
  background: rgba(20, 32, 44, .9);
  border: 1px solid rgba(159, 220, 255, .3);
  border-radius: 5px;
  padding: 2px 4px;
}

/* --- phone: the callout clears the whole top block ------------------------
   Modes, HUD, and the MAP, which is the tallest of the three. Derived from
   the map's own sizing expression so the two cannot drift apart.

   This lives at the END of the file on purpose: #td-tut's base rule is
   declared after the mobile blocks above, and at equal specificity the later
   declaration wins — an override up there was simply ignored. */
@media (pointer: coarse), (max-width: 700px) {
  #td-tut,
  #td-tut.tut-flash {
    top: calc(max(8px, env(safe-area-inset-top)) + 42px + min(240px, 32vmin) + 14px);
    max-width: min(92vw, 420px);
  }
}

/* --- LAUNCH CONTROL — DeepWatch's console, ported whole -------------------
   The hazard border, the metal safety toggle (its two PNG sprites came with
   it), and the chunky launch button that runs grey -> orange (needs a
   target) -> red (authorised). The ritual is unchanged underneath: this is
   the same arm/paint/launch state machine wearing the instrument it
   deserved. Source: ~/Documents/Dev/centroid-defense. */
/* RETIRED FROM VIEW (owner, 2026-09-14): the gunship's 105 is the strike now, so the console and its switch are hidden;
   the strike's code stays (the 105 lands through it). An orbital laser may come back as its own weapon. */
#td-launch { display: none !important; }
#td-launch {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  /* top of the screen, directly under the mode row: the orbital strike is
     promoted to the HUD's first rank (operator ruling), and the bottom
     centre it used to hold belongs to thumbs */
  top: 92px;
  z-index: 7;
  width: 296px;
  font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
}

.caution-frame {
  border: 4px solid transparent;
  border-image: repeating-linear-gradient(45deg, #ffc548 0 10px, #1a1a1a 10px 20px) 4;
  background: rgba(6, 10, 8, 0.88);
  padding: 6px 10px;
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 78px;
}
.safety {
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  font: inherit;
  color: #3a8c3a;
  width: 36px;
}
.safety .switch-img {
  width: 28px;
  height: 46px;
  object-fit: contain;
  pointer-events: none;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.6));
}
.safety .lbl-on, .safety .lbl-off {
  font-size: 9px;
  letter-spacing: 0.18em;
  color: #274427;
  transition: color 180ms, text-shadow 180ms;
  line-height: 1;
}
.safety[aria-pressed="true"] .lbl-on { color: #ff3322; text-shadow: 0 0 6px rgba(255, 51, 34, 0.6); }
.safety[aria-pressed="false"] .lbl-off { color: #3a8c3a; text-shadow: 0 0 4px rgba(58, 140, 58, 0.4); }
.safety.locked { cursor: not-allowed; opacity: 0.45; }
#td-launch-readout {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  text-align: center;
  overflow: hidden;
}
#td-launch-readout .status {
  color: #3a8c3a;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#td-launch-readout .status.charging { color: #a06a22; }
#td-launch-readout .status.ready { color: #ffaa44; text-shadow: 0 0 6px rgba(255, 170, 68, 0.4); }
#td-launch-readout .status.armed { color: #ff3322; text-shadow: 0 0 6px rgba(255, 51, 34, 0.6); }
#td-launch-readout .target {
  color: #274427;
  font-size: 9px;
  letter-spacing: 0.18em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#td-launch-readout .target.set { color: #88ff88; text-shadow: 0 0 5px rgba(136, 255, 136, 0.5); }
.launch-button {
  width: 76px;
  height: 58px;
  border: 3px solid #444;
  border-radius: 10px;
  background: radial-gradient(circle at 35% 30%, #4a4a4a, #2a2a2a 70%, #1a1a1a);
  color: #555;
  font-family: inherit;
  font-weight: 800;
  cursor: not-allowed;
  padding: 0;
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.5), inset 0 2px 0 rgba(255, 255, 255, 0.05);
  transition: transform 80ms ease, box-shadow 200ms ease, background 200ms ease, color 200ms ease;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  line-height: 1;
}
.launch-button .kanji {
  font-size: 21px;
  font-weight: 900;
  font-family: 'Hiragino Mincho ProN', 'Yu Mincho', 'Noto Serif JP', serif;
}
.launch-button .latin { font-size: 8px; letter-spacing: 0.22em; margin-top: 1px; }
.launch-button.target {
  background: radial-gradient(circle at 35% 30%, #ffb255, #ff7a14 60%, #b04400);
  color: #fff;
  border-color: #c25a14;
  cursor: pointer;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55);
  animation: targetPulse 1.4s ease-in-out infinite;
}
@keyframes targetPulse {
  0%, 100% { box-shadow: 0 0 10px rgba(255, 130, 40, 0.55), inset 0 -3px 0 rgba(0, 0, 0, 0.45), inset 0 2px 0 rgba(255, 255, 255, 0.12); }
  50% { box-shadow: 0 0 20px rgba(255, 130, 40, 0.95), inset 0 -3px 0 rgba(0, 0, 0, 0.45), inset 0 2px 0 rgba(255, 255, 255, 0.18); }
}
.launch-button.armed {
  background: radial-gradient(circle at 35% 30%, #ff7766, #cc1010 60%, #660000);
  color: #fff;
  border-color: #8a1a1a;
  cursor: pointer;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
  animation: launchPulse 0.8s ease-in-out infinite;
}
@keyframes launchPulse {
  0%, 100% { box-shadow: 0 0 16px rgba(255, 51, 34, 0.55), inset 0 -3px 0 rgba(0, 0, 0, 0.45), inset 0 2px 0 rgba(255, 255, 255, 0.18); }
  50% { box-shadow: 0 0 26px rgba(255, 51, 34, 0.95), inset 0 -3px 0 rgba(0, 0, 0, 0.45), inset 0 2px 0 rgba(255, 255, 255, 0.22); }
}
.launch-button.armed:active {
  transform: translateY(2px);
  animation: none;
  background: radial-gradient(circle at 35% 30%, #cc4433, #880000 60%, #330000);
}
@keyframes strike-refuse {
  0%, 100% { opacity: 1; }
  25%, 75% { opacity: 0.15; }
  50% { opacity: 1; }
}
#td-launch.flicker { animation: strike-refuse 0.36s linear; }
/* the feed owns the screen; the console stands aside with the rest */
#tab-td.striking #td-launch { visibility: hidden; }
@media (pointer: coarse), (max-width: 560px) {
  #td-launch { width: 236px; }
  .caution-frame { min-height: 66px; gap: 8px; padding: 5px 8px; }
  .launch-button { width: 64px; height: 50px; }
  .launch-button .kanji { font-size: 17px; }
}

/* --- orbital strike: the munition feed ------------------------------------
   The world goes black-and-white surveillance footage for the fall: a CSS
   filter on the WebGL canvas (compositing-cost only), a green ops HUD, and
   the range counter in red. pointer-events: none throughout — the tap that
   skips must reach the canvas underneath. */
#tab-td.striking #td-app canvas {
  filter: grayscale(1) contrast(1.3) brightness(1.12);
}
/* the gunship seat's view (owner, 2026-09-16): thermal, always — its visual identity, with no normal or night to switch to. The radar keeps its own green */
#tab-td.gunship-seat #td-strikecam { display: none !important; }   /* the 105 is watched from the seat, not the munition feed */
/* !important because three.js setSize writes `display: block` INLINE on its
   canvas, and an inline style beats any sheet rule. Found by probing the
   element's cssText — the selector matched and still lost. */
#tab-td.striking .minimap { display: none !important; }
/* the feed is a takeover: the game's own chrome stands aside so the ops HUD
   is the only text on screen — it was printing straight over the stats */
#tab-td.striking #td-stats,
#tab-td.striking .tc-util,
#tab-td.striking #td-next,
#tab-td.striking #td-toast,
#tab-td.striking #td-throttle,
#tab-td.striking .tzone,
#tab-td.striking .tfire { visibility: hidden; }
#td-strikecam {
  position: absolute;
  inset: 0;
  z-index: 8;
  pointer-events: none;
  font: 12px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: #49f36e;
  text-shadow: 0 0 6px rgba(73, 243, 110, 0.55);
}
#td-strikecam.hidden { display: none; }
/* scanlines + vignette: what says "feed" rather than "filter" */
#td-strikecam .sc-scan {
  position: absolute;
  inset: 0;
  background:
    repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.16) 0 1px, transparent 1px 3px),
    radial-gradient(ellipse at center, transparent 55%, rgba(0, 0, 0, 0.5) 100%);
}
#td-strikecam .sc-bracket {
  position: absolute;
  width: 60px;
  height: 60px;
  border: 2px solid rgba(73, 243, 110, 0.7);
}
#td-strikecam .sc-bracket.tl { top: 14%; left: 22%; border-right: 0; border-bottom: 0; }
#td-strikecam .sc-bracket.tr { top: 14%; right: 22%; border-left: 0; border-bottom: 0; }
#td-strikecam .sc-bracket.bl { bottom: 16%; left: 22%; border-right: 0; border-top: 0; }
#td-strikecam .sc-bracket.br { bottom: 16%; right: 22%; border-left: 0; border-top: 0; }
#td-strikecam .sc-cross {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -54%);
  font-size: 26px;
  opacity: 0.9;
}
#sc-info {
  position: absolute;
  /* below the cb-badge row, which is fixed at the page level and does not
     stand aside for the feed */
  top: max(52px, calc(env(safe-area-inset-top) + 44px));
  left: 16px;
  white-space: pre;
  letter-spacing: 0.08em;
}
#sc-range {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, 34px);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: #ff4136;
  text-shadow: 0 0 8px rgba(255, 65, 54, 0.6);
  font-variant-numeric: tabular-nums;
}
#td-strikecam .sc-skip {
  position: absolute;
  bottom: max(16px, env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  opacity: 0.55;
  letter-spacing: 0.2em;
}

/* --- tank-auto radial: pick a directive, not cycle six of them ----------- */
#td-auto-radial {
  position: absolute;
  top: 96px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px;
  background: rgba(8, 13, 20, 0.94);
  border: 1px solid rgba(159, 220, 255, 0.35);
  border-radius: 10px;
}
#td-auto-radial.hidden { display: none; }
#td-auto-radial button {
  font: 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.1em;
  color: #9fdcff;
  background: rgba(20, 32, 44, 0.7);
  border: 1px solid rgba(159, 220, 255, 0.25);
  border-radius: 7px;
  padding: 8px 14px;
  cursor: pointer;
  text-align: left;
}
#td-auto-radial button:hover { border-color: rgba(159, 220, 255, 0.6); color: #eaf6ff; }
#td-auto-radial button.active {
  color: #061018;
  background: #6fe6ff;
  border-color: #6fe6ff;
}

/* the score line leads the HUD; the insignia rides next to the best */
.hud-score { color: #e8edf5; font-weight: bold; letter-spacing: 0.06em; }
.hud-rank { color: #cfd3da; letter-spacing: 1px; }
.hud-rank svg { vertical-align: -6px; }

/* --- phone: the top-centre stack folds into the LEFT column -------------
   Headless lays out wide and crops, so these are verified by the ?layout
   rectangle probe, not by screenshots. The NEXT WAVE chip used to sit at
   top:8px centred — written straight over the mode row on a 390px screen
   (the operator's complaint). Column: stats, NEXT WAVE, launch console;
   the radar keeps the right. */
@media (pointer: coarse), (max-width: 700px) {
  #td-next {
    left: max(12px, env(safe-area-inset-left));
    right: auto;
    transform: none;
    top: calc(max(8px, env(safe-area-inset-top)) + 148px);
    max-width: 300px;
    text-align: left;
  }
  #td-launch {
    left: max(12px, env(safe-area-inset-left));
    transform: none;
    top: calc(max(8px, env(safe-area-inset-top)) + 206px);
  }
  #td-toast { top: 42%; }
}

/* --- MK-CX DOWN ----------------------------------------------------------
   A hull is lost. Same CRT phosphor idiom as the danger warning below —
   bloom plus a hint of RGB misconvergence — but held, not flickering: the
   warning means STOP TOUCHING THAT, this one means it already happened. */
#td-toast .td-down {
  font: 800 clamp(28px, 9vw, 60px)/1 ui-monospace, Menlo, monospace;
  letter-spacing: 0.1em;
  color: #ff2a1a;
  text-shadow: 0 0 22px rgba(255, 42, 26, 0.85),
    2px 0 0 rgba(255, 0, 60, 0.35), -2px 0 0 rgba(0, 255, 255, 0.25);
}
#td-toast .td-down-sub {
  margin-top: 4px;
  font: 600 12px ui-monospace, Menlo, monospace;
  letter-spacing: 0.22em;
  color: #ff8a7a;
  text-shadow: 0 0 10px rgba(255, 42, 26, 0.5);
}

/* --- the CRT danger warning ---------------------------------------------
   First dangerous contact of a wave: a red phosphor glyph with scanlines
   and a coarse flicker, gone in under two seconds. Deliberately louder
   than the toasts — it means STOP TOUCHING THAT. */
#td-danger {
  position: absolute;
  top: 36%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 11;
  text-align: center;
  pointer-events: none;
  color: #ff2a1a;
  font: bold 15px ui-monospace, Menlo, monospace;
  letter-spacing: 0.28em;
  animation: dg-flicker 0.12s steps(2) infinite;
}
#td-danger.hidden { display: none; }
#td-danger .dg-sign {
  font-size: 64px;
  line-height: 1;
  /* phosphor bloom + a hint of RGB misconvergence, the CRT tell */
  text-shadow: 0 0 18px rgba(255, 42, 26, 0.8),
    2px 0 0 rgba(255, 0, 60, 0.35), -2px 0 0 rgba(0, 255, 255, 0.25);
}
#td-danger .dg-label {
  margin-top: 6px;
  text-shadow: 0 0 10px rgba(255, 42, 26, 0.7);
}
#td-danger::after {
  content: '';
  position: absolute;
  inset: -12px -24px;
  background: repeating-linear-gradient(0deg,
    rgba(0, 0, 0, 0) 0 2px, rgba(0, 0, 0, 0.35) 2px 4px);
  pointer-events: none;
}
@keyframes dg-flicker {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.72; }
}
@media (prefers-reduced-motion: reduce) {
  #td-danger { animation: none; }
}

@keyframes pulse-soft { 50% { opacity: 0.45; } }

/* --- kill callouts + the ram combo counter -------------------------------
   Quick bragging text: pops big, settles, drifts up and out inside 1.2s.
   The combo counter is the intensity dial — every 10 rams it grows a size
   and heats a color, white through magenta. */
#td-callouts {
  position: absolute;
  top: 44%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 12;
  pointer-events: none;
  text-align: center;
  width: max-content;
}
.callout {
  /* THE SHOUT LAYER. Everything the game raises its voice with reads from
     the same four pack variables, so a typeface decision is one edit in
     src/fonts.js and never a sweep through this file. calc() on the size
     is what lets a bitmap face (which runs large) and a terminal face
     (which runs small) sit at the same optical weight. */
  font-family: var(--font-shout);
  font-size: calc(var(--shout-unit) * var(--r, 1));
  font-weight: bold;
  letter-spacing: var(--shout-track);
  text-shadow: var(--shout-halo);
  margin: 2px 0;
  animation: co-pop 1.2s ease-out forwards;
}
/* Three shouts, three phosphors — the halo comes from the pack now, so a
   callout never carries a glow the rest of the layer does not. */
.co-reckless { color: var(--p-green); }
.co-heart { color: var(--p-white); }
.co-streak { color: var(--p-blue); }
/* the A6's post marker sits on the board, not in this lane — but its
   button needs to read as an ORDER rather than a purchase: no cost, no
   colour from the tower palette, just the machine's own amber */
.radial-item.shop-move { border-color: #ffb45eaa; color: #ffd9a0; }
/* THE ENDING CLEARS THE GLASS. Every instrument fades out for the pull-out —
   a wide shot of a planet with a score and a throttle over it is a level with
   the camera pulled back, not an ending. Faded rather than removed, so
   nothing reflows and the layout cannot shift under a modal that is about to
   open on top of it. */
body.td-victory #td-stats,
body.td-victory #td-throttle,
body.td-victory #td-launch,
body.td-victory .minimap,
body.td-victory .tc-util,
body.td-victory .tfire,
body.td-victory #td-hint,
body.td-victory #td-tut {
  opacity: 0;
  transition: opacity 700ms ease-out;
  pointer-events: none;
}

/* THE LAST ENEMY. Everything else in this lane is praise for a good moment;
   this is the end of the campaign, so it is the one callout that is RED and
   the one that is bigger than a milestone. It holds longer too — the pop
   animation is tuned for a shout you read in passing, and this is a line the
   player should still be reading while the camera starts to pull away. */
.co-victory {
  color: #ff5a4d;
  --r: 1.9;
  text-shadow: 0 0 18px rgba(255, 90, 77, 0.85), 0 0 46px rgba(255, 60, 40, 0.55);
  letter-spacing: 0.18em;
  animation-duration: 3.4s;
}
.co-victory-sub {
  color: #ffd0c4;
  --r: 1.15;
  letter-spacing: 0.3em;
  text-shadow: 0 0 14px rgba(255, 120, 100, 0.6);
  animation-duration: 3.4s;
}
.co-milestone {
  color: var(--p-white);
  --r: 1.28;
  /* the milestone is bigger, not differently lit: one treatment across the
     whole shout tier, so a rung and a shout read as the same machine */
  text-shadow: var(--shout-halo);
}
/* THE CARGO SHOUT: a part secured, a tower unlocked. Information, not praise,
   so it outlives the praise switch and holds long enough to read the name */
.co-cargo {
  color: var(--p-white);
  --r: 1.45;
  letter-spacing: 0.14em;
  text-shadow: var(--shout-halo);
  animation-duration: 2.6s;
}
@keyframes co-pop {
  0% { opacity: 0; transform: scale(1.9); }
  14% { opacity: 1; transform: scale(1); }
  75% { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-20px) scale(0.94); }
}
#td-combo {
  position: absolute;
  /* below the callout stack: three concurrent callouts reach ~52% */
  top: 56%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 12;
  pointer-events: none;
  font-family: var(--font-shout);
  font-weight: bold;
  letter-spacing: var(--shout-track);
  text-shadow: var(--shout-halo);
}
#td-combo.hidden { display: none; }

/* --- callouts off: numbers only, and off the middle of the screen --------
   With the praise switched off what is left is scoring, and scoring does not
   want the centre of the board — that space is where the player is actually
   looking. The two numbers split to either side of it, high and a little in
   from the edges: the streak multiplier left, the ram count right, both
   readable in peripheral vision without ever sitting over the tank. */
.no-callouts #td-callouts {
  /* clear of the HUD: the score bar, the CTR buttons and the wave card own
     the top ~22%, and 17% put the numbers straight through them */
  top: 27%;
  left: 28%;
  text-align: right;
}
.no-callouts #td-combo {
  /* 72, not 76: the lil-gui panel is pinned top-right on DESKTOP (it only
     auto-hides on a phone) and is ~245px wide, so anything further out
     disappears under it on a narrower window. */
  top: 27%;
  left: 72%;
}
/* a phone has no room to push them this far out — bring them back in rather
   than off the edge */
@media (max-width: 560px) {
  .no-callouts #td-callouts { top: 30%; left: 27%; }
  .no-callouts #td-combo { top: 30%; left: 73%; }
}
/* numbers alone can be smaller than a shout and still read */
.no-callouts .callout {
  font-size: calc(var(--shout-unit) * 0.72);
  animation-duration: 0.9s;
}

/* the bench's ladder wears what #td-combo wears, minus the positioning */
.combobox [data-tier] {
  font-family: var(--font-shout);
  font-weight: bold;
  letter-spacing: var(--shout-track);
}
#td-combo[data-tier="0"], .combobox [data-tier="0"] { font-size: calc(var(--shout-unit) * 0.81); color: var(--p-green-dim); text-shadow: var(--shout-halo); }
#td-combo[data-tier="1"], .combobox [data-tier="1"] { font-size: calc(var(--shout-unit) * 1.05); color: var(--p-green); text-shadow: var(--shout-halo); }
#td-combo[data-tier="2"], .combobox [data-tier="2"] { font-size: calc(var(--shout-unit) * 1.33); color: var(--p-green-hot); text-shadow: var(--shout-halo); }
#td-combo[data-tier="3"], .combobox [data-tier="3"] { font-size: calc(var(--shout-unit) * 1.62); color: var(--p-white); text-shadow: var(--shout-halo); }
#td-combo[data-tier="4"], .combobox [data-tier="4"] { font-size: calc(var(--shout-unit) * 1.90); color: var(--p-blue-hot); text-shadow: var(--shout-halo); }
#td-combo[data-tier="5"], .combobox [data-tier="5"] {
  /* the top rung is the tube giving up: full white, and it flickers */
  font-size: calc(var(--shout-unit) * 2.29);
  color: #ffffff;
  text-shadow: var(--shout-halo);
  animation: dg-flicker 0.14s steps(2) infinite;
}
#td-combo.pop { animation: combo-pop 0.2s ease-out; }
#td-combo[data-tier="5"].pop { animation: combo-pop 0.2s ease-out, dg-flicker 0.14s steps(2) infinite; }
@keyframes combo-pop {
  45% { transform: translateX(-50%) scale(1.4); }
}
/* the secondary-fire pad blinks red through its lockout */
.tfire.overheat { animation: dg-flicker 0.3s steps(2) infinite; }
@media (prefers-reduced-motion: reduce) {
  #td-combo[data-tier="5"], .tfire.overheat { animation: none; }
}

/* --- end-of-wave SitRep --------------------------------------------------
   The downtime between waves earns a recap: field-manual green, histogram
   bars in each enemy's own tint, kill tempo as a block sparkline. */
#td-sitrep {
  position: absolute;
  top: 32%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 8;
  width: min(86vw, 360px);
  padding: 12px 18px 10px;
  border: 1px solid rgba(110, 255, 140, 0.5);
  border-radius: 8px;
  background: rgba(3, 14, 7, 0.92);
  color: #86ff9e;
  font: 12px/1.7 ui-monospace, SFMono-Regular, Menlo, monospace;
  text-shadow: 0 0 6px rgba(110, 255, 140, 0.4);
  cursor: pointer;
}
#td-sitrep.hidden { display: none; }
.sr-head {
  font-weight: bold;
  letter-spacing: 0.22em;
  color: #b8ffca;
  margin-bottom: 4px;
}
.sr-line b { color: #d6ffdf; }
.sr-row {
  display: flex;
  align-items: center;
  gap: 8px;
  line-height: 1.5;
}
.sr-name { width: 72px; color: #6fe387; overflow: hidden; text-overflow: ellipsis; }
.sr-track { flex: 1; height: 8px; background: rgba(110, 255, 140, 0.12); border-radius: 2px; overflow: hidden; }
.sr-bar { display: block; height: 100%; border-radius: 2px; }
.sr-n { width: 24px; text-align: right; color: #d6ffdf; }
.sr-spark { letter-spacing: 0.08em; color: #b8ffca; }
.sr-leak { color: #ff5340; }
.sr-foot {
  margin-top: 6px;
  text-align: center;
  letter-spacing: 0.2em;
  color: #6fe387;
  animation: pulse-soft 1.6s ease-in-out infinite;
}

/* --- the HUD instrument panel (TD only; the shared rule keeps position) --
   Three brightness tiers for three reading distances: vitals bright and
   big, resources mid, meta/objectives dim. Same console family as the
   field manual and SitRep, but subtler — this one never goes away. */
#td-stats.hud-panel {
  white-space: normal;
  line-height: 1.55;
  background: rgba(3, 14, 7, 0.62);
  border: 1px solid rgba(110, 255, 140, 0.28);
  border-radius: 8px;
  padding: 7px 12px 8px;
  color: #6fe387;
  text-shadow: 0 0 6px rgba(110, 255, 140, 0.22);
  min-width: 232px;
  pointer-events: none;
}
#td-stats.hud-panel .hud-meta { font-size: 11px; color: #4fae63; letter-spacing: 0.06em; }
#td-stats.hud-panel .hud-meta b { color: #b8ffca; font-size: 13px; }
.hud-vitals { font-size: 14px; letter-spacing: 0.04em; }
.hud-lbl { font-size: 10px; color: #4fae63; letter-spacing: 0.14em; }
.hp-heart { color: #ff6a88; text-shadow: 0 0 8px rgba(255, 106, 136, 0.5); }

/* THE SHIELD, on the alert line. Three states, and the one that has to read
   fastest is the SEAM — two seconds of cooldown between charges, which is
   what a chained shield is timed against. Up is cyan and carries a draining
   underline; cooling and idle are grey, and the rack pips sit beside all
   three so the player never has to open a menu to know what they can spend. */
.sh-bar { position: relative; padding: 0 .25em; color: #59c8ff; }
.sh-bar::after { content: ''; position: absolute; left: 0; bottom: -2px; height: 2px;
  width: calc(var(--sh) * 100%); background: #59c8ff; }
.sh-cool { color: #6d7f8c; }
.sh-idle { color: #6d7f8c; }
.sh-pip { font-style: normal; color: #59c8ff; letter-spacing: -1px; opacity: .8; }
/* the KEY, when you have a charge to spend. The readout is otherwise grey
   while idle, which is right — but a grey row gives no hint that pressing
   something would change it. */
.sh-ready { color: #0b1a24; background: #59c8ff; border-radius: 3px;
  padding: 0 4px; font-weight: 700; }
/* the solar array's reserve beside the rack (src/fx/shield-array.js): grey
   while it waits, cyan while it feeds, dim and struck once it is dry */
.sh-array { color: #6d7f8c; margin-left: .2em; }
.sh-array.on { color: #59c8ff; text-shadow: 0 0 6px rgba(89, 200, 255, .55); }
.sh-array.dry { color: #8a98a3; border: 1px solid #4a5660; border-radius: 3px; padding: 0 3px; }
/* the ram premium over the hull (src/fx/ram-readout.js): +N kg ×M rises and
   fades; the shout type, heating with the combo tier like #td-combo */
.ram-float-layer { position: absolute; inset: 0; pointer-events: none; z-index: 12; overflow: hidden; }
.ram-float { position: absolute; transform: translate(-50%, -100%); white-space: nowrap;
  font-family: var(--font-shout); font-weight: bold; letter-spacing: var(--shout-track);
  font-size: calc(var(--shout-unit) * 0.6); color: var(--p-green); text-shadow: var(--shout-halo); }
.ram-float.hidden { display: none; }
.ram-float.go { animation: ram-rise 1.1s ease-out forwards; }
.ram-float[data-tier="1"], .ram-float[data-tier="2"] { color: var(--p-green-hot); font-size: calc(var(--shout-unit) * 0.72); }
.ram-float[data-tier="3"], .ram-float[data-tier="4"], .ram-float[data-tier="5"] { color: var(--p-white); font-size: calc(var(--shout-unit) * 0.84); }
@keyframes ram-rise {
  0% { opacity: 0; transform: translate(-50%, -60%) scale(1.35); }
  12% { opacity: 1; transform: translate(-50%, -100%) scale(1); }
  70% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -320%); }
}
@media (prefers-reduced-motion: reduce) { .ram-float.go { animation: none; } }
.sentry-pilot-mode .ram-float-layer { display: none; }
.hp-dim { color: rgba(255, 106, 136, 0.22); }
.hp-you { color: #9fdcff; margin-left: 8px; text-shadow: 0 0 7px rgba(159, 220, 255, 0.4); }
.hp-ammo { color: #eef4ff; margin-left: 8px; }
.hp-ammo.out { color: #ff5340; text-shadow: 0 0 8px rgba(255, 83, 64, 0.6); }
.hud-res { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
.hud-wave { color: #d6ffdf; letter-spacing: 0.06em; }
.hud-wave b { font-size: 16px; }
.hud-obj { font-size: 11px; color: #4fae63; }
/* the one white row: an alert is the brightest thing the tube can do */
.hud-alert { font-size: 11px; color: var(--p-white); text-shadow: 0 0 8px rgba(234, 255, 242, 0.5); letter-spacing: 0.08em; }

/* mode row joins the console family. Two clusters encoded by tint alone —
   camera controls cyan (the tank's color), system controls green — so the
   grouping needs no extra markup and no extra width. */
#tab-td .tc-util button {
  background: rgba(3, 14, 7, 0.72);
  color: #86ff9e;
  border: 1px solid rgba(110, 255, 140, 0.4);
  border-radius: 6px;
  text-shadow: 0 0 6px rgba(110, 255, 140, 0.3);
  opacity: 1;
}
#tab-td .tc-util button:active { background: rgba(110, 255, 140, 0.25); color: #eafff0; }
#tab-td #td-pad-tank, #tab-td #td-pad-orbit, #tab-td #td-pad-ctrheart, #tab-td #td-pad-ctrtank {
  color: #9fdcff;
  border-color: rgba(159, 220, 255, 0.4);
  text-shadow: 0 0 6px rgba(159, 220, 255, 0.3);
}
/* the AUTO button IS the who-is-driving line now: amber while engaged,
   wearing the directive's name */
#tab-td #td-pad-dir.engaged {
  color: #ffb000;
  border-color: rgba(255, 176, 0, 0.55);
  text-shadow: 0 0 7px rgba(255, 176, 0, 0.45);
}
/* the NEXT WAVE chip joins the same idiom */
#td-next {
  background: rgba(3, 14, 7, 0.85);
  border: 1px solid rgba(110, 255, 140, 0.35);
  color: #86ff9e;
  border-radius: 8px;
}
#td-hint { color: #4fae63; }
@media (pointer: coarse), (max-width: 700px) {
  /* the panel must hold FIVE rows (alerts included) inside the band above
     the NEXT WAVE chip at top+148 — tightened, not truncated */
  #td-stats.hud-panel { line-height: 1.38; padding: 5px 10px 6px; min-width: 0; }
  #td-stats.hud-panel .hud-vitals { font-size: 12px; }
  #td-stats.hud-panel .hud-wave b { font-size: 13px; }
}

/* --- the announcement lane -----------------------------------------------
   SitRep, NEW THREAT / NEW TOWER cards, and toasts move OFF the sightline
   (operator: "somewhere where there's room, not in your face center").
   One lane so they never fight each other: desktop = the left rail below
   the HUD panel (right of the throttle column, clear of the radar);
   phone = the right column below the radar, above the fire cluster.
   Callouts, the combo counter, and the danger warning STAY centred —
   momentary flair is supposed to be in your face. */
#td-sitrep, #td-wave, #td-toast, #td-tower {
  left: 80px;
  right: auto;
  top: 202px; /* fully clear of the launch console's dashed frame */
  bottom: auto;
  transform: none;
}
@media (pointer: coarse), (max-width: 700px) {
  #td-sitrep, #td-wave, #td-toast, #td-tower {
    left: auto;
    right: max(8px, env(safe-area-inset-right));
    /* below the launch console's slot, above the fire cluster */
    top: calc(max(8px, env(safe-area-inset-top)) + 306px);
    width: min(74vw, 300px);
    min-width: 0;
    max-width: 74vw;
  }
  /* compact on the phone so the tallest card clears the fire button at
     every width this media block covers */
  #td-sitrep { font-size: 11px; line-height: 1.5; padding: 8px 12px 8px; }
}

/* --- phone columns v2 ----------------------------------------------------
   The launch console folds VERTICAL under the radar (right column) — it
   was a 296px bar eating the left column. The announcement lane moves to
   the left column below the NEXT chip; the hack button docks under the
   console. Verified by the rectangle probe, as ever. */
@media (pointer: coarse), (max-width: 700px) {
  #td-launch {
    left: auto;
    right: max(8px, env(safe-area-inset-right));
    top: calc(max(8px, env(safe-area-inset-top)) + 218px);
    width: 128px;
    transform: none;
  }
  #td-launch.caution-frame,
  #td-launch .caution-frame { flex-direction: column; }
  #td-launch { flex-direction: column; min-height: 0; gap: 6px; padding: 7px 6px; display: flex; }
  #td-launch-readout { gap: 2px; }
  #td-launch-readout .status { font-size: 10px; }
  #td-launch-readout .target { font-size: 9px; }
  .launch-button { width: 86px; height: 42px; }

  #td-sitrep, #td-wave, #td-toast, #td-tower {
    left: max(12px, env(safe-area-inset-left));
    right: auto;
    top: calc(max(8px, env(safe-area-inset-top)) + 204px);
    width: min(62vw, 260px);
  }
  /* the tallest card must die above the throttle (y~430) */
  #td-sitrep { font-size: 10px; line-height: 1.42; padding: 7px 10px 7px; }
  #td-sitrep .sr-name { width: 56px; }
  #td-sitrep .sr-row { line-height: 1.35; }
}

/* dev-log build line: tiny copy affordances, not a clickable label */
.log-copy {
  font: 11px ui-monospace, Menlo, monospace;
  background: transparent;
  border: 1px solid rgba(160, 180, 200, 0.3);
  border-radius: 4px;
  color: inherit;
  cursor: pointer;
  padding: 1px 5px;
  margin-left: 4px;
  vertical-align: 1px;
}
.log-copy:hover { border-color: rgba(160, 180, 200, 0.7); }

/* --- the SIM tab: console-family batch runner ---------------------------- */
#tab-sim { color: #86ff9e; font: 13px/1.7 ui-monospace, Menlo, monospace; }
#sim-panel { max-width: 860px; margin: 60px auto 0; padding: 0 16px; }
.sim-head { font-weight: bold; letter-spacing: 0.2em; color: #b8ffca; margin-bottom: 12px; }
.sim-controls { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 12px; }
.sim-controls label { color: #6fe387; display: flex; gap: 6px; align-items: center; }
.sim-controls select, .sim-controls input {
  background: #06140a; color: #b8ffca; border: 1px solid rgba(110, 255, 140, 0.4);
  border-radius: 5px; padding: 4px 8px; font: inherit;
}
.sim-controls input[type="number"] { width: 64px; }
#sim-run, #sim-stop {
  font: bold 12px ui-monospace, Menlo, monospace; letter-spacing: 0.1em;
  color: #08140a; background: #86ff9e; border: 0; border-radius: 5px;
  padding: 6px 14px; cursor: pointer;
}
#sim-stop { background: #ff5340; color: #140806; }
#sim-status { color: #6fe387; }
#sim-agg { margin: 8px 0 10px; color: #d6ffdf; }
#sim-agg b { color: #ffffff; }
#sim-table { border-collapse: collapse; width: 100%; }
#sim-table th, #sim-table td {
  text-align: right; padding: 3px 10px; border-bottom: 1px solid rgba(110, 255, 140, 0.15);
}
#sim-table th { color: #6fe387; font-weight: normal; letter-spacing: 0.08em; }
#sim-table td:nth-child(2) { text-transform: uppercase; }
tr.sim-win td:nth-child(2) { color: #86ff9e; }
tr.sim-loss td:nth-child(2) { color: #ff5340; }
tr.sim-timeout td:nth-child(2), tr.sim-stalled td:nth-child(2) { color: #ffb000; }
#sim-frame {
  /* the run is visible but small — a picture-in-picture of the game
     playing itself; the DATA is the product, the view is a courtesy */
  position: fixed; right: 12px; bottom: 12px;
  width: 360px; height: 240px; border: 1px solid rgba(110, 255, 140, 0.4);
  border-radius: 6px; background: #000;
}

/* --- the last transmission (game over) ----------------------------------- */
.go-verdict {
  font-family: var(--font-banner);
  font-size: calc(var(--banner-unit) * 0.90);
  font-weight: bold;
  letter-spacing: var(--shout-track);
  color: var(--p-white);
  text-shadow: var(--banner-halo);
  margin: 8px 0 2px;
  text-transform: uppercase;
}
.go-verdict.best { color: #86ff9e; text-shadow: 0 0 14px rgba(134, 255, 158, 0.6); }
.go-reason { color: #ff5340; margin-bottom: 8px; }
.go-grid { display: grid; gap: 3px; margin-bottom: 8px; text-align: left; }
.go-grid b { font-size: 15px; }
.go-best {
  font-style: normal; font-weight: bold; color: #08140a; background: #86ff9e;
  border-radius: 4px; padding: 1px 6px; margin-left: 4px;
  animation: pulse-soft 1.4s ease-in-out infinite;
}
.go-killers { margin: 8px 0 4px; color: #ff8f80; letter-spacing: 0.08em; }
.go-killer {
  width: 44px; height: 44px; vertical-align: middle; margin-left: 6px;
  border: 1px solid rgba(255, 83, 64, 0.5); border-radius: 6px;
  background: #0a0508;
}
#td-msg .sr-row, #tab-td .msg .sr-row { text-align: left; }

/* --- phone HUD v3: SMALLER, and never everything at once -----------------
   Field screenshot: every element at desktop scale, all visible at the
   same time — unplayable. Everything compacts; the mute button goes
   (the phone has a mute switch); and while the tower radial is open the
   transient furniture (cards, console, chip, prompt) clears out. */
@media (pointer: coarse), (max-width: 700px) {
  /* the mode row: compact, and no mute */
  #td-pad-sound { display: none; }
  /* SIX buttons do not fit 390px in one line — TANK and ORBIT replaced a
     single cycling camera button, which is worth the width but not worth
     pushing the row off the screen (it pushed ORBIT and the mute clean off
     the right edge). Wrap it, centred, and keep it clear of the tab bar. */
  #tab-td .tc-util {
    gap: 5px;
    flex-wrap: wrap;
    justify-content: center;
    max-width: calc(100vw - 96px);
  }
  #tab-td .tc-util button {
    padding: 4px 8px;
    font-size: 10px;
    letter-spacing: 0.05em;
    border-radius: 5px;
  }
  /* instrument panel: tighter still, clear of the (smaller) radar */
  #td-stats.hud-panel {
    font-size: 10px;
    line-height: 1.32;
    padding: 4px 9px 5px;
    max-width: calc(100vw - 165px);
  }
  #td-stats.hud-panel .hud-meta { font-size: 9px; }
  #td-stats.hud-panel .hud-meta b { font-size: 11px; }
  #td-stats.hud-panel .hud-vitals { font-size: 11px; }
  #td-stats.hud-panel .hud-wave b { font-size: 12px; }
  #td-stats.hud-panel .hud-obj, #td-stats.hud-panel .hud-alert { font-size: 9px; }
  #td-stats.hud-panel .hud-rank svg { width: 14px; height: 14px; vertical-align: -3px; }
  /* NEXT chip: up under the shorter panel, smaller */
  #td-next { top: calc(max(8px, env(safe-area-inset-top)) + 124px); font-size: 10px; padding: 4px 8px 5px; }
  .nx-head { margin-bottom: 2px; }
  /* announce cards + toasts: narrow and quiet */
  #td-sitrep, #td-wave, #td-toast, #td-tower {
    top: calc(max(8px, env(safe-area-inset-top)) + 166px);
    width: min(56vw, 232px);
    font-size: 10px;
    padding: 7px 10px;
  }
  /* the banner's hierarchy INVERTS on a phone — the label grows, the name
     shrinks — so these are ratio overrides, not a scale. Sizes stay
     relative; only the proportions change. */
  #td-wave .wave-num, #td-tower .wave-num { --r: 0.62; }
  #td-wave .wave-name, #td-tower .wave-name { --r: 0.57; }
  #td-wave .wave-role, #td-tower .wave-role { font-size: calc(var(--ui-unit) * 0.77); }
  #td-wave .wave-icon, #td-tower .wave-icon { width: 40px; height: 40px; }
  /* launch console: under the smaller radar, smaller itself */
  #td-launch { top: calc(max(8px, env(safe-area-inset-top)) + 172px); width: 106px; padding: 5px 5px; gap: 4px; }
  .safety .switch-img { width: 22px; height: 36px; }
  #td-launch-readout .status { font-size: 9px; }
  #td-launch-readout .target { font-size: 8px; }
  .launch-button { width: 62px; height: 34px; }
  .launch-button .latin { font-size: 7px; }

  /* the wheel: whole radial scaled down uniformly */
  #td-shop { transform: scale(0.76); }
  .shop-note { display: none; }
  /* throttle lever: smaller footprint */
  #td-throttle { transform: scale(0.82); transform-origin: bottom left; }
  /* the radial is a FOCUSED task: transient furniture clears out under it */
  #tab-td.shopping #td-sitrep, #tab-td.shopping #td-wave, #tab-td.shopping #td-toast, #tab-td.shopping #td-tower, #tab-td.shopping #td-launch, #tab-td.shopping #td-next {
    display: none;
  }
}

/* --- the codex (lore panel) ---------------------------------------------- */
#units-lorepanel {
  position: fixed;
  inset: 0;
  z-index: 65;
  background: rgba(2, 8, 4, 0.96);
  color: #a8e8b6;
  font: 13px/1.75 ui-monospace, SFMono-Regular, Menlo, monospace;
  display: flex;
  flex-direction: column;
}
#units-lorepanel.hidden { display: none; }
.lp-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 18px;
  border-bottom: 1px solid rgba(110, 255, 140, 0.35);
}
.lp-title { font-weight: bold; letter-spacing: 0.2em; color: #b8ffca; }
#lore-body {
  overflow-y: auto;
  padding: 14px 20px 40px;
  max-width: 760px;
  margin: 0 auto;
  width: 100%;
  box-sizing: border-box;
}
.lp-section {
  margin: 22px 0 6px;
  font-weight: bold;
  letter-spacing: 0.24em;
  color: #6fe387;
  border-bottom: 1px dashed rgba(110, 255, 140, 0.3);
  padding-bottom: 3px;
}
.lp-entry { margin-bottom: 16px; }
.lp-name { font-weight: bold; color: #d6ffdf; letter-spacing: 0.08em; }
.lp-entry p { margin: 3px 0; }
.lp-visual { color: #5aa86e; font-style: italic; font-size: 12px; }

/* the killer dossier on the last transmission */
.go-kinfo { margin-top: 5px; font-size: 12px; color: #cfe2ee; min-height: 18px; }
.go-noram { color: #ff5340; font-weight: bold; }
.go-snafu {
  font-family: var(--font-banner);
  font-size: calc(var(--banner-unit) * 0.62);
  font-weight: bold;
  letter-spacing: 0.22em;
  text-shadow: var(--banner-halo);
  color: var(--p-blue);
  margin-top: 4px;
}

/* --- THE VIEWER ON A PHONE ----------------------------------------------
   Operator: some of the unit viewer renders offscreen. Everything down here
   is one row too wide for 390px — three group tabs, six option buttons, a
   note written for a desktop measure, and the prev/next arrows pushed out
   past the edge by all of it. The chrome is a bottom-anchored strip, so
   nothing clipped here scrolls back into view: it is simply gone.

   Width clause AND coarse pointer, because a coarse-pointer rule cannot be
   exercised by any desktop browser, headless included — the pads on the TD
   tab learned that the hard way. */
@media (pointer: coarse), (max-width: 700px) {
  /* The rows are children of a CENTRED COLUMN, so they take their content
     width and simply overhang the screen — max-width alone did not engage
     the wrap. Giving each row the container's width is what makes flex-wrap
     mean anything here. */
  #units-chrome { padding: 10px 8px 12px; gap: 6px; max-width: 100vw; }
  #units-stage, #units-opts, #units-groups, #units-sounds {
    width: 100%;
    max-width: calc(100vw - 16px);
    box-sizing: border-box;
  }
  #units-stage { gap: 6px; }
  #units-groups button, #units-opts button, #units-stage button {
    padding: 6px 9px;
    font-size: 11px;
    letter-spacing: 0.04em;
  }
  /* the arrows are the one thing that must never wrap or shrink out: they
     are how you move through the roster at all */
  #units-prev, #units-next { flex: 0 0 auto; padding: 6px 11px; }
  #units-id { padding: 0 2px; }
  #units-note {
    font-size: 10px;
    min-height: 0;
    max-width: 100%;
  }
  #units-sounds { flex-wrap: wrap; justify-content: center; }
  /* six option buttons wrap to two rows rather than running off the side */
  #units-opts { gap: 6px; row-gap: 6px; }
}

/* --- THE LAUNCH CONSOLE ON A PHONE --------------------------------------
   It was taking a third of the width and a fifth of the height of a 430pt
   screen for a control used a handful of times a run (operator screenshot).
   On a phone it is a SWITCH, and nothing else, until it is armed — then the
   readout and the launch key appear, because that is the only moment they
   have anything to say. Desktop is untouched: there is room there, and the
   console being always-legible is the point of it.

   Width clause AND coarse pointer, and placed at the END of the file, both
   for reasons this stylesheet has already learned the hard way. */
@media (pointer: coarse), (max-width: 700px) {
  /* The mode row wraps to TWO lines on a phone since TANK and ORBIT replaced
     the single camera button, so it now reaches y=55 where the HUD and the
     radar both start at 50 — measured as a 5px graze on each. Both move down
     to clear it. A hairline overlap on a HUD is the kind of thing that reads
     as sloppy long before anyone can say why. */
  #tab-td #td-stats.hud-panel { top: calc(max(8px, env(safe-area-inset-top)) + 52px); }
  #tab-td .minimap { top: calc(max(8px, env(safe-area-inset-top)) + 52px); }

  #td-launch {
    width: auto;
    padding: 3px 6px;
    gap: 0;
    border-width: 3px;
    border-image-slice: 3;
  }
  #td-launch .caution-frame,
  #td-launch { min-height: 0; }
  /* the parts that only matter once you have committed */
  #td-launch #td-launch-readout,
  #td-launch #td-launch-btn { display: none; }
  /* ARMED, the console grows AND the minimap is promoted to a radar at the
     same moment — measured, they collided by 107x92px. The console drops
     below the radar for as long as it is armed, and goes back up to its
     switch-sized perch when it is not. */
  #td-launch.armed {
    gap: 8px;
    padding: 4px 8px;
    top: calc(max(8px, env(safe-area-inset-top)) + 278px);
  }
  #td-launch.armed #td-launch-readout { display: block; }
  #td-launch.armed #td-launch-btn { display: flex; }

  .safety { width: 26px; gap: 0; }
  .safety .switch-img { width: 20px; height: 32px; }
  .safety .lbl-on, .safety .lbl-off { font-size: 7px; letter-spacing: 0.06em; }

  .launch-button { width: 54px; height: 42px; border-width: 2px; }
  .launch-button .kanji { font-size: 15px; }
  .launch-button .latin { font-size: 7px; }
  #td-launch-readout .status { font-size: 8px; }
  #td-launch-readout .target { font-size: 8px; }
}

/* --- PORTAL BENCH (temporary sidequest tab) ------------------------------
   Same shape as the beam lab: the canvas fills the tab, lil-gui docks right,
   and the readout sits bottom-left where the GUI cannot cover it. */
#tab-portal { position: relative; width: 100%; height: 100%; overflow: hidden; }
#portal-app { position: absolute; inset: 0; }
#portal-app canvas { display: block; width: 100%; height: 100%; }
#portal-gui { position: absolute; top: 8px; right: 8px; z-index: 6; }
#portal-hud {
  position: absolute; left: 12px; bottom: 12px; z-index: 6;
  font: 12px/1.55 ui-monospace, Menlo, monospace;
  color: var(--p-green-hot, #9f9);
  background: rgba(4, 14, 9, 0.82);
  border: 1px solid var(--p-green-dim, #274);
  border-left: 3px solid var(--p-green, #3f7);
  border-radius: 8px;
  padding: 8px 12px;
  pointer-events: none;
  max-width: min(560px, calc(100vw - 24px));
}
#portal-hud b { color: var(--p-white, #fff); }

/* --- TD frame readout (2026-09-02) ------------------------------------
   Top-left, tucked beside the ≡ button so it never sits on the score.
   Monospace and dim on purpose: an instrument, not a HUD element. --- */
#td-perf {
  position: fixed;
  /* UNDER the score panel, not beside the ≡: the ≡ does not sit where its
     own rule says (a badge holds that slot), and a readout in the toolbar
     row overlapped it. The score panel's bottom edge is a stable anchor. */
  top: 168px;
  left: 14px;
  z-index: 61;
  font: 600 11px ui-monospace, Menlo, monospace;
  letter-spacing: 0.06em;
  color: #9fdcff;
  background: rgba(8, 12, 20, 0.72);
  border: 1px solid rgba(0, 208, 255, 0.28);
  border-radius: 6px;
  padding: 4px 8px;
  pointer-events: none;
  white-space: nowrap;
}
#td-perf b { color: #e8fbff; font-weight: 700; }
#td-perf.hidden { display: none; }

/* --- TD variables MODAL (2026-09-02) ----------------------------------
   The lil-gui panel used to be one tall column pinned to the right edge —
   thirty controls and four folders, scrolled by feel. It is re-homed into a
   centred modal: a nav column of PAGES on the left, one page at a time on
   the right. The lil-gui elements are MOVED, not rebuilt, so every slider's
   behaviour is untouched. Opens from DEV · Tuning in the navigation shell, closes on ×
   or Esc, and — unlike the old column — is allowed to open while playing,
   because a toggle you cannot use mid-run is not a toggle. --- */
#td-vars {
  position: fixed;
  inset: 0;
  z-index: 62;
  display: grid;
  grid-template-columns: 168px minmax(0, 1fr);
  width: min(760px, calc(100vw - 24px));
  height: min(78vh, 640px);
  margin: auto;
  background: rgba(10, 14, 22, 0.96);
  border: 1px solid rgba(0, 208, 255, 0.35);
  border-radius: 10px;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.6);
  overflow: hidden;
  backdrop-filter: blur(4px);
}
#td-vars.hidden, body:not(.vars-open) #td-vars { display: none; }
#td-vars .vars-nav {
  display: flex; flex-direction: column; gap: 2px;
  padding: 44px 8px 8px;
  border-right: 1px solid rgba(0, 208, 255, 0.18);
  overflow-y: auto;
}
#td-vars .vars-nav button {
  text-align: left;
  font: 600 12px ui-monospace, Menlo, monospace;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #9aa7bd;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  padding: 8px 10px;
  cursor: pointer;
}
#td-vars .vars-nav button:hover { color: #e8fbff; border-color: rgba(0, 208, 255, 0.25); }
#td-vars .vars-nav button.active {
  color: #e8fbff;
  background: rgba(0, 208, 255, 0.12);
  border-color: rgba(0, 208, 255, 0.45);
}
#td-vars .vars-body { overflow-y: auto; padding: 44px 12px 12px; }
#td-vars .vars-page { display: none; }
#td-vars .vars-page.active { display: block; }
#td-vars .vars-close {
  position: absolute; top: 8px; right: 10px;
  width: 30px; height: 30px;
  font: 700 18px ui-monospace, Menlo, monospace;
  color: #9fdcff; background: transparent;
  border: 1px solid rgba(0, 208, 255, 0.3); border-radius: 6px;
  cursor: pointer;
}
#td-vars .vars-close:hover { color: #fff; border-color: #00d0ff; }
/* the moved lil-gui pieces: static, full width, no root title, and never
   hidden by the column rules that apply to the OLD placement */
#td-vars .lil-gui, #td-vars .lil-gui.root {
  display: block !important;
  position: static !important;
  width: 100% !important;
  max-height: none !important;
}
#td-vars .lil-gui.root > .title { display: none; }
#td-vars .vars-page > .lil-gui > .title { display: none; }
#td-vars .vars-page > .lil-gui > .children { display: block !important; }
@media (max-width: 560px) {
  #td-vars { grid-template-columns: 1fr; height: min(84vh, 640px); }
  #td-vars .vars-nav { flex-direction: row; flex-wrap: wrap; padding-top: 42px; border-right: 0;
    border-bottom: 1px solid rgba(0, 208, 255, 0.18); }
  #td-vars .vars-body { padding-top: 10px; }
}

/* --- TD after-action board (2026-09-02) --------------------------------
   Six small windows in the debrief, read like an analyst's desk: roster,
   attribution, tempo, records, the best-strike replay, defence. Then one
   button. The verdict and the orders come only after it. --- */
#td-msg .dbf-title { font-size: 15px; margin-bottom: 8px; }
#td-msg .dbf-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
  width: min(640px, 84vw); margin: 0 auto 10px; text-align: left;
}
#td-msg .dbf-win {
  border: 1px solid rgba(0, 208, 255, 0.25); border-radius: 6px;
  padding: 6px 8px; background: rgba(0, 20, 30, 0.35);
  font-size: 11px; line-height: 1.35; min-height: 92px;
}
#td-msg .dbf-head { font-size: 10px; letter-spacing: 0.14em; color: #9fdcff; margin-bottom: 5px; }
#td-msg .dbf-row { display: flex; align-items: center; gap: 6px; margin: 2px 0; }
#td-msg .dbf-icon { width: 20px; height: 20px; flex: 0 0 20px; image-rendering: pixelated; }
#td-msg .dbf-name { flex: 0 0 64px; text-transform: lowercase; }
#td-msg .dbf-tag { font-size: 9px; letter-spacing: 0.1em; color: #7a8598; flex: 0 0 34px; }
#td-msg .dbf-n { margin-left: auto; font-size: 13px; }
#td-msg .dbf-track { flex: 1; height: 6px; background: rgba(255,255,255,0.06); border-radius: 3px; overflow: hidden; }
#td-msg .dbf-bar { display: block; height: 100%; border-radius: 3px; }
#td-msg .dbf-dim { color: #7a8598; font-size: 10px; margin-top: 4px; }
#td-msg .dbf-svg { width: 100%; height: 64px; display: block; }
#td-msg .dbf-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 8px; }
#td-msg .dbf-tile b { display: block; font-size: 15px; }
#td-msg .dbf-record b { color: #9fdcff; }
#td-msg .dbf-record--rainbow b {
  color: #9fdcff;
  background: linear-gradient(100deg, #ff7979, #ffcb70, #edee80, #80efb3, #83d8ff, #bfa0ff, #ff9fd5);
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  width: fit-content;
}
#td-msg .dbf-tile span { display: block; font-size: 9.5px; color: #9aa7bd; letter-spacing: 0.06em; }
#td-msg .dbf-replay { width: 100%; height: 120px; display: block; border-radius: 4px; background: #020a10; }
@media (max-width: 560px) { #td-msg .dbf-grid { grid-template-columns: 1fr; } }

/* --- TD campaign debrief (2026-09-02) ------------------------------------
   Every round on one table, then the retro decision. --- */
#td-msg .camp-wrap { width: min(680px, 86vw); margin: 6px auto 10px; overflow-x: auto; }
#td-msg .camp-table { width: 100%; border-collapse: collapse; font-size: 11px; text-align: right; }
#td-msg .camp-table th { font-size: 9.5px; letter-spacing: 0.12em; color: #9fdcff; font-weight: 600;
  padding: 3px 6px; border-bottom: 1px solid rgba(0, 208, 255, 0.35); text-transform: uppercase; }
#td-msg .camp-table td { padding: 3px 6px; border-bottom: 1px solid rgba(0, 208, 255, 0.12); }
#td-msg .camp-table td:first-child, #td-msg .camp-table th:first-child { text-align: left; }
#td-msg .camp-table tfoot td { border-top: 1px solid rgba(0, 208, 255, 0.45); border-bottom: 0;
  color: #e8fbff; font-weight: 700; }
#td-msg .camp-table b { font-size: 12px; }
#td-msg .coin {
  margin: 12px auto 2px; font: 700 15px ui-monospace, Menlo, monospace; letter-spacing: 0.22em;
  color: #ffd75a; text-shadow: 0 0 10px rgba(255, 215, 90, 0.55);
}
#td-msg .coin b { color: #fff3c4; }
#td-msg .continue {
  margin: 2px auto 8px; font: 800 22px ui-monospace, Menlo, monospace; letter-spacing: 0.3em;
  color: #86ff9e; text-shadow: 0 0 14px rgba(134, 255, 158, 0.6);
  animation: td-blink 1.1s steps(2, start) infinite;
}
@keyframes td-blink { to { visibility: hidden; } }
#td-msg button[disabled] { opacity: 0.45; cursor: default; }

/* --- THE MOBILE SHELL (docs/MOBILE-PORT-PLAN.md) ----------------------------
   Scoped under body.mobile-shell so desktop is byte-identical. Phase 2 cut:
   DRIVE keeps fire, plasma, radar, the mode switch and the menu; the steering
   pads and throttle are gone because the tank is COMMANDED by tap. -------- */
#td-rotate, #mob-mode { display: none; }
body.mobile-shell #mob-mode {
  display: block; position: fixed; z-index: 61;
  right: max(14px, env(safe-area-inset-right)); top: max(12px, env(safe-area-inset-top));
  min-width: 96px; height: 48px; padding: 0 16px;
  font: 800 15px ui-monospace, Menlo, monospace; letter-spacing: 0.18em;
  color: #e8fbff; background: rgba(0, 208, 255, 0.16);
  border: 2px solid rgba(0, 208, 255, 0.7); border-radius: 12px;
  touch-action: manipulation;
}
body.mobile-shell.mob-build #mob-mode { background: rgba(134, 255, 158, 0.16); border-color: rgba(134, 255, 158, 0.75); }
body.mobile-shell #td-pad-left, body.mobile-shell #td-pad-right, body.mobile-shell #td-throttle,
body.mobile-shell #td-hint,   /* key legend: there are no keys */
body.mobile-shell #td-pad-dir, body.mobile-shell #td-pad-tank, body.mobile-shell #td-pad-orbit,
body.mobile-shell #td-pad-map, body.mobile-shell #td-pad-ctrheart, body.mobile-shell #td-pad-ctrtank,
body.mobile-shell #td-pad-sound { display: none !important; }
body.mobile-shell #td-pad-fire, body.mobile-shell #td-pad-laser { min-width: 64px; min-height: 64px; font-size: 22px; }

body.mobile-shell #td-app { touch-action: none; }
/* Phase 4 — HUD SUBTRACTION PER MODE, AND CAPTIONS.
   DRIVE shows what a sub-second read needs: vitals + resources, the wave
   clock, a compact launch console, the radar, the two thumbs. Score, the
   objectives row and the drones' status lines belong to BUILD, the calm
   mode, which in turn drops the thumbs, the console and the radar (orbit
   shows the whole planet). Isao's channel and the announcement lane leave
   the sightline: both anchor to the bottom edge beside the radar, as
   captions — nothing to dismiss, nothing over the board. Sizes are set here
   rather than inherited from the coarse-pointer block so the headless ruler
   (which is never coarse) measures what a phone gets. */
body.mobile-shell #td-stats.hud-panel { line-height: 1.38; padding: 5px 10px 6px; min-width: 0; }
body.mobile-shell #td-stats.hud-panel .hud-meta { font-size: 9px; }
body.mobile-shell #td-stats.hud-panel .hud-meta b { font-size: 11px; }
body.mobile-shell #td-stats.hud-panel .hud-vitals { font-size: 12px; }
body.mobile-shell #td-stats.hud-panel .hud-wave b { font-size: 13px; }
body.mobile-shell #td-stats.hud-panel .hud-obj, body.mobile-shell #td-stats.hud-panel .hud-alert { font-size: 9px; }
body.mobile-shell:not(.mob-build) #td-stats.hud-panel .hud-meta,
body.mobile-shell:not(.mob-build) #td-stats.hud-panel .hud-obj { display: none; }
/* the launch console, compact: safety + status + button. The target is read
   on the board, not off a readout. */
body.mobile-shell #td-launch { top: 52px; width: auto; min-height: 0; padding: 4px 8px; gap: 8px; }
body.mobile-shell #td-launch-readout .target { display: none; }
body.mobile-shell #td-launch-readout .status { font-size: 9px; }
body.mobile-shell.mob-build #td-pad-fire, body.mobile-shell.mob-build #td-pad-laser, body.mobile-shell.mob-build #td-pad-shield, body.mobile-shell.mob-build #td-launch, body.mobile-shell.mob-build #tab-td .minimap { display: none !important; }
/* captions: Isao beside the radar on the bottom edge, the lane just above him */
body.mobile-shell #td-brief {
  left: 124px; bottom: max(8px, env(safe-area-inset-bottom));
  width: min(520px, calc(100vw - 340px)); padding: 6px 10px; gap: 8px;
  /* a transform-free entrance: wave-pop starts at translateX(-50%), and CSS
     animations do not advance under headless virtual time, so the ruler
     measured the brief frozen on its first frame, centred on its own left
     edge. Fade in, and the rectangle is the rectangle. */
  transform: none; animation: td-caption-pop 0.25s ease-out;
}
@keyframes td-caption-pop { from { opacity: 0; } to { opacity: 1; } }
body.mobile-shell #td-brief-face { width: 56px; height: 56px; }
/* the tower radial on glass: every target ≥ 48px (plan §2.2), the note big
   enough to read at arm's length */
body.mobile-shell #td-shop .shop-close { width: 48px; height: 48px; }
body.mobile-shell #td-shop .shop-note { font-size: 12px; }
/* Phase 5 — MODALS FOR THE PHONE. Measured at 844x303 (the ruler's
   landscape) before any of this: the message box clips at 295px because
   its overflow is hidden for the hologram sweep, so every button below the
   fold — proceed, the orders, another planet — was unreachable. On the
   shell the box scrolls as a whole, fills the height, and every button is
   a thumb target. The sweep scrolls with the content; nobody will miss it. */
body.mobile-shell #td-msg {
  box-sizing: border-box;   /* max-height is the OUTER box, or padding pushes it off-screen */
  overflow-y: auto; max-height: calc(100dvh - 12px); max-width: 94vw;
  padding: 10px 16px 12px; font-size: 13px; line-height: 1.55;
}
body.mobile-shell #td-msg button { min-height: 44px; padding: 8px 16px; font-size: 13px; touch-action: manipulation; }
body.mobile-shell #td-msg .msg-glenemy, body.mobile-shell #td-msg .msg-glfriend { min-height: 40px; }
body.mobile-shell #td-msg .dbf-win { min-height: 0; }
body.mobile-shell #td-msg .dbf-grid { width: min(700px, 90vw); }

body.mobile-shell #td-vars {
  width: calc(100vw - 12px); height: calc(100dvh - 12px);
  grid-template-columns: 124px minmax(0, 1fr);
}
body.mobile-shell #td-vars .vars-nav { padding: 40px 6px 8px; }
body.mobile-shell #td-vars .vars-nav button { padding: 12px 10px; font-size: 11px; }
body.mobile-shell #td-vars .vars-body { padding: 40px 10px 10px; }
body.mobile-shell #td-vars button { min-height: 44px; }
body.mobile-shell #td-vars .vars-close { width: 44px; height: 44px; }
/* THE SHELL PINS ITS GEOMETRY against the phone's own coarse-pointer blocks.
   Every rule above was measured headless, where (pointer: coarse) is false;
   on a real phone the coarse blocks moved the radar to the top-right at 240px
   and the launch console under it, and neither the ruler nor the screenshots
   could see it (the operator's phone could). Two ids + the class outranks
   every selector in those blocks, and each rule states the WHOLE geometry —
   a property left unsaid is a property the phone block still owns.
   ?coarse=1 makes the ruler apply those blocks, so this is measured now. */
/* the radar TOP-RIGHT under the switch (operator: bottom-left is under the
   fingers); the thumbs pinned to the bottom-right corner so the two never
   meet, even at the ruler's 303 px height */
body.mobile-shell #tab-td .minimap { left: auto; bottom: auto; right: 14px; top: 68px; }
body.mobile-shell #tab-td #td-pad-fire { right: 14px; bottom: 14px; left: auto; top: auto; }
body.mobile-shell #tab-td #td-pad-laser { right: 92px; bottom: 22px; left: auto; top: auto; }

/* the floating stick */
#td-stick { display: none; }
body.mobile-shell #td-stick { display: block; position: fixed; z-index: 40; width: 0; height: 0; pointer-events: none; }
body.mobile-shell #td-stick.hidden { display: none; }
body.mobile-shell #td-stick .stick-ring { position: absolute; left: -72px; top: -72px; width: 144px; height: 144px;
  border-radius: 50%; border: 2px solid rgba(159, 220, 255, 0.55); background: rgba(20, 32, 44, 0.25); }
body.mobile-shell #td-stick .stick-knob { position: absolute; left: -26px; top: -26px; width: 52px; height: 52px;
  border-radius: 50%; background: rgba(159, 220, 255, 0.8); box-shadow: 0 0 14px rgba(0, 208, 255, 0.6); }
body.mobile-shell #tab-td #td-stats.hud-panel { top: 58px; left: 14px; right: auto; max-width: none; font-size: 11px; line-height: 1.38; }   /* 46 predates the navigation shell's bar, whose row ends at 54 */
body.mobile-shell #tab-td #td-next {
  top: 8px; left: 50%; right: auto; transform: translateX(-50%);
  max-width: 96vw; text-align: center; font-size: 11px; padding: 5px 10px 6px;
}
body.mobile-shell #tab-td #td-launch, body.mobile-shell #tab-td #td-launch.caution-frame {
  top: 52px; left: 50%; right: auto; transform: translateX(-50%);
  width: auto; display: flex; flex-direction: row; min-height: 0; padding: 4px 8px; gap: 8px;
}
body.mobile-shell #tab-td #td-launch .launch-button { width: 64px; height: 44px; }
/* the console collapses to its safety switch until the strike is armed —
   the phone block does this under (pointer: coarse); the shell does it
   itself, so the ruler and the phone see the same 48px console */
body.mobile-shell #tab-td #td-launch-readout { display: none; }
body.mobile-shell #tab-td #td-launch:not(.armed) .launch-button { display: none; }

/* the coach / tutorial banner: right of the launch console, above the
   thumbs — the only slot at the ruler's 303px height that touches neither
   the console (ends 136) nor the caption lane (starts ~160) */
body.mobile-shell #tab-td #td-tut {
  top: 52px; left: auto; right: 116px; transform: none;
  max-width: 250px; text-align: left; font-size: 12px; line-height: 1.4; padding: 8px 12px;
}
body.mobile-shell #tab-td #td-brief { left: 124px; right: auto; top: auto; bottom: max(8px, env(safe-area-inset-bottom)); width: min(520px, calc(100vw - 340px)); }
body.mobile-shell #tab-td #td-sitrep, body.mobile-shell #tab-td #td-toast {
  left: 124px; right: auto; top: auto; bottom: 100px; transform: none; width: min(300px, 40vw);
}
/* the two ANNOUNCEMENT CARDS (new threat, new tower) go top-left under the
   HUD panel, stacked, out of the play area: on a 418 px phone the bottom
   lane put them mid-frame, over the tank (operator's photo), and they fire
   on the same wave so they sat on each other. Sprite and text compact. */
body.mobile-shell #tab-td #td-wave, body.mobile-shell #tab-td #td-tower {
  left: 14px; right: auto; top: 122px; bottom: auto; transform: none; animation: td-caption-pop 0.25s ease-out;
  width: auto; min-width: 0; max-width: 62vw; padding: 4px 10px; font-size: 10px; line-height: 1.3;
  display: flex; align-items: center; justify-content: flex-start; gap: 8px; text-align: left; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
body.mobile-shell #tab-td #td-wave.hidden, body.mobile-shell #tab-td #td-tower.hidden { display: none; }
body.mobile-shell #tab-td #td-wave:not(.hidden) ~ #td-tower { top: 168px; }
body.mobile-shell.mob-build #tab-td #td-wave, body.mobile-shell.mob-build #tab-td #td-tower { top: 156px; }
body.mobile-shell.mob-build #tab-td #td-wave:not(.hidden) ~ #td-tower { top: 202px; }
/* !important: the sprite renderer writes its 96px onto the canvas's own style */
body.mobile-shell #tab-td #td-wave .wave-sprite { width: 34px !important; height: 34px !important; flex: 0 0 34px; margin: 0; }
body.mobile-shell #tab-td #td-tower .wave-icon { width: 28px; height: 28px; flex: 0 0 28px; margin: 0; }
body.mobile-shell #tab-td #td-wave .wave-ram { font-size: 10px; letter-spacing: 0.04em; margin: 0; }
body.mobile-shell #tab-td #td-wave .wave-num, body.mobile-shell #tab-td #td-tower .wave-num { font-size: 11px; margin: 0; }
body.mobile-shell #tab-td #td-wave .wave-name, body.mobile-shell #tab-td #td-tower .wave-name,
body.mobile-shell #tab-td #td-wave .wave-role, body.mobile-shell #tab-td #td-tower .wave-role,
body.mobile-shell #tab-td #td-wave .wave-ram { display: inline; font-size: 10px; margin: 0; }
body.mobile-shell #tab-td #td-wave .wave-name::before, body.mobile-shell #tab-td #td-tower .wave-name::before,
body.mobile-shell #tab-td #td-wave .wave-role::before, body.mobile-shell #tab-td #td-tower .wave-role::before,
body.mobile-shell #tab-td #td-wave .wave-ram::before { content: ' · '; opacity: 0.6; }
/* the vertical budget at 844x303 (the ruler's landscape) is the top band
   (~150), a lane (~55) and Isao (~85): Isao is wide and small so his long
   lines wrap to two, not three */
body.mobile-shell #td-brief-line { font-size: 11px; line-height: 1.35; margin: 0; }
body.mobile-shell #td-brief-title { font-size: 9px; }
body.mobile-shell #td-brief-foot { font-size: 10px; }
body.mobile-shell #td-sitrep, body.mobile-shell #td-wave, body.mobile-shell #td-toast, body.mobile-shell #td-tower {
  left: 124px; right: auto; top: auto; bottom: 100px; transform: none; animation: td-caption-pop 0.25s ease-out;
  width: min(300px, 40vw); min-width: 0; max-width: 40vw; font-size: 11px; padding: 5px 10px 6px;
}
body.mobile-shell #td-toast .wave-num, body.mobile-shell #td-wave .wave-num, body.mobile-shell #td-tower .wave-num { font-size: 12px; line-height: 1.25; }
body.mobile-shell #td-toast .wave-role, body.mobile-shell #td-wave .wave-name, body.mobile-shell #td-tower .wave-name { font-size: 10px; }
/* PORTRAIT PLAYS (2026-09-18: the friends open the live link holding the phone upright, and a wall saying "rotate" is a
   closed door). The rotate prompt is a passive hint for a few seconds, never over the controls, never in the way of a thumb;
   the layout below gives the upright phone its own bands instead of the landscape geometry squeezed into 390 px. */
@media (orientation: portrait) {
  body.mobile-shell #td-rotate {
    display: flex; position: fixed; left: 50%; top: 48%; transform: translate(-50%, -50%); z-index: 44; pointer-events: none;
    flex-direction: row; align-items: center; gap: 10px; padding: 8px 14px;
    background: rgba(8, 12, 16, 0.78); border: 1px solid rgba(223, 232, 238, 0.35); border-radius: 10px;
    color: #dfe8ee; text-align: left; font: 600 11px ui-monospace, Menlo, monospace; letter-spacing: 0.08em;
    animation: td-rotate-fade 6s ease-in forwards;
  }
  body.mobile-shell #td-rotate div { font-size: 26px; line-height: 1; color: #ffffff; animation: td-rotate-hint 2.4s ease-in-out 3; }
  body.mobile-shell #td-rotate p { margin: 0; line-height: 1.5; max-width: 60vw; }
}
@keyframes td-rotate-hint { 0%, 100% { transform: rotate(0); } 50% { transform: rotate(-90deg); } }
@keyframes td-rotate-fade { 0%, 75% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } }

/* --- THE PHONE'S CHROME, BOTH WAYS UP (2026-09-18, scripts/browser-test.mjs --phone) --------------------------------
   Measured on an emulated 390x844 touch phone. The navigation shell's bar and the mode switch both claimed the top-right
   corner; the views strip at 64 px sat on the pad's laser button; the seat's CSS hid the pad's fire button, which is the
   seat's trigger on touch; the gun buttons were 30 px; the sentry footer ran off both edges; the monitor and SOL-82's HOLD
   sat on the strip. One band plan, every control at least 44 px. */
#sector-card { top: 18%; left: 50%; transform: translateX(-50%); }   /* the sector brief's card (src/fx/sector-run.js); the phone moves it under its top band below */
body.mobile-shell #shell-bar { right: auto; left: max(8px, env(safe-area-inset-left)); max-width: calc(100vw - 134px); }   /* the mode switch keeps the right corner */
body.mobile-shell #shell-bar .shell-toggle { flex: 0 0 auto; }
body.mobile-shell #shell-bar .shell-place { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.mobile-shell #shell-bar #build-tag { display: none; }   /* the build tag is a developer's line; a phone has no room for it */
body.mobile-shell #tab-td #td-next { max-width: 40vw; }
body.mobile-shell #story-views { flex-wrap: wrap; justify-content: center; max-width: calc(100vw - 24px); box-sizing: border-box; }
body.mobile-shell #story-views button { min-height: 44px; min-width: 44px; padding: 7px 10px; touch-action: manipulation; }
body.mobile-shell #story-views [data-view="map"] { display: none; }   /* BUILD is the orbit view on the phone; MAP would be the same button twice */
/* the sector line (src/fx/sector-run.js hudLine) is the one .hud-obj a phone must keep while driving: the sector, its breaches, the gate */
body.mobile-shell #td-stats.hud-panel .hud-obj.hud-sector { display: block; }
body.mobile-shell #sentry-pilot { right: 124px; }   /* the panel is full-bleed on a desktop; on a phone its header ran under the radar in the top-right corner */
body.mobile-shell #sentry-pilot .pilot-guns { flex-wrap: wrap; }
body.mobile-shell #sentry-pilot button { min-height: 44px; min-width: 44px; touch-action: manipulation; }
body.mobile-shell #sentry-pilot footer { position: static; transform: none; display: block; white-space: normal; font-size: 11px; line-height: 1.4; padding: 6px 9px; max-width: 60vw; }   /* in the panel's own column, not a bar over the pad */
/* the seat's trigger on touch is the pad's fire button (src/sentry-pilot.js): app.css hides every .tfire under the seat, this shows the one it needs */
body.mobile-shell #tab-td.sentry-pilot-mode #td-pad-fire { display: block !important; }
body.mobile-shell #tab-td.sentry-pilot-mode #mob-mode, body.mobile-shell #tab-td.laser-seat #mob-mode { display: none; }   /* no BUILD from a seat */
body.mobile-shell #laser-seat-keys button { min-height: 44px; min-width: 44px; touch-action: manipulation; }
body.mobile-shell #td-brief-next { min-width: 44px; min-height: 44px; touch-action: manipulation; }   /* the comms card's NEXT was 34x20 */
body.mobile-shell #gunship-briefing button, body.mobile-shell #sol82-briefing button { min-height: 44px; min-width: 44px; padding: 8px 14px; touch-action: manipulation; }   /* the briefings' NEXT and SKIP were 65x30 */
@media (orientation: portrait) {
  /* the bottom band, up from the edge: the pad row (fire, laser, shield in one row at the right), the strip, Isao */
  body.mobile-shell #tab-td #td-pad-shield { right: 170px; bottom: 22px; left: auto; top: auto; width: 56px; height: 56px; min-width: 56px; min-height: 56px; font-size: 20px; }
  /* a centred fixed strip shrink-wraps to the half-screen right of its anchor and wraps one button a row: span the width instead */
  body.mobile-shell #story-views { bottom: 100px; left: 12px; right: 12px; transform: none; width: auto; max-width: none; gap: 5px; padding: 5px 6px; }
  body.mobile-shell #story-views button { font-size: 11px; padding: 7px 8px; }   /* TANK · GUNSHIP · 12 S LEFT · SOL-82 OVERHEAD in one row at 390 px */
  body.mobile-shell #sector-card { left: 16px; right: 16px; transform: none; max-width: none; }
  body.mobile-shell #tab-td #td-brief { left: 12px; right: 12px; width: auto; bottom: 156px; }   /* above the strip, under the tank (third person puts it near y 530) */
  body.mobile-shell #tab-td #td-sitrep, body.mobile-shell #tab-td #td-toast { left: 12px; right: auto; bottom: 256px; width: min(300px, calc(100vw - 24px)); max-width: none; }
  /* the offer keeps the right of the announcement row; the coach gets its own full-width row under it (at 46vw the
     offer's card sat on the coach's second line and ate the word "or" — artifacts/browser/phone-opening.png) */
  body.mobile-shell #skip-tutorial { right: 12px; bottom: auto; top: 188px; gap: 4px; padding: 5px 6px; font-size: 10px; }   /* fixed text on one status line and one row of 44 px buttons, ending above the coach's row at 268 px: the phone drops the pips and the next chapter's name */
  body.mobile-shell #skip-tutorial .tut-pips, body.mobile-shell #skip-tutorial [data-next] span { display: none; }
  body.mobile-shell #skip-tutorial button { min-height: 44px; min-width: 64px; }
  body.mobile-shell #tab-td #td-tut { top: 268px; left: 12px; right: 12px; max-width: none; text-align: center; }
  /* the top band: HUD under the shell's toggle on the left, the radar under the mode switch on the right */
  body.mobile-shell #tab-td #td-stats.hud-panel { top: 68px; max-width: calc(100vw - min(240px, 32vmin) - 40px); }
  body.mobile-shell #tab-td #td-wave, body.mobile-shell #tab-td #td-tower { top: 160px; max-width: 40vw; }   /* under a HUD that now carries the sector's two lines, left of the offer */
  body.mobile-shell #tab-td #td-wave:not(.hidden) ~ #td-tower { top: 206px; }
  body.mobile-shell #sector-card { top: 352px; }   /* under the announcement row and the coach (the desktop card sits at 18%) */
  /* the gunship seat: the ground-truth monitor above the strip, the readout beside it */
  body.mobile-shell #tab-td.gunship-seat #story-monitor { width: min(40vw, 640px); bottom: 160px; right: 12px; }
  body.mobile-shell #gunship-hud .ro { top: auto; bottom: 160px; left: 12px; right: auto; width: 40vw; min-width: 0; max-width: none; padding: 8px 9px; font-size: 10px; }   /* bottom-left beside the monitor: the reticle keeps the centre */
  /* SOL-82: the lens is the top of the frame, the panel sits under it, HOLD and TANK above the strip */
  /* portrait puts the lens across the top (src/fx/laser-scope.js scopeRect), so the inset HUD's OPTICS, LASER and PASS
     blocks (src/fx/laser-inset-hud.js) hang under it to about y 515: the panel goes below them, left of the keys */
  body.mobile-shell #laser-seat { top: auto; bottom: 206px; left: 12px; max-width: 52vw; }   /* above a strip that runs two rows while a pass is overhead */
  body.mobile-shell #laser-seat-keys { bottom: 220px; right: 12px; }   /* beside the panel, above a strip that runs two rows while a pass is overhead */
}
@media (orientation: landscape) {
  /* 844x390 is 87 px shorter than the landscape this chrome was ruled for, and the bottom lane no longer holds both Isao
     and the strip: the strip takes the free middle of the top row, between the shell's bar and the mode switch. */
  body.mobile-shell #story-views { top: max(8px, env(safe-area-inset-top)); bottom: auto; left: 50%; right: auto; transform: translateX(-50%); max-width: calc(100vw - 460px); }
  body.mobile-shell #tab-td #td-tut { top: 76px; }   /* out from under the strip's new row */
}

/* THE METAL LAB (metal-tab.js): the beam lab's chrome, same rules */
#metal-app { position: absolute; inset: 0; }
#metal-app canvas { display: block; width: 100%; height: 100%; }
#metal-copy {
  /* LEFT, not right: the lil-gui panel owns the right edge and swallowed it
     there — the button existed and could not be clicked. */
  position: absolute; left: 12px; bottom: 40px; z-index: 7;
  width: 40px; height: 40px; padding: 0;
  font: 600 18px ui-monospace, Menlo, monospace; line-height: 1;
  color: #9fdcff; background: rgba(12, 26, 38, 0.85);
  border: 1px solid #2b6b96; border-radius: 8px; cursor: pointer;
}
/* THE DEEP LINK, one class for every lab. A tuning session's answer is a
   set of numbers, and until this button there was no way to carry one out of
   the tab except by reading it off a screenshot. It sits ABOVE the preset-copy
   button where a lab has one, so the left edge reads as a stack of tools and
   the two never move relative to each other. */
.lab-link {
  position: absolute; left: 12px; bottom: 88px; z-index: 7;
  width: 40px; height: 40px; padding: 0;
  font: 600 17px ui-monospace, Menlo, monospace; line-height: 1;
  color: #9fdcff; background: rgba(12, 26, 38, 0.85);
  border: 1px solid #2b6b96; border-radius: 8px; cursor: pointer;
}
.lab-link:hover { background: rgba(22, 50, 74, 0.95); color: #dff4ff; }
.lab-link:active { transform: translateY(1px); }
@media (pointer: coarse), (max-width: 700px) {
  /* the phone puts the lil-gui drawer across the bottom 46vh, so the bottom
     edge is not a place a button can live; top-left is the one free corner
     (the gear owns top-right, the preset copy sits beside it) */
  .lab-link { left: 12px; bottom: auto; top: calc(env(safe-area-inset-top, 0px) + 112px); width: 44px; height: 44px; }
}
#metal-copy:hover { background: rgba(22, 50, 74, 0.95); color: #dff4ff; }
#metal-copy:active { transform: translateY(1px); }
#metal-hud.flash { color: #66ff88; text-shadow: 0 0 10px rgba(102, 255, 136, 0.6); }
#metal-hud {
  position: absolute; left: 12px; bottom: 12px; z-index: 6;
  font: 600 12px ui-monospace, Menlo, monospace; letter-spacing: 0.08em;
  color: #9fdcff; text-shadow: 0 0 8px rgba(0, 180, 255, 0.5);
  pointer-events: none;
  /* the drop-off readout is three lines: where the beam ends, what it
     burned, and what it never reached. Without this they run off the edge
     as one line and the last two are simply not there. */
  white-space: pre-line; max-width: 60vw; line-height: 1.5;
}

/* THE METAL LAB on a phone (operator, 2026-09-04: "on mobile it's
   impossible to tweak"): the panel becomes a bottom sheet — full width,
   under half the screen, scrollable, with rows a thumb can hit — and the
   gear hides it so the cast can be seen. Coarse is paired with a width
   clause because no desktop browser can exercise pointer:coarse. */
#metal-gear { position: absolute; right: 12px; top: 12px; z-index: 8; width: 44px; height: 44px; border-radius: 10px;
  border: 1px solid rgba(120, 200, 255, 0.35); background: rgba(10, 22, 34, 0.85); color: #9fdcff; font-size: 20px; display: none; }
#tab-metal.panel-hidden .lil-gui.root { display: none !important; }
@media (pointer: coarse), (max-width: 700px) {
  #metal-gear { display: block; }
  #tab-metal .lil-gui.root {
    display: block !important;   /* the shell's coarse block hides every .lil-gui; this one is the point of the tab */
    position: fixed !important; left: 0 !important; right: 0 !important; bottom: 0 !important; top: auto !important;
    width: 100% !important; max-height: 46vh; overflow-y: auto; -webkit-overflow-scrolling: touch;
    --widget-height: 36px; --font-size: 15px; --input-font-size: 15px; --name-width: 44%; --slider-knob-width: 6px;
    border-radius: 12px 12px 0 0; box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.5);
  }
  #tab-metal .lil-gui.root .controller { touch-action: pan-y; }
  #metal-hud { bottom: auto; top: 104px; left: 12px; right: 12px; font-size: 11px; }   /* under the navigation shell's bar */
  #metal-copy { top: 12px; bottom: auto; left: auto; right: 64px; }
}

/* the preset, on screen: selectable text, tap to close */
#metal-src { position: absolute; left: 12px; right: 12px; top: 120px; z-index: 9; max-height: 60vh; overflow: auto; margin: 0;
  padding: 12px 14px; border-radius: 10px; border: 1px solid rgba(120, 200, 255, 0.35); background: rgba(6, 14, 22, 0.94);
  color: #dff4ff; font: 12px ui-monospace, Menlo, monospace; white-space: pre-wrap; -webkit-user-select: text; user-select: text; }
#metal-src.hidden { display: none; }
@media (pointer: coarse), (max-width: 700px) {
  /* under the phone's status bar and the tab bar: the buttons sat behind the clock */
  #metal-gear { top: calc(env(safe-area-inset-top, 0px) + 112px); right: 12px; }
  #metal-copy { top: calc(env(safe-area-inset-top, 0px) + 112px); right: 64px; left: auto; bottom: auto; }
  #metal-hud { top: calc(env(safe-area-inset-top, 0px) + 168px); }
  #metal-src { top: calc(env(safe-area-inset-top, 0px) + 168px); font-size: 13px; }
}

/* RE-CENTRE. Orbit controls on a phone are one finger to turn and two to
   zoom, and a few seconds of that puts the range somewhere behind you with
   no way back — the same problem the board solved with its two CTR buttons,
   so this borrows their name. Left edge on desktop above the deep link;
   under the gear on a phone, because the panel drawer owns the bottom. */
.lab-center {
  position: absolute; left: 12px; bottom: 136px; z-index: 7;
  height: 40px; padding: 0 10px;
  font: 700 12px ui-monospace, Menlo, monospace; letter-spacing: 0.08em; line-height: 1;
  color: #9fdcff; background: rgba(12, 26, 38, 0.85);
  border: 1px solid #2b6b96; border-radius: 8px; cursor: pointer;
}
.lab-center:hover { background: rgba(22, 50, 74, 0.95); color: #dff4ff; }
.lab-center:active { transform: translateY(1px); }
@media (pointer: coarse), (max-width: 700px) {
  .lab-center { left: auto; right: 12px; bottom: auto; top: calc(env(safe-area-inset-top, 0px) + 164px); height: 44px; font-size: 13px; }
}

/* the SENTRY RANGE, on the astronaut study's furniture — same stage, same
   gear button, same two-line HUD. A lab that looks like the other labs is a
   lab nobody has to learn. */
#sentry-app { position: absolute; inset: 0; }
#sentry-hud {
  position: absolute; left: 12px; bottom: 12px; z-index: 6;
  font: 12px/1.5 ui-monospace, Menlo, monospace; color: #9fdcff;
  text-shadow: 0 0 8px rgba(0, 0, 0, 0.9); white-space: pre-line;
  /* the readout runs long once a wall and a battery are in it; let it wrap
     inside its own box rather than spilling off the left edge */
  max-width: min(74vw, calc(100vw - 24px)); overflow-wrap: anywhere; pointer-events: none;
}
#sentry-gear { position: absolute; right: 12px; top: 12px; z-index: 8; width: 44px; height: 44px; border-radius: 10px;
  border: 1px solid rgba(120, 200, 255, 0.35); background: rgba(10, 22, 34, 0.85); color: #9fdcff; font-size: 20px; display: none; }
#tab-sentry.panel-hidden .lil-gui.root { display: none !important; }
@media (pointer: coarse), (max-width: 700px) {
  #sentry-gear { display: block; top: calc(env(safe-area-inset-top, 0px) + 112px); right: 12px; }
  #sentry-hud { bottom: auto; top: calc(env(safe-area-inset-top, 0px) + 168px); left: 12px; right: 12px; font-size: 11px; max-width: none; }
  #tab-sentry .lil-gui.root { display: block !important; position: fixed !important; left: 0 !important; right: 0 !important; bottom: 0 !important; top: auto !important;
    width: 100% !important; max-height: 46vh; overflow-y: auto; --widget-height: 36px; --font-size: 15px; --input-font-size: 15px; --name-width: 44%;
    border-radius: 12px 12px 0 0; }
}

#td-perf { pointer-events: auto; max-width: min(640px, calc(100vw - 24px)); }
#td-perf details { white-space: normal; max-height: 35vh; overflow: auto; }
#td-perf summary, #td-perf button { cursor: pointer; }
#td-perf button { margin-top: 6px; }
#td-perf textarea { display: block; width: 95%; height: 8em; }

/* THE STORY LAB (labs/story-tab.js): planet, clearing, arrival */
#story-app { position: absolute; inset: 0; }
#story-app canvas { display: block; width: 100%; height: 100%; }
#story-hud {
  position: absolute; left: 12px; bottom: 12px; z-index: 7; pointer-events: none;
  display: flex; flex-direction: column; gap: 8px;
  font: 12px ui-monospace, Menlo, monospace; color: #9fdcff;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.9);
}
#story-hud .story-lines { display: flex; flex-direction: column; gap: 2px; max-width: 60ch; }
#story-hud .story-lines b { letter-spacing: 0.22em; font-weight: 600; }
#story-hud .story-keys { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; pointer-events: auto; }
#story-hud .story-keys button {
  font: 600 12px ui-monospace, Menlo, monospace; color: #9fdcff; cursor: pointer;
  background: rgba(12, 26, 38, 0.85); border: 1px solid #2b6b96; border-radius: 8px; padding: 8px 12px; min-height: 40px;
}
#story-hud .story-keys button:hover { background: rgba(22, 50, 74, 0.95); color: #dff4ff; }
#story-hud .story-keys span { color: #6a94b0; }
#story-hud .story-keys a.story-back { color: #9fdcff; text-decoration: none; border: 1px solid #2b6b96; border-radius: 8px; padding: 8px 12px; min-height: 40px; display: inline-flex; align-items: center; background: rgba(12, 26, 38, 0.85); }
#story-hud .story-keys button.on { background: rgba(40, 90, 120, 0.95); color: #dff4ff; border-color: #6fc3ff; }
/* ISAO'S COMMS ARE A SMALL TERMINAL ON THE LEFT, never a banner (owner, recurring: 2026-09-13 "much much too long") — the same card size as the game's #td-brief */
#story-hud #story-comms { align-self: flex-start; box-sizing: border-box; width: fit-content; max-width: min(280px, calc(100vw - 24px)); font: 600 12px ui-monospace, Menlo, monospace; letter-spacing: 0.06em; line-height: 1.35; border: 1px solid; border-left-width: 3px; border-radius: 8px; padding: 6px 10px; background: rgba(4, 14, 9, 0.9); text-shadow: 0 0 8px currentColor; white-space: normal; }
/* the story's view strip, unlocked after the first wave: TANK / SENTRY / MAP */
#story-views { position: fixed; bottom: 64px; left: 50%; transform: translateX(-50%); display: flex; gap: 6px; padding: 6px 8px; background: rgba(8, 14, 20, 0.85); border: 1px solid #2b6b96; border-radius: 10px; z-index: 40; }
#story-views button { font: 700 12px ui-monospace, Menlo, monospace; letter-spacing: 0.08em; color: #9fdcff; background: rgba(12, 26, 38, 0.85); border: 1px solid #2b6b96; border-radius: 8px; padding: 7px 12px; cursor: pointer; }
#story-views button.active { background: rgba(40, 90, 120, 0.95); color: #dff4ff; border-color: #6fc3ff; }
#story-views button.gunship:disabled { opacity: 0.45; cursor: default; }
#story-views button.gunship.live { border-color: #ffb43d; color: #ffd48a; }
#story-views button.gunship.ready { border-color: #6fe6ff; color: #dff4ff; }
/* SOL-82 (src/fx/laser-station.js): monochrome, counting down; lit white through a pass; amber only when its energy runs low */
#story-views button.laser { color: #c9d3da; border-color: #56646c; }
#story-views button.laser:disabled { opacity: 0.6; cursor: default; }
#story-views button.laser.live { color: #ffffff; border-color: #dfe8ee; background: rgba(44, 54, 62, 0.95); }
#story-views button.laser.warn { color: #ffd48a; border-color: #ffb020; }
/* the seat: the tank's HUD, the radar and the feed make way for the scope (bottom left) and its panel */
#tab-td.laser-seat .minimap, #tab-td.laser-seat #story-monitor, #tab-td.laser-seat #td-strikecam { display: none !important; }
#tab-td.laser-seat #td-perf { top: auto !important; bottom: 12px; left: auto !important; right: 14px; }
#laser-seat { position: absolute; left: 16px; top: 58px; z-index: 80; max-width: min(92vw, 560px); pointer-events: none; color: #dfe8ee; font: 12px ui-monospace, Menlo, monospace; letter-spacing: 0.06em; text-shadow: 0 1px 3px #000; }
#laser-seat header { font-size: 17px; letter-spacing: 2px; }
#laser-seat header small { font-size: 10px; margin-left: 12px; color: #9aa8b0; }
#laser-seat .laser-seat-line { display: flex; gap: 12px; align-items: center; margin-top: 6px; }
#laser-seat .laser-seat-bar { display: inline-block; width: 140px; height: 6px; border: 1px solid #6d7b85; background: #0b0f12; }
#laser-seat .laser-seat-bar i { display: block; height: 100%; width: 0; background: #dfe8ee; }
#laser-seat .laser-seat-bar i.low { background: #ffb020; }
#laser-seat .laser-seat-warn { margin-top: 6px; color: #ffb020; font-weight: 700; }
#laser-seat footer { margin-top: 6px; color: #9aa8b0; font-size: 11px; }
#laser-seat-keys { position: absolute; right: 18px; bottom: 124px; z-index: 80; display: flex; gap: 10px; align-items: center; }
#laser-seat-keys button { font: 700 12px ui-monospace, Menlo, monospace; letter-spacing: 0.08em; color: #dfe8ee; background: rgba(8, 12, 16, 0.85); border: 1px solid #6d7b85; border-radius: 8px; padding: 8px 12px; cursor: pointer; touch-action: none; user-select: none; }
#laser-seat-keys [data-hold] { width: 84px; height: 84px; border-radius: 50%; }
#laser-seat-keys [data-hold].on { color: #ffffff; border-color: #ff3b1f; background: rgba(60, 16, 10, 0.9); }
/* the build tag, joined to the navigation shell top right, for debugging which release is in front of you */
#build-tag { font: 600 10px ui-monospace, Menlo, monospace; letter-spacing: 0.06em; color: #6fa8c8; background: rgba(8, 14, 20, 0.7); border: 1px solid #234c68; border-radius: 6px; padding: 2px 6px; pointer-events: none; }
/* the story's spotting monitor: a second small render riding behind a guided round */
#story-monitor { position: fixed; right: 12px; bottom: 64px; width: 224px; height: 140px; border: 1px solid #2b6b96; border-radius: 8px; background: rgba(8, 14, 20, 0.5); z-index: 40; pointer-events: none; }
/* GROUND TRUTH · IMPACT, bigger in the gunship seat (owner, 2026-09-15): the impact is the payoff, so the monitor grows to
   about 640 x 400 on a wide screen, 16:10, bottom right; its height stops short of the readout above it on shorter screens.
   The Talon and missile feeds keep the small box. The monitor renders at the box's own aspect, so nothing stretches. */
.gunship-seat #story-monitor { width: min(44vw, 640px); height: auto; aspect-ratio: 16 / 10; max-height: max(160px, calc(100vh - 540px)); border-color: #3a8fbf; }
.gunship-seat #story-monitor .head { font-size: 11px; }   /* the seat's root carries .gunship-seat: the game's #tab-td, the boss lab's stage */
#story-monitor .feed { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: inherit; }
#story-monitor .head { position: absolute; z-index: 1; left: 6px; top: 4px; font: 600 10px ui-monospace, Menlo, monospace; letter-spacing: 0.08em; color: #9fdcff; text-shadow: 0 0 4px #000; }
/* the story's fire-control scope for a guided mount: the Sniper workshop's idiom, one panel and a reticle */
#gunship-hud { position: fixed; inset: 0; z-index: 38; pointer-events: none; --phosphor: #cfeee9; --cyan: #5fe6d6; }
#gunship-hud .reticle { position: absolute; inset: 0; width: 100%; height: 100%; }
#gunship-hud .ro { position: absolute; right: 18px; top: 200px; width: 27vw; max-width: 250px; min-width: 160px; padding: 10px 12px; font: 11px ui-monospace, Menlo, monospace; color: var(--phosphor); border-right: 1px solid rgba(95, 230, 214, .28); border-top: 1px solid rgba(95, 230, 214, .28); background: linear-gradient(180deg, rgba(8, 16, 15, .66), rgba(8, 16, 15, .30)); }
#gunship-hud .r-head { font-size: 10px; letter-spacing: .24em; text-transform: uppercase; color: var(--cyan); margin-bottom: 4px; display: flex; justify-content: space-between; align-items: baseline; }
#gunship-hud .field { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; border-bottom: 1px dotted rgba(95, 230, 214, .18); }
#gunship-hud .field .k { color: #6fa39c; letter-spacing: .12em; text-transform: uppercase; font-size: 10px; }
#gunship-hud .field .v { color: var(--phosphor); }
#gunship-hud .hot { color: #ffb43d; }
#gunship-hud .field.bar { align-items: center; }
#gunship-hud .meter { flex: 1; height: 6px; margin-left: 10px; background: rgba(95, 230, 214, .12); border: 1px solid rgba(95, 230, 214, .28); }
#gunship-hud .meter i { display: block; height: 100%; width: 0; background: #5fe6d6; transition: width .1s linear; }
#gunship-hud .meter.hot i { background: #ff3b2f; }
#story-scope { position: fixed; inset: 0; z-index: 38; pointer-events: none; }   /* the whole optic is one canvas now (src/fx/story-scope.js): the reticle and its telemetry blocks are drawn, not styled */
#story-scope .reticle { position: absolute; inset: 0; width: 100%; height: 100%; }
/* ISAO's study: the SYNTHETIC LEARNING x58 terminal as a modal */
#synthetic-modal[hidden] { display: none; }
/* SOL-82's briefing (src/fx/sol82-briefing.js): the gunship briefing's sheet, with labels riding the model on the view */
#sol82-briefing[hidden] { display: none; }
#sol82-briefing { position: fixed; inset: 0; z-index: 70; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.84); font-family: ui-monospace, Menlo, monospace; }
#sol82-briefing .sheet { width: min(1100px, calc(100vw - 32px)); max-height: calc(100vh - 32px); overflow: auto; background: #07141a; border: 1px solid #2b6b96; padding: 14px 18px 12px; box-shadow: 0 0 30px rgba(95, 230, 214, 0.12); }
#sol82-briefing header { border-bottom: 1px solid #1f4a5e; padding-bottom: 6px; margin-bottom: 10px; }
#sol82-briefing h1 { margin: 0; color: #62d7ff; font-size: 14px; letter-spacing: 2px; text-shadow: 0 0 6px rgba(98, 215, 255, 0.4); }
#sol82-briefing .sub { margin: 4px 0 0; color: #6fa8c8; font-size: 10px; letter-spacing: 1px; }
#sol82-briefing .body { display: grid; grid-template-columns: 3fr 2fr; gap: 14px; align-items: stretch; }
#sol82-briefing .view { position: relative; }
#sol82-briefing canvas { width: 100%; aspect-ratio: 3 / 2; display: block; background: #0b1e26; border: 1px solid #1f4a5e; cursor: pointer; }
#sol82-briefing .labels { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
#sol82-briefing .labels svg { position: absolute; inset: 0; width: 100%; height: 100%; }
#sol82-briefing .labels line { stroke: rgba(98, 215, 255, 0.6); stroke-width: 1; }
#sol82-briefing .labels circle { fill: #62d7ff; }
#sol82-briefing .label { position: absolute; transform: translate(-50%, -50%); padding: 2px 6px; border: 1px solid rgba(98, 215, 255, 0.55); background: rgba(4, 12, 16, 0.82); color: #bfeaff; font-size: 10px; letter-spacing: 0.12em; white-space: nowrap; }
#sol82-briefing .text h2 { margin: 6px 0 10px; color: #ffd48a; font-size: 13px; letter-spacing: .18em; }
#sol82-briefing .text ul { margin: 0; padding: 0 0 0 14px; color: #dff4ff; font-size: 13px; line-height: 1.6; }
#sol82-briefing .dots { margin-top: 12px; display: flex; gap: 6px; }
#sol82-briefing .dots i { width: 18px; height: 3px; background: #1f4a5e; }
#sol82-briefing .dots i.on { background: #62d7ff; }
#sol82-briefing footer { display: flex; justify-content: space-between; align-items: center; margin-top: 10px; padding-top: 8px; border-top: 1px solid #1f4a5e; color: #6fa8c8; font-size: 10px; letter-spacing: 1px; }
#sol82-briefing button { font: inherit; color: #dff4ff; background: #0b1e26; border: 1px solid #2b6b96; padding: 4px 12px; cursor: pointer; letter-spacing: 1px; }
@media (max-width: 720px) { #sol82-briefing .body { grid-template-columns: 1fr; } }
#gunship-briefing[hidden] { display: none; }
#gunship-briefing { position: fixed; inset: 0; z-index: 70; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.84); font-family: ui-monospace, Menlo, monospace; }
#gunship-briefing .sheet { width: min(1040px, calc(100vw - 32px)); max-height: calc(100vh - 32px); overflow: auto; background: #07141a; border: 1px solid #2b6b96; padding: 14px 18px 12px; box-shadow: 0 0 40px rgba(95, 230, 214, 0.12); }
#gunship-briefing header { border-bottom: 1px solid #1f4a5e; padding-bottom: 6px; margin-bottom: 10px; }
#gunship-briefing h1 { margin: 0; color: #5fe6d6; font-size: 14px; letter-spacing: 2px; text-shadow: 0 0 6px rgba(95, 230, 214, 0.4); }
#gunship-briefing .sub { margin: 4px 0 0; color: #6fa8c8; font-size: 10px; letter-spacing: 1px; }
#gunship-briefing .body { display: grid; grid-template-columns: 3fr 2fr; gap: 14px; align-items: stretch; }
#gunship-briefing canvas { width: 100%; aspect-ratio: 3 / 2; display: block; background: #0b1e26; border: 1px solid #1f4a5e; cursor: pointer; }
#gunship-briefing .text h2 { margin: 6px 0 10px; color: #ffd48a; font-size: 13px; letter-spacing: .18em; }
#gunship-briefing .text ul { margin: 0; padding: 0 0 0 14px; color: #dff4ff; font-size: 13px; line-height: 1.6; }
#gunship-briefing .dots { margin-top: 12px; display: flex; gap: 6px; }
#gunship-briefing .dots i { width: 18px; height: 3px; background: #1f4a5e; }
#gunship-briefing .dots i.on { background: #5fe6d6; }
#gunship-briefing footer { display: flex; justify-content: space-between; align-items: center; margin-top: 12px; color: #6fa8c8; font-size: 10px; letter-spacing: 1px; }
#gunship-briefing footer button { font: 700 12px ui-monospace, Menlo, monospace; letter-spacing: .12em; color: #5fe6d6; background: #07141a; border: 1px solid #5fe6d6; padding: 7px 14px; cursor: pointer; }
@media (max-width: 720px) { #gunship-briefing .body { grid-template-columns: 1fr; } }
#synthetic-modal { position: fixed; inset: 0; z-index: 70; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.82); font-family: 'Courier New', ui-monospace, monospace; color: #aaa; }
#synthetic-modal .sheet { width: min(980px, calc(100vw - 32px)); max-height: calc(100vh - 32px); overflow: auto; background: #000; border: 1px solid #444; padding: 14px 18px 12px; box-shadow: 0 0 40px rgba(0, 255, 65, 0.08); }
#synthetic-modal header { border-bottom: 1px solid #444; padding-bottom: 6px; margin-bottom: 10px; }
#synthetic-modal h1 { margin: 0; color: #00ff41; font-size: 14px; letter-spacing: 2px; text-shadow: 0 0 6px rgba(0, 255, 65, 0.4); }
#synthetic-modal .sub { margin: 4px 0 0; color: #666; font-size: 10px; letter-spacing: 1px; }
#synthetic-modal .isao { margin: 0 0 12px; color: #dff4ff; font: 13px/1.5 ui-monospace, Menlo, monospace; border-left: 3px solid #00ff41; padding-left: 10px; }
#synthetic-modal .grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
#synthetic-modal canvas { width: 100%; aspect-ratio: 3 / 2; display: block; background: #050505; border: 1px solid #333; }
#synthetic-modal footer { display: flex; justify-content: space-between; align-items: center; margin-top: 12px; color: #555; font-size: 10px; letter-spacing: 1px; }
#synthetic-modal footer button { font: 700 12px ui-monospace, Menlo, monospace; letter-spacing: .12em; color: #00ff41; background: #000; border: 1px solid #00ff41; padding: 7px 14px; cursor: pointer; }
@media (max-width: 720px) { #synthetic-modal .grid { grid-template-columns: 1fr; } }

/* --- DEV SCREENS (src/fx/docs-overlay.js, src/fx/felt-capture.js): over the page, which keeps running --- */
#docs-overlay { position: fixed; inset: 0; z-index: 90; background: rgba(6, 10, 16, 0.97); }
#docs-overlay[hidden] { display: none; }
#docs-overlay #notes { position: relative; height: 100%; }
#docs-overlay .docs-close { position: absolute; top: 10px; right: 14px; z-index: 1; width: 40px; height: 36px; font: 700 18px ui-monospace, Menlo, monospace; color: #9fdcff; background: rgba(12, 26, 38, 0.85); border: 1px solid #2b6b96; border-radius: 8px; cursor: pointer; }
#felt-capture { position: fixed; inset: 0; z-index: 90; display: grid; place-items: center; background: rgba(6, 10, 16, 0.6); }
#felt-capture[hidden] { display: none; }
#felt-capture .felt-card { position: relative; width: min(460px, calc(100vw - 32px)); max-height: calc(100vh - 32px); overflow-y: auto; box-sizing: border-box; padding: 16px; background: #0c1520; border: 1px solid #2b6b96; border-radius: 10px; color: #d2e9e5; font: 13px ui-monospace, Menlo, monospace; }
#felt-capture h3 { margin: 0 0 8px; }
#felt-capture textarea, #felt-capture select { width: 100%; box-sizing: border-box; margin: 6px 0; font: inherit; color: #dff4ff; background: #111b22; border: 1px solid #2b6b96; border-radius: 6px; }
#felt-capture button { font: inherit; color: #9fdcff; background: rgba(12, 26, 38, 0.85); border: 1px solid #2b6b96; border-radius: 6px; padding: 8px 10px; min-height: 36px; cursor: pointer; }
#felt-capture .felt-close { position: absolute; top: 8px; right: 8px; }
/* --- THE NAVIGATION SHELL (src/fx/shell-nav.js): PLAYTEST | DEV top right, joined to the build tag --- */
#shell-bar { position: fixed; top: max(8px, env(safe-area-inset-top)); right: max(8px, env(safe-area-inset-right)); z-index: 80; display: flex; align-items: center; gap: 8px; font: 600 11px ui-monospace, Menlo, monospace; letter-spacing: 0.06em; }
#shell-bar .shell-toggle { display: flex; border: 1px solid #2b6b96; border-radius: 8px; overflow: hidden; background: rgba(8, 14, 20, 0.85); }
#shell-bar .shell-toggle button { font: inherit; color: #6fa8c8; background: none; border: 0; padding: 8px 12px; min-height: 36px; cursor: pointer; touch-action: manipulation; }
#shell-bar .shell-toggle button + button { border-left: 1px solid #2b6b96; }
#shell-bar .shell-toggle button.current { color: #dff4ff; background: rgba(40, 90, 120, 0.95); }
#shell-bar .shell-place { color: #9fdcff; background: rgba(8, 14, 20, 0.7); border-radius: 6px; padding: 2px 6px; }
#shell-bar .shell-place:empty { display: none; }
#shell-nav { position: fixed; top: calc(max(8px, env(safe-area-inset-top)) + 46px); right: max(8px, env(safe-area-inset-right)); z-index: 80; box-sizing: border-box; width: min(340px, calc(100vw - 16px)); max-height: calc(100vh - 70px); overflow-y: auto; padding: 10px; background: rgba(10, 16, 26, 0.96); border: 1px solid #2a3350; border-radius: 10px; font: 12px ui-monospace, Menlo, monospace; color: #aab8dd; }
#shell-nav[hidden], #shell-nav [hidden] { display: none !important; }
#shell-nav .shell-group { display: flex; flex-wrap: wrap; gap: 6px; padding: 6px 0; border-bottom: 1px solid #1d2438; }
#shell-nav .shell-group:last-child { border-bottom: 0; }
#shell-nav h3 { width: 100%; margin: 2px 0; font: 600 10px ui-monospace, Menlo, monospace; letter-spacing: 0.12em; text-transform: uppercase; color: #6a789c; }
#shell-nav button { font: inherit; color: #9fdcff; background: rgba(12, 26, 38, 0.85); border: 1px solid #2b6b96; border-radius: 6px; cursor: pointer; touch-action: manipulation; }
#shell-nav .shell-big { flex: 1 1 100%; text-align: left; padding: 10px 12px; min-height: 44px; }
#shell-nav [data-group="footer"] .shell-big { flex: 1 1 0; }
#shell-nav .shell-small { min-width: 36px; min-height: 36px; padding: 4px 8px; }
#shell-nav .shell-row { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; width: 100%; }
#shell-nav .shell-row > span { width: 56px; color: #6a789c; }
#shell-nav button.active { color: #dff4ff; background: rgba(40, 90, 120, 0.95); border-color: #6fc3ff; }
#shell-nav button:disabled { opacity: 0.35; cursor: default; }
#shell-nav .shell-count { display: flex; align-items: center; gap: 6px; width: 100%; }
#shell-nav .shell-count input { width: 64px; font: inherit; color: #dff4ff; background: rgba(12, 26, 38, 0.85); border: 1px solid #2b6b96; border-radius: 6px; padding: 6px; }
body.playing:not(.shell-open) #shell-bar { opacity: 0.32; }
#vars-toggle, #td-link { display: none !important; }

/* touch only: DEV (labs, tuning, docs, tools) is desktop-only, so a phone
   never sees its toggle or drawer section, and PLAYTEST's own targets grow
   to the 44px touch minimum. Desktop sizes are untouched. */
@media (pointer: coarse) {
  #shell-bar .shell-toggle button[data-mode="dev"],
  #shell-nav section[data-mode="dev"] {
    display: none !important;
  }
  #shell-bar .shell-toggle button { min-height: 44px; }
  #shell-nav .shell-small { min-width: 44px; min-height: 44px; }
}

/* sector debrief ==========================================================
   src/fx/sector-debrief.js, the end-of-sector card (V1 session design,
   section 4). A military console in phosphor: one ink family, ONE accent
   (amber; red on a lost sector), big banner-face numbers that roll, the
   shout face for the words that shout, stamps that thump. The card sizes
   itself by its HOST's width (container sdb), so a 400 px host stacks on any
   screen. Everything here is prefixed sdb-. The old after-action board
   (.dbf-*, above) is the look it grew out of, rainbow records included. */
.sdb-root {
  --sdb-ink: #cffbe9;
  --sdb-hi: #f2fff9;
  --sdb-glow: #35ffd0;
  --sdb-glow-soft: rgba(53, 255, 208, 0.45);
  --sdb-dim: #62978d;
  --sdb-line: rgba(53, 255, 208, 0.26);
  --sdb-faint: rgba(53, 255, 208, 0.1);
  --sdb-accent: #ffb020;
  --sdb-accent-soft: rgba(255, 176, 32, 0.16);
  --sdb-accent-ink: #160c00;
  position: absolute;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  box-sizing: border-box;
  padding: min(3vmin, 28px);
  container: sdb / inline-size;
  background: radial-gradient(120% 90% at 50% 38%, rgba(3, 26, 22, 0.72), rgba(0, 4, 5, 0.94));
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  color: var(--sdb-ink);
  font-family: var(--font-ui);
  line-height: 1.2;
  outline: none;
  -webkit-user-select: none;
  user-select: none;
}
.sdb-root[hidden] { display: none; }
.sdb-root *, .sdb-root *::before, .sdb-root *::after { box-sizing: border-box; }
.sdb-root[data-outcome="lost"] {
  --sdb-accent: #ff4d2e;
  --sdb-accent-soft: rgba(255, 77, 46, 0.18);
  --sdb-accent-ink: #1a0400;
}

/* the console: header, scrolling body, footer; corner brackets and scanlines */
.sdb-frame {
  position: relative;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-columns: minmax(0, 1fr);   /* a long header line must not widen the whole card past its frame */
  width: min(1120px, 100%);
  height: min(780px, 100%);
  overflow: hidden;
  background:
    radial-gradient(90% 60% at 50% 0%, rgba(53, 255, 208, 0.08), transparent 70%),
    linear-gradient(180deg, #04201c, #010a0a 70%);
  border: 1px solid var(--sdb-line);
  box-shadow: 0 0 0 1px #000, 0 0 60px rgba(53, 255, 208, 0.12), inset 0 0 90px rgba(0, 0, 0, 0.55);
}
.sdb-frame::before {
  content: '';
  position: absolute;
  inset: 5px;
  z-index: 3;
  pointer-events: none;
  --c: var(--sdb-glow);
  background:
    linear-gradient(var(--c), var(--c)) top left / 22px 2px no-repeat,
    linear-gradient(var(--c), var(--c)) top left / 2px 22px no-repeat,
    linear-gradient(var(--c), var(--c)) top right / 22px 2px no-repeat,
    linear-gradient(var(--c), var(--c)) top right / 2px 22px no-repeat,
    linear-gradient(var(--c), var(--c)) bottom left / 22px 2px no-repeat,
    linear-gradient(var(--c), var(--c)) bottom left / 2px 22px no-repeat,
    linear-gradient(var(--c), var(--c)) bottom right / 22px 2px no-repeat,
    linear-gradient(var(--c), var(--c)) bottom right / 2px 22px no-repeat;
  opacity: 0.75;
}
.sdb-scan {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
  background: repeating-linear-gradient(0deg, rgba(0, 0, 0, 0) 0 2px, rgba(0, 10, 8, 0.3) 2px 3px);
}
.sdb-scan::after {
  content: '';
  position: absolute;
  left: 0; right: 0; top: -22%;
  height: 22%;
  background: linear-gradient(180deg, transparent, rgba(53, 255, 208, 0.05), transparent);
  animation: sdb-roll 7s linear infinite;
}
.sdb-root[data-outcome="lost"] .sdb-scan::after {
  background: linear-gradient(180deg, transparent, rgba(255, 77, 46, 0.1), transparent);
  animation-duration: 2.6s;
}
@keyframes sdb-roll { to { transform: translateY(560%); } }

.sdb-head {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 6px 16px;
  padding: 12px 22px 10px;
  border-bottom: 1px solid var(--sdb-line);
  background: linear-gradient(180deg, rgba(53, 255, 208, 0.06), transparent);
}
.sdb-tx {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  font-size: 19px; letter-spacing: 0.2em; color: var(--sdb-dim); white-space: nowrap;
}
.sdb-tx span { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.sdb-dot {
  flex: none; width: 9px; height: 9px; border-radius: 50%;
  background: var(--sdb-accent); box-shadow: 0 0 10px var(--sdb-accent);
  animation: sdb-blink 1.2s steps(2, jump-none) infinite;
}
@keyframes sdb-blink { 50% { opacity: 0.2; } }
.sdb-tabs { display: flex; gap: 2px; min-width: 0; }
.sdb-tabs button {
  font: inherit; font-size: 17px; letter-spacing: 0.14em; line-height: 1;
  color: var(--sdb-dim); background: transparent; border: 1px solid transparent;
  padding: 5px 9px 4px; cursor: pointer; white-space: nowrap;
}
.sdb-tabs button b { font-weight: normal; color: var(--sdb-line); margin-right: 6px; }
.sdb-tabs button.is-seen { color: var(--sdb-ink); }
.sdb-tabs button.is-seen b { color: var(--sdb-glow); }
.sdb-tabs button:hover { border-color: var(--sdb-line); }
.sdb-tabs button.is-on { color: var(--sdb-accent-ink); background: var(--sdb-accent); }
.sdb-tabs button.is-on b { color: var(--sdb-accent-ink); }

.sdb-body {
  position: relative;
  z-index: 1;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: clamp(12px, 2cqi, 24px) clamp(14px, 3.4cqi, 44px) clamp(14px, 2cqi, 24px);
  scrollbar-width: thin;
  scrollbar-color: var(--sdb-line) transparent;
}
.sdb-body.sdb-enter { animation: sdb-enter 0.3s steps(5, end); }
@keyframes sdb-enter { 0% { opacity: 0; } 40% { opacity: 0.75; } 60% { opacity: 0.3; } 100% { opacity: 1; } }

.sdb-foot {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 10px 14px;
  padding: 10px 16px;
  border-top: 1px solid var(--sdb-line);
  background: rgba(0, 0, 0, 0.35);
}
.sdb-count { font-family: var(--font-banner); font-size: 26px; letter-spacing: 0.08em; color: var(--sdb-ink); }
.sdb-hint { margin-right: auto; font-size: 16px; letter-spacing: 0.18em; color: var(--sdb-dim); white-space: nowrap; }
.sdb-acts { display: flex; gap: 8px; margin-left: auto; }
.sdb-btn {
  min-height: 46px; padding: 0 18px;
  font-family: var(--font-shout); font-size: 12px; letter-spacing: 0.1em; line-height: 1;
  color: var(--sdb-ink); background: rgba(53, 255, 208, 0.05);
  border: 1px solid var(--sdb-line); border-radius: 0; cursor: pointer; white-space: nowrap;
  touch-action: manipulation;
}
.sdb-btn:hover, .sdb-btn:focus-visible { color: var(--sdb-hi); border-color: var(--sdb-glow); background: rgba(53, 255, 208, 0.14); outline: none; }
.sdb-btn:disabled { opacity: 0.25; cursor: default; }
.sdb-btn[hidden] { display: none; }
.sdb-btn--go, .sdb-btn--go:hover, .sdb-btn--go:focus-visible {
  color: var(--sdb-accent-ink); background: var(--sdb-accent); border-color: var(--sdb-accent);
  box-shadow: 0 0 24px var(--sdb-accent-soft), inset 0 0 0 1px rgba(0, 0, 0, 0.5);
}
.sdb-btn--go:hover, .sdb-btn--go:focus-visible { border-color: var(--sdb-hi); filter: brightness(1.12); }
.sdb-btn.sdb-nudge { animation: sdb-nudge 0.5s ease-out; }
@keyframes sdb-nudge { 0%, 100% { transform: none; } 20% { transform: translateX(-6px); } 45% { transform: translateX(6px); } 70% { transform: translateX(-2px); } }

/* type */
.sdb-kicker { font-size: clamp(18px, 2.1cqi, 26px); letter-spacing: 0.34em; color: var(--sdb-dim); }
.sdb-word {
  margin: 6px 0 0;
  font-family: var(--font-shout); font-weight: normal;
  font-size: clamp(30px, 7.4cqi, 88px); line-height: 1.04; letter-spacing: 0.02em;
  color: var(--sdb-hi);
  text-shadow: 0 0 22px var(--sdb-glow-soft), 0 0 2px var(--sdb-glow), 4px 0 0 rgba(255, 60, 60, 0.22), -4px 0 0 rgba(60, 150, 255, 0.22);
}
.sdb-word span { display: inline-block; }
.sdb-root[data-outcome="lost"] .sdb-word {
  font-size: clamp(22px, 5.6cqi, 68px);
  color: var(--sdb-accent);
  text-shadow: 0 0 22px var(--sdb-accent-soft), 3px 0 0 rgba(60, 150, 255, 0.25);
  animation: sdb-glitch 3.4s steps(1) infinite;
}
@keyframes sdb-glitch { 0%, 92%, 100% { translate: 0; } 93% { translate: -6px 0; } 95% { translate: 5px 0; } 97% { translate: 0; } }
.sdb-camp .sdb-word { font-size: clamp(24px, 4.6cqi, 58px); }
.sdb-camp .sdb-stat > .sdb-num { font-size: clamp(36px, 4.6cqi, 62px); }
.sdb-hero { display: flex; flex-direction: column; }
.sdb-sub { margin-top: 8px; font-size: clamp(17px, 2cqi, 24px); letter-spacing: 0.24em; color: var(--sdb-glow); }
.sdb-title {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 18px;
  margin: 0 0 clamp(12px, 2cqi, 22px);
  font-family: var(--font-shout); font-weight: normal;
  font-size: clamp(18px, 2.9cqi, 34px); letter-spacing: 0.04em;
  color: var(--sdb-hi); text-shadow: 0 0 14px var(--sdb-glow-soft);
}
.sdb-title small { font-family: var(--font-ui); font-size: clamp(16px, 1.8cqi, 21px); letter-spacing: 0.22em; color: var(--sdb-dim); text-shadow: none; }
.sdb-h {
  display: flex; justify-content: space-between; gap: 10px;
  margin: 12px 0 8px; padding-bottom: 5px; border-bottom: 1px solid var(--sdb-line);
  font-size: 17px; letter-spacing: 0.26em; color: var(--sdb-dim);
}
.sdb-col > .sdb-h:first-child { margin-top: 0; }
.sdb-none { font-family: var(--font-ui); font-weight: normal; font-size: 18px; letter-spacing: 0.22em; color: var(--sdb-dim); }
.sdb-root [data-reveal] { opacity: 0; transform: translateY(6px); transition: opacity 0.24s steps(4, end), transform 0.3s ease-out; }
.sdb-root [data-reveal].is-in { opacity: 1; transform: none; }

.sdb-num {
  font-family: var(--font-banner); font-weight: normal; font-variant-numeric: tabular-nums;
  color: var(--sdb-hi); text-shadow: 0 0 14px var(--sdb-glow-soft); white-space: nowrap;
}
.sdb-num--s { font-size: 1.2em; color: var(--sdb-ink); text-shadow: none; }
.sdb-num--xs { font-size: 19px; text-shadow: none; }
.sdb-unit { margin-left: 6px; font-style: normal; font-size: 18px; letter-spacing: 0.16em; color: var(--sdb-dim); }
.sdb-rainbow {
  color: #9fdcff;
  background: linear-gradient(100deg, #ff7979, #ffcb70, #edee80, #80efb3, #83d8ff, #bfa0ff, #ff9fd5, #ff7979);
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  text-shadow: none;
  filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.22));
  animation: sdb-rainbow 2.4s linear infinite;
}
@keyframes sdb-rainbow { to { background-position: -200% 0; } }

/* page 1: the hero */
.sdb-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin-top: clamp(14px, 2.4cqi, 28px); border-block: 1px solid var(--sdb-line); }
.sdb-stat { min-width: 0; padding: 12px 18px; border-left: 1px solid var(--sdb-faint); }
.sdb-stat:first-child { border-left: 0; padding-left: 0; }
.sdb-stat > span { display: block; margin-bottom: 4px; font-size: 17px; letter-spacing: 0.24em; color: var(--sdb-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sdb-stat > .sdb-num { font-size: clamp(40px, 5.4cqi, 76px); line-height: 0.9; }
.sdb-stat > small { display: block; margin-top: 6px; font-size: 16px; letter-spacing: 0.1em; color: var(--sdb-dim); }
.sdb-stat--warn > .sdb-num { color: var(--sdb-accent); text-shadow: 0 0 14px var(--sdb-accent-soft); }
.sdb-stamps { display: flex; flex-wrap: wrap; align-items: center; gap: 18px 30px; margin-top: clamp(14px, 2.4cqi, 28px); padding: 6px 4px; }
/* a big haul of stamps shrinks to stay on one row under the numbers */
.sdb-stamps[data-count="3"] .sdb-stamp { font-size: clamp(12px, 1.5cqi, 18px); }
.sdb-stamps:not([data-count="1"]):not([data-count="2"]):not([data-count="3"]) { gap: 14px 18px; }
.sdb-stamps:not([data-count="1"]):not([data-count="2"]):not([data-count="3"]) .sdb-stamp { padding: 10px 12px 9px; font-size: clamp(11px, 1.2cqi, 14px); }
.sdb-stamps:not([data-count="1"]):not([data-count="2"]):not([data-count="3"]) .sdb-stamp small { font-size: 14px; letter-spacing: 0.1em; }

/* stamps: rubber on paper, thumped in rotated with an overshoot */
.sdb-stamp {
  --rot: -6deg;
  position: relative;
  display: inline-flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 13px 18px 11px;
  color: var(--sdb-accent);
  border: 3px solid currentColor;
  background: repeating-linear-gradient(118deg, var(--sdb-accent-soft) 0 2px, transparent 2px 7px);
  font-family: var(--font-shout); font-size: clamp(13px, 1.65cqi, 20px); letter-spacing: 0.06em; line-height: 1;
  text-align: center;
  text-shadow: 0 0 12px var(--sdb-accent-soft);
  transform: rotate(var(--rot));
  opacity: 0;
}
.sdb-stamp::before { content: ''; position: absolute; inset: 3px; border: 1px solid currentColor; pointer-events: none; }
.sdb-stamp small { font-family: var(--font-ui); font-size: clamp(14px, 1.4cqi, 17px); letter-spacing: 0.14em; }
.sdb-stamp.is-in, .sdb-stamp.is-set { opacity: 1; }
.sdb-stamp.is-in { animation: sdb-thump 0.46s cubic-bezier(0.18, 0.9, 0.32, 1.3) both; }
@keyframes sdb-thump {
  0% { opacity: 0; transform: rotate(calc(var(--rot) - 16deg)) scale(2.8); filter: blur(4px); }
  55% { opacity: 1; transform: rotate(calc(var(--rot) + 2deg)) scale(0.88); filter: blur(0); }
  78% { transform: rotate(var(--rot)) scale(1.05); }
  100% { opacity: 1; transform: rotate(var(--rot)) scale(1); }
}
.sdb-stamp--closer { position: absolute; top: 12px; right: 14px; gap: 5px; padding: 8px 12px 7px; font-size: clamp(14px, 1.9cqi, 22px); }
.sdb-stamp--closer small { font-size: 13px; letter-spacing: 0.18em; }
.sdb-stamp--closer.is-open { border-style: dashed; }
.sdb-stamp--mini { justify-self: end; padding: 6px 8px 5px; border-width: 2px; font-size: 10px; }
.sdb-stamp--mini::before { inset: 2px; }
.sdb-root .sdb-frame.sdb-jolt { animation: sdb-jolt 0.24s ease-out; }
@keyframes sdb-jolt { 25% { transform: translate(-2px, 3px); } 55% { transform: translate(2px, -1px); } 100% { transform: none; } }
.sdb-root.sdb-power .sdb-frame { animation: sdb-power 0.42s cubic-bezier(0.2, 0.8, 0.2, 1) both; }
@keyframes sdb-power {
  0% { opacity: 0.2; transform: scale(0.5, 0.004); filter: brightness(4); }
  40% { opacity: 1; transform: scale(1, 0.004); filter: brightness(3); }
  100% { transform: none; filter: none; }
}
.sdb-root.sdb-snap .sdb-frame, .sdb-root.sdb-snap .sdb-body, .sdb-root.sdb-snap [data-reveal],
.sdb-root.sdb-snap .sdb-stamp, .sdb-root.sdb-snap .sdb-glyph, .sdb-root.sdb-snap .sdb-chip { animation: none !important; transition: none !important; }

/* page 2: the breaches */
.sdb-breaches { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: clamp(12px, 2.2cqi, 26px); }
.sdb-breach { position: relative; min-width: 0; padding: 16px 20px 14px; border: 1px solid var(--sdb-line); background: linear-gradient(180deg, rgba(53, 255, 208, 0.06), rgba(53, 255, 208, 0) 60%); }
.sdb-breach > header { display: flex; align-items: baseline; gap: 14px; min-height: 66px; padding: 0 130px 8px 0; margin-bottom: 4px; border-bottom: 1px dashed var(--sdb-line); }
.sdb-letter { font-family: var(--font-shout); font-size: clamp(30px, 4cqi, 48px); line-height: 1; color: var(--sdb-hi); text-shadow: 0 0 16px var(--sdb-glow-soft); }
.sdb-tag { font-size: 19px; letter-spacing: 0.24em; color: var(--sdb-glow); white-space: nowrap; }
.sdb-big { display: flex; align-items: baseline; gap: 12px; margin: 10px 0 6px; }
.sdb-big > .sdb-num { font-size: clamp(56px, 7.4cqi, 100px); line-height: 0.85; }
.sdb-big > span, .sdb-total > span { font-size: 18px; letter-spacing: 0.26em; color: var(--sdb-dim); }
.sdb-kv { display: flex; align-items: center; gap: 12px; min-height: 38px; border-top: 1px solid var(--sdb-faint); font-size: 17px; letter-spacing: 0.2em; color: var(--sdb-dim); }
.sdb-kv > em { margin-left: auto; font-style: normal; font-family: var(--font-banner); font-size: 26px; letter-spacing: 0.04em; color: var(--sdb-ink); white-space: nowrap; }
.sdb-kv > em .sdb-num { font-size: 1em; color: inherit; text-shadow: none; }
.sdb-kv.is-warn > em { color: var(--sdb-accent); }
.sdb-pips { display: flex; flex-wrap: wrap; gap: 5px; }
.sdb-pip { width: 14px; height: 14px; border: 1px solid var(--sdb-line); }
.sdb-pip.is-fought { background: var(--sdb-glow); border-color: var(--sdb-glow); box-shadow: 0 0 8px var(--sdb-glow-soft); }
.sdb-note { margin-top: clamp(12px, 2cqi, 22px); padding: 10px 14px; border-left: 3px solid var(--sdb-line); font-size: 19px; letter-spacing: 0.14em; color: var(--sdb-ink); }
.sdb-note > span, .sdb-note i { font-style: normal; color: var(--sdb-dim); }
.sdb-note.is-warn { border-left-color: var(--sdb-accent); background: linear-gradient(90deg, var(--sdb-accent-soft), transparent 70%); }

/* page 3: the kills */
.sdb-kills { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 4px clamp(18px, 3.4cqi, 44px); }
.sdb-total { display: flex; align-items: baseline; gap: 14px; margin-bottom: 10px; }
.sdb-total > .sdb-num { font-size: clamp(60px, 7.6cqi, 100px); line-height: 0.8; }
.sdb-row { display: grid; grid-template-columns: minmax(6.5em, 9em) minmax(0, 1fr) minmax(3.2em, auto); align-items: center; gap: 12px; min-height: 27px; font-size: 18px; letter-spacing: 0.14em; }
.sdb-row > .sdb-num { justify-self: end; font-size: 24px; text-shadow: none; }
.sdb-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sdb-row.is-sub { min-height: 22px; font-size: 16px; color: var(--sdb-dim); }
.sdb-row.is-sub .sdb-label { padding-left: 0.6em; }
.sdb-row.is-zero { opacity: 0.34; }
.sdb-track { position: relative; height: 12px; overflow: hidden; background: var(--sdb-faint); }
.sdb-row.is-sub .sdb-track { height: 6px; }
.sdb-bar { position: absolute; inset: 0; display: block; background: var(--sdb-glow); box-shadow: 0 0 12px var(--sdb-glow-soft); }
.sdb-bar--x { transform-origin: 0 50%; transform: scaleX(var(--g, 0)); }
.sdb-bar--y { transform-origin: 50% 100%; transform: scaleY(var(--g, 0)); }
.sdb-row.is-sub .sdb-bar { background: repeating-linear-gradient(90deg, var(--sdb-glow) 0 4px, transparent 4px 6px); box-shadow: none; opacity: 0.75; }
.sdb-row.is-lead .sdb-bar { background: var(--sdb-accent); box-shadow: 0 0 14px var(--sdb-accent-soft); }
.sdb-row.is-lead > .sdb-num { color: var(--sdb-accent); }
.sdb-hist { display: flex; align-items: stretch; gap: clamp(4px, 0.8cqi, 10px); height: clamp(130px, 14cqi, 180px); }
.sdb-belt { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 5px; }
.sdb-belt-col { position: relative; flex: 1; width: 100%; max-width: 44px; background: var(--sdb-faint); }
.sdb-belt-col .sdb-bar { background: linear-gradient(180deg, var(--sdb-hi), var(--sdb-glow) 30%); }
.sdb-swatch { display: block; width: 100%; max-width: 44px; height: 6px; background: var(--belt); box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6); }
.sdb-belt-l { font-size: 14px; letter-spacing: 0.04em; color: var(--sdb-dim); white-space: nowrap; }
.sdb-tempo { margin-top: 4px; }
/* the campaign's THE RUN and THE BEST MOMENTS (src/core/run-recap.js) */
.sdb-run-spark { height: clamp(90px, 11cqi, 130px); }
.sdb-run-tick { stroke: var(--sdb-line); }
.sdb-run-names { position: relative; height: 18px; margin-top: 4px; }
.sdb-run-names > span { position: absolute; top: 0; font-size: 13px; letter-spacing: 0.1em; color: var(--sdb-dim); text-align: center; overflow: hidden; border-left: 1px solid var(--sdb-line); }
.sdb-mults { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(10px, 1.6cqi, 22px) clamp(12px, 2cqi, 28px); margin-top: clamp(14px, 2cqi, 26px); }
@container (max-width: 640px) { .sdb-mults { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.sdb-mult svg { display: block; width: 100%; height: clamp(40px, 5cqi, 64px); margin-top: 6px; background: var(--sdb-faint); }
.sdb-mult rect { fill: var(--sdb-glow); opacity: 0.55; }
.sdb-mult rect.is-peak { fill: var(--sdb-accent); opacity: 1; }
.sdb-mult rect.is-lost { fill: var(--sdb-dim); }
.sdb-mult.is-warn rect.is-peak { fill: #ff4d2e; }
.sdb-clips { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: clamp(10px, 1.6cqi, 22px); margin: clamp(14px, 2cqi, 26px) 0 0; }
.sdb-clip { margin: 0; border: 1px solid var(--sdb-line); background: #000; }
.sdb-clip canvas { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; filter: grayscale(1) contrast(1.25) brightness(1.05); }
.sdb-clip figcaption { padding: 6px 10px; font-size: 14px; letter-spacing: 0.1em; color: var(--sdb-accent); }
.sdb-lapse-film { max-width: min(100%, 720px, calc((100cqh - 260px) * 16 / 9)); margin-top: clamp(14px, 2cqi, 26px); }
.sdb-lapse-film canvas { filter: none; }
.sdb-lapse-film figcaption { display: flex; align-items: center; gap: 14px; }
.sdb-lapse-bar { flex: 1; height: 3px; background: var(--sdb-faint); }
.sdb-lapse-bar > i { display: block; height: 100%; width: 0; background: var(--sdb-accent); }
.sdb-best-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr)); gap: clamp(10px, 1.6cqi, 22px); margin-top: clamp(14px, 2cqi, 26px); }
.sdb-best { display: flex; flex-direction: column; gap: 4px; padding: 12px 14px; border: 1px solid var(--sdb-line); background: var(--sdb-faint); }
.sdb-best > span { font-size: 14px; letter-spacing: 0.12em; color: var(--sdb-dim); }
.sdb-best > .sdb-num { font-size: clamp(30px, 3.6cqi, 48px); line-height: 1.05; color: var(--sdb-hi); text-shadow: 0 0 12px var(--sdb-glow-soft); }
.sdb-best > small { font-size: 13px; letter-spacing: 0.08em; color: var(--sdb-dim); }
.sdb-spark-wrap { position: relative; height: clamp(70px, 8.4cqi, 100px); margin-top: 26px; }
.sdb-spark { display: block; width: 100%; height: 100%; overflow: visible; clip-path: inset(-30px calc((1 - var(--g, 0)) * 100%) -2px -2px); }
.sdb-spark-grid { stroke: var(--sdb-faint); stroke-width: 1; vector-effect: non-scaling-stroke; }
.sdb-spark-area { fill: rgba(53, 255, 208, 0.12); }
.sdb-spark-line { fill: none; stroke: var(--sdb-glow); stroke-width: 2; stroke-linejoin: round; vector-effect: non-scaling-stroke; filter: drop-shadow(0 0 4px var(--sdb-glow-soft)); }
.sdb-peak { position: absolute; width: 10px; height: 10px; margin: -5px 0 0 -5px; background: var(--sdb-accent); box-shadow: 0 0 12px var(--sdb-accent); }
.sdb-peak > span { position: absolute; bottom: 16px; left: 50%; translate: -50% 0; font-size: 17px; letter-spacing: 0.12em; color: var(--sdb-accent); white-space: nowrap; }
.sdb-peak.is-right > span { left: auto; right: -4px; translate: none; }
.sdb-peak.is-left > span { left: -4px; translate: none; }

/* page 4: the tank */
.sdb-tank { display: grid; grid-template-columns: minmax(200px, 0.75fr) minmax(0, 1.5fr); gap: clamp(18px, 3.4cqi, 44px); align-items: center; }
.sdb-gauge-wrap { position: relative; width: min(100%, 300px); aspect-ratio: 1; margin: 0 auto; }
.sdb-gauge { display: block; width: 100%; height: 100%; overflow: visible; transform: rotate(-90deg); }
.sdb-gauge circle { fill: none; }
.sdb-gauge-ticks { stroke: var(--sdb-line); stroke-width: 6; stroke-dasharray: 0.35 4.65; }
.sdb-gauge-bg { stroke: var(--sdb-faint); stroke-width: 9; }
.sdb-gauge-fg { stroke: var(--sdb-accent); stroke-width: 9; filter: drop-shadow(0 0 5px var(--sdb-accent-soft)); }
.sdb-gauge-c { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; text-align: center; }
.sdb-gauge-c > .sdb-num { font-size: clamp(54px, 7cqi, 92px); line-height: 0.85; }
.sdb-gauge-c > span { font-size: 17px; letter-spacing: 0.3em; color: var(--sdb-dim); }
.sdb-gauge-c > small { font-size: 16px; letter-spacing: 0.1em; color: var(--sdb-dim); }
.sdb-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px; background: var(--sdb-faint); border: 1px solid var(--sdb-faint); }
.sdb-tile { min-width: 0; padding: 12px 16px 14px; background: #021312; }
.sdb-tile > span { display: block; margin-bottom: 4px; font-size: 16px; letter-spacing: 0.22em; color: var(--sdb-dim); white-space: nowrap; }
.sdb-tile > .sdb-num { font-size: clamp(36px, 4.6cqi, 60px); line-height: 0.9; }
.sdb-tile.is-warn > .sdb-num { color: var(--sdb-accent); text-shadow: 0 0 12px var(--sdb-accent-soft); }
.sdb-tile--wide { grid-column: 1 / -1; }
.sdb-tile--wide .sdb-row { grid-template-columns: 8em minmax(0, 1fr) minmax(4em, auto); }
.sdb-tile--parts { grid-column: span 2; }
.sdb-glyphs { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; min-height: 46px; }
.sdb-glyph { width: 44px; height: 44px; overflow: visible; opacity: 0; }
.sdb-hull { width: 62px; height: 42px; }
.sdb-glyph.is-in, .sdb-glyph.is-set { opacity: 1; }
.sdb-glyph.is-in { animation: sdb-drop 0.52s cubic-bezier(0.3, 1.5, 0.5, 1) both; }
@keyframes sdb-drop { 0% { opacity: 0; transform: translateY(-36px); } 55% { opacity: 1; transform: translateY(4px); } 78% { transform: translateY(-3px); } 100% { opacity: 1; transform: none; } }
.sdb-crate rect, .sdb-crate path { fill: none; stroke: var(--sdb-accent); stroke-width: 2.2; }
.sdb-crate rect + rect { fill: var(--sdb-accent-soft); }
.sdb-hull path { fill: none; stroke: var(--sdb-ink); stroke-width: 2; stroke-linejoin: round; }
.sdb-hull .sdb-x { stroke: var(--sdb-accent); stroke-width: 3; }

/* page 5: the colony */
.sdb-colony { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 4px clamp(18px, 3.4cqi, 44px); }
.sdb-chips { display: flex; flex-wrap: wrap; gap: 8px; min-height: 40px; }
.sdb-chip {
  padding: 9px 12px 8px; opacity: 0;
  font-family: var(--font-shout); font-size: 11px; letter-spacing: 0.08em; line-height: 1;
  color: var(--sdb-hi); background: rgba(53, 255, 208, 0.1); border: 1px solid var(--sdb-glow);
}
.sdb-chip.is-in, .sdb-chip.is-set { opacity: 1; }
.sdb-chip.is-in { animation: sdb-drop 0.52s cubic-bezier(0.3, 1.5, 0.5, 1) both; }
.sdb-line { display: flex; align-items: baseline; gap: 12px; min-height: 34px; padding-top: 4px; border-bottom: 1px solid var(--sdb-faint); font-size: 18px; letter-spacing: 0.16em; color: var(--sdb-dim); }
.sdb-line > em { margin-left: auto; font-style: normal; color: var(--sdb-ink); white-space: nowrap; }
.sdb-line.is-zero > em { color: var(--sdb-dim); }
.sdb-line.is-warn > em, .sdb-line.is-warn .sdb-num { color: var(--sdb-accent); }
.sdb-rec { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(96px, auto); align-items: center; gap: 2px 14px; min-height: 54px; padding: 4px 0; border-bottom: 1px solid var(--sdb-faint); }
.sdb-rec > span { font-size: 17px; letter-spacing: 0.14em; color: var(--sdb-dim); }
.sdb-rec > .sdb-num { font-size: clamp(34px, 3.8cqi, 50px); line-height: 0.9; }
.sdb-rec > small, .sdb-rec > .sdb-stamp { justify-self: end; }
.sdb-rec > small { font-size: 17px; letter-spacing: 0.14em; color: var(--sdb-dim); }
.sdb-isao {
  grid-column: 1 / -1;
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: baseline; gap: 4px 18px;
  margin-top: clamp(14px, 2.4cqi, 26px); padding: 14px 18px;
  border-left: 3px solid var(--sdb-accent);
  background: linear-gradient(90deg, var(--sdb-accent-soft), transparent 80%);
}
.sdb-isao > b { grid-row: span 2; font-family: var(--font-shout); font-weight: normal; font-size: 13px; letter-spacing: 0.1em; color: var(--sdb-accent); }
.sdb-isao > p { margin: 0; min-height: 1.25em; font-size: clamp(19px, 2.3cqi, 28px); letter-spacing: 0.04em; color: var(--sdb-hi); }
.sdb-isao > p.is-typing::after { content: '\2588'; margin-left: 2px; color: var(--sdb-accent); animation: sdb-blink 0.5s steps(2, jump-none) infinite; }

/* the campaign card */
/* the sector table and Isao's send-off share a row on a wide card, and stack on a narrow one */
.sdb-camp-row { display: grid; grid-template-columns: minmax(0, 2.2fr) minmax(220px, 1fr); align-items: start; gap: clamp(14px, 2.6cqi, 32px); margin-top: clamp(14px, 2.4cqi, 26px); }
.sdb-camp-row > .sdb-isao { grid-column: auto; grid-template-columns: minmax(0, 1fr); margin-top: 0; }
.sdb-camp-row > .sdb-isao > b { grid-row: auto; }
.sdb-camp-row > .sdb-isao > p { font-size: clamp(18px, 1.9cqi, 23px); }
.sdb-table-wrap { overflow-x: auto; }
.sdb-table { width: 100%; border-collapse: collapse; }
.sdb-table th { padding: 6px 8px; border-bottom: 1px solid var(--sdb-line); font-weight: normal; font-size: 14px; letter-spacing: 0.16em; text-align: right; color: var(--sdb-dim); white-space: nowrap; }
.sdb-table td { padding: 6px 8px; border-bottom: 1px solid var(--sdb-faint); font-family: var(--font-banner); font-size: 23px; text-align: right; color: var(--sdb-ink); white-space: nowrap; }
.sdb-table td .sdb-num { font-size: 1em; color: inherit; text-shadow: none; }
.sdb-table .sdb-l { text-align: left; letter-spacing: 0.1em; }
.sdb-table th:first-child, .sdb-table td:first-child { padding-left: 0; text-align: left; color: var(--sdb-dim); }
.sdb-table tfoot td { border-top: 1px solid var(--sdb-accent); border-bottom: 0; color: var(--sdb-accent); }
.sdb-lost { font-style: normal; font-size: 0.7em; letter-spacing: 0.2em; color: var(--sdb-accent); }
.sdb-marks i { display: inline-block; width: 11px; height: 11px; margin-left: 6px; border: 2px solid var(--sdb-accent); transform: rotate(45deg); }

/* the host's width decides the columns, not the screen's */
@container sdb (max-width: 980px) {
  .sdb-tabs button span { display: none; }
  .sdb-tabs button.is-on span { display: inline; }
}
@container sdb (max-width: 720px) {
  .sdb-head { padding: 10px 14px 8px; }
  .sdb-tx { flex: 1 1 100%; font-size: 16px; letter-spacing: 0.12em; }
  .sdb-tabs button { padding: 5px 7px 4px; font-size: 15px; }
  .sdb-hint { display: none; }
  .sdb-count { margin-right: auto; font-size: 22px; }
  .sdb-foot { gap: 8px; padding: 8px 10px; }
  .sdb-btn { min-height: 44px; padding: 0 12px; font-size: 10px; }
  .sdb-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sdb-stat { padding: 12px 0 12px 14px; }
  .sdb-stat:nth-child(odd) { border-left: 0; padding-left: 0; }
  .sdb-stat:nth-child(n+3) { border-top: 1px solid var(--sdb-faint); }
  .sdb-stat > .sdb-num { font-size: clamp(34px, 11cqi, 56px); }
  .sdb-stat > span, .sdb-stat > small { font-size: 14px; letter-spacing: 0.12em; }
  .sdb-hero:not(.sdb-camp) > .sdb-stats { order: 1; }
  .sdb-stamps { justify-content: center; gap: 12px 14px; margin-top: 16px; }
  .sdb-stamp { gap: 5px; padding: 8px 10px 7px; border-width: 2px; font-size: 11px; }
  .sdb-stamp small { font-size: 13px; letter-spacing: 0.1em; }
  .sdb-tile--dmg { grid-column: 1 / -1; }
  .sdb-breach { padding: 14px 14px 12px; }
  .sdb-breach > header { padding-right: 112px; }
  .sdb-kills, .sdb-tank, .sdb-colony { grid-template-columns: minmax(0, 1fr); }
  .sdb-row { grid-template-columns: 6.5em minmax(0, 1fr) minmax(2.8em, auto); gap: 8px; font-size: 16px; }
  .sdb-gauge-wrap { width: min(56%, 200px); }
  .sdb-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sdb-tile { padding: 10px 12px 12px; }
  .sdb-tile > span { font-size: 14px; letter-spacing: 0.14em; }
  .sdb-tile--wide .sdb-row { grid-template-columns: 6.5em minmax(0, 1fr) minmax(3.4em, auto); }
  .sdb-camp-row { grid-template-columns: minmax(0, 1fr); }
  .sdb-tile--parts { grid-column: span 1; }
  .sdb-table .sdb-opt { display: none; }
  .sdb-table th { padding: 6px 8px; font-size: 14px; }
  .sdb-table td { padding: 6px 8px; font-size: 22px; }
}
@container sdb (max-width: 460px) {
  .sdb-kv { gap: 8px; font-size: 15px; letter-spacing: 0.12em; }
  .sdb-hist { height: 140px; }
  .sdb-belt-l { font-size: 12px; }
  .sdb-isao { grid-template-columns: minmax(0, 1fr); padding: 12px 14px; }
  .sdb-isao > b { grid-row: auto; }
  .sdb-rec { grid-template-columns: minmax(0, 1fr) auto; }
  .sdb-rec > span { grid-column: 1 / -1; }
}
/* the navigation shell sits over the top of a phone screen: the card starts below it */
@media (max-width: 720px) {
  .sdb-root { padding-top: calc(env(safe-area-inset-top, 0px) + 54px); }
}
.sdb-root.sdb-still *, .sdb-root.sdb-still *::before, .sdb-root.sdb-still *::after { animation: none !important; transition: none !important; }
@media (prefers-reduced-motion: reduce) {
  .sdb-root *, .sdb-root *::before, .sdb-root *::after { animation: none !important; transition: none !important; }
}
/* end sector debrief */

/* THE STORY WORLD WEARS NO CAMPAIGN FURNITURE (2026-09-16): the centre/auto/orbit/map/tank chips and the old key hint do nothing a player
   needs there; the strip names the views. The sound chip stays, alone. */
#tab-td.story-world .tc-util button:not(#td-pad-sound), #tab-td.story-world #td-hint { display: none !important; }

/* ==== THE TUTORIAL CARD (src/fx/tutorial-card.js): where the opening is (TUTORIAL 3/6 · FIRST WAVE and a pip a chapter), NEXT
   to the next chapter and SKIP ALL to the back door, from the landing until the first sector. Bottom right, above the pad, in the
   story's monochrome: a pale outline that cannot be read as damage or as a campaign chip, and thumb-sized buttons on a phone. It
   removes itself; there is no hidden state. ==== */
#skip-tutorial { position: absolute; right: 16px; bottom: 132px; z-index: 45; display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; background: rgba(8, 12, 16, 0.86); border: 1px solid rgba(223, 232, 238, 0.55); color: #dfe8ee; font: 12px/1.2 ui-monospace, Menlo, monospace; letter-spacing: 0.14em; pointer-events: auto; }
#skip-tutorial .tut-where { display: flex; align-items: baseline; gap: 10px; white-space: nowrap; }
#skip-tutorial .tut-where b { font-weight: 700; letter-spacing: 0.18em; }
#skip-tutorial .tut-name { flex: 1; }
#skip-tutorial .tut-pips { letter-spacing: 0.04em; color: #9aa8b0; }
#skip-tutorial .tut-go { display: flex; gap: 6px; }
#skip-tutorial button { flex: 1 1 auto; padding: 7px 12px; background: transparent; border: 1px solid rgba(223, 232, 238, 0.45); color: inherit; font: 700 12px/1.2 ui-monospace, Menlo, monospace; letter-spacing: 0.14em; white-space: nowrap; cursor: pointer; }
#skip-tutorial button:hover, #skip-tutorial button:focus-visible { border-color: #ffffff; color: #ffffff; background: rgba(28, 36, 42, 0.92); }
#skip-tutorial button:disabled { opacity: 0.4; cursor: default; }
@media (max-width: 720px) { #skip-tutorial { right: 12px; bottom: 168px; } #skip-tutorial button { min-height: 44px; } }

/* ==== CONTROLS CARD (src/fx/controls-card.js): the tank's keys, once past the handover; H or ? again ==== */
#controls-card { position: absolute; right: 16px; top: 60px; z-index: 40; width: min(300px, calc(100vw - 32px)); padding: 10px 12px 12px; background: rgba(8, 12, 16, 0.86); border: 1px solid rgba(220, 235, 240, 0.28); color: #dfe8ec; font: 12px/1.45 ui-monospace, Menlo, monospace; pointer-events: auto; }
#controls-card[hidden] { display: none; }
#controls-card header { display: flex; justify-content: space-between; align-items: center; font-weight: 700; letter-spacing: 0.14em; margin-bottom: 6px; }
#controls-card header button { background: none; border: 0; color: inherit; font: inherit; font-size: 16px; line-height: 1; cursor: pointer; padding: 0 2px; }
#controls-card dl { display: grid; grid-template-columns: auto 1fr; gap: 3px 12px; margin: 0; }
#controls-card dt { color: #fff; font-weight: 700; white-space: nowrap; }
#controls-card dd { margin: 0; color: #aab6bb; }
/* THE PAGE (2026-09-25): three short sections, centred over the world at the start and on H */
#controls-card { left: 50%; right: auto; top: 50%; transform: translate(-50%, -50%); width: min(560px, calc(100vw - 32px)); max-height: calc(100vh - 32px); overflow: auto; }
#controls-card h4 { margin: 10px 0 4px; font-size: 11px; letter-spacing: 0.16em; color: #7fa0aa; font-weight: 700; }
#controls-card footer { margin-top: 10px; font-size: 11px; color: #7fa0aa; letter-spacing: 0.08em; text-align: right; }
#tab-td.pad-labelled [data-label]::after { content: attr(data-label); position: absolute; left: 50%; bottom: calc(100% + 4px); transform: translateX(-50%); font: 700 10px/1 ui-monospace, Menlo, monospace; letter-spacing: 0.12em; color: #fff; background: rgba(8, 12, 16, 0.8); padding: 3px 5px; white-space: nowrap; pointer-events: none; }
/* ==== end CONTROLS CARD ==== */

/* THE SHOWCASE (src/fx/showcase.js): the montage over a real run of the skipped world. While it plays the HUD is
   gone — every piece of chrome inside the game tab and the navigation shell — and the cards are the only thing over
   the world. The game's own canvas stays; the minimap canvas is hidden by name because it is a canvas like the
   renderer's. Monochrome, as the rest of the game's UI. */
body.showcase-on #shell-bar,
body.showcase-on #tab-td > *:not(#td-app):not(#showcase):not(.ram-float-layer),
body.showcase-on #td-app > *:not(canvas):not(.ram-float-layer),
body.showcase-on #td-app > canvas.minimap,
body.showcase-on #td-app > canvas.laser-inset-hud { display: none !important; }   /* the ram readout is the exception: the combo over the hull IS beat C (owner, 2026-09-24). SOL-82's telemetry panels are canvases inside the game element, so they need naming; its round scope is drawn in the 3D pass and cannot be reached from here, and does not have to be — SOL-82 never comes online in the four beats */
/* THE ARRIVAL (src/fx/arrival.js): the landing and the close-up play clean, like the showcase. The chrome is gone while they run
   except Isao's panel (the close-up's lines are his) and the SKIP TUTORIAL offer (a way past the whole opening, the landing too). */
body.arrival-on #shell-bar,
body.arrival-on #tab-td > *:not(#td-app):not(#td-brief):not(#skip-tutorial),
body.arrival-on #td-app > *:not(canvas),
body.arrival-on #td-app > canvas.minimap { display: none !important; }

#showcase {
  position: absolute; inset: 0; z-index: 90;
  display: grid; grid-template-areas: 'stage'; align-items: end; justify-items: center;
  font-family: var(--font-ui, system-ui, sans-serif); color: #eef2f5;
  background: radial-gradient(120% 85% at 50% 50%, transparent 55%, rgba(4, 6, 8, 0.72) 100%);
}
/* [hidden] must win over every display here: the wireframe stage is a canvas the rail hides between its two reveals,
   and a bare `display: block` left it standing over the world shots that follow (caught in the --showcase stills). */
#showcase [hidden] { display: none !important; }
#showcase .sc-wire {
  grid-area: stage; align-self: stretch; justify-self: stretch; width: 100%; height: 100%;
  background: #05080a; display: block;
}
#showcase .sc-card {
  grid-area: stage; align-self: end; margin: 0 0 min(14vh, 120px); padding: 10px 22px 12px;
  text-align: center; max-width: min(88vw, 780px);
  background: linear-gradient(180deg, rgba(6, 9, 11, 0) 0%, rgba(6, 9, 11, 0.78) 45%);
  border-bottom: 1px solid rgba(238, 242, 245, 0.35);
}
#showcase .sc-card h1 {
  margin: 0; font-size: clamp(18px, 4.4vw, 40px); font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase; line-height: 1.1;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.85);
}
#showcase .sc-card p {
  margin: 6px 0 0; font-size: clamp(10px, 1.6vw, 14px); letter-spacing: 0.22em;
  text-transform: uppercase; color: #a9b3bb;
}
/* BEAT A's LABELS: the briefing's own treatment — a pale box standing off the model with a leader line back to the
   structure it names (src/fx/wireframe-stage.js measures the anchor, src/fx/showcase.js places the box). */
#showcase .sc-labels { grid-area: stage; align-self: stretch; justify-self: stretch; position: relative; width: 100%; height: 100%; pointer-events: none; }
#showcase .sc-labels svg { position: absolute; inset: 0; width: 100%; height: 100%; }
#showcase .sc-labels svg line { stroke: rgba(191, 230, 234, 0.55); stroke-width: 1; }
#showcase .sc-label {
  position: absolute; transform: translate(-50%, -50%); white-space: nowrap;
  padding: 2px 7px; font-size: clamp(8px, 1.2vw, 12px); letter-spacing: 0.16em; text-transform: uppercase;
  color: #d8f0f3; background: rgba(5, 12, 14, 0.72); border: 1px solid rgba(191, 230, 234, 0.4);
}
/* the card sits LOW: the frame under it is Isao's own closeup, and his face belongs in the clear upper two thirds */
#showcase .sc-finale {
  grid-area: stage; align-self: end; margin-bottom: min(8vh, 70px); text-align: center; padding: 22px 30px 26px;
  background: rgba(6, 9, 11, 0.62); border: 1px solid rgba(238, 242, 245, 0.22);
  max-width: min(92vw, 640px);
}
#showcase .sc-finale h1 {
  margin: 0; font-size: clamp(24px, 6.5vw, 58px); letter-spacing: 0.08em; text-transform: uppercase;
  font-family: var(--font-shout, var(--font-ui, system-ui, sans-serif));
  text-shadow: 0 2px 22px rgba(0, 0, 0, 0.9);
}
#showcase .sc-finale > p {
  margin: 6px 0 20px; font-size: 11px; letter-spacing: 0.3em; color: #a9b3bb; text-transform: uppercase;
}
#showcase .sc-choice { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
#showcase .sc-choice button {
  appearance: none; cursor: pointer; min-width: 190px; padding: 12px 20px;
  display: grid; gap: 3px; text-align: left;
  background: rgba(238, 242, 245, 0.06); color: #eef2f5;
  border: 1px solid rgba(238, 242, 245, 0.42); font: inherit;
}
#showcase .sc-choice button:hover, #showcase .sc-choice button:focus-visible {
  background: rgba(238, 242, 245, 0.16); border-color: #eef2f5; outline: none;
}
#showcase .sc-choice b { font-size: 15px; letter-spacing: 0.14em; }
#showcase .sc-choice small { font-size: 10px; letter-spacing: 0.1em; color: #a9b3bb; text-transform: uppercase; }
#showcase .sc-skip {
  grid-area: stage; align-self: start; justify-self: end; margin: 14px 16px;
  appearance: none; cursor: pointer; padding: 7px 14px; font: inherit;
  font-size: 10px; letter-spacing: 0.24em;
  background: rgba(6, 9, 11, 0.55); color: #a9b3bb; border: 1px solid rgba(238, 242, 245, 0.3);
}
#showcase .sc-skip:hover { color: #eef2f5; border-color: #eef2f5; }
@media (max-width: 560px) {
  #showcase .sc-choice button { min-width: 0; width: 100%; }
}

/* WHAT IS HOLDING (src/fx/integrity-hud.js, owner 2026-09-30): the Stålheart's pips and each standing gate's bar at the top
   centre over every view and seat; a hit flashes the row, a hit on the Stålheart pulses the screen's edge */
#integrity-hud { position: fixed; top: 92px; left: 50%; transform: translateX(-50%); z-index: 40; pointer-events: none;
  display: flex; flex-direction: column; align-items: center; gap: 3px; font: 600 11px ui-monospace, Menlo, monospace; letter-spacing: 0.08em; }
#integrity-hud[hidden] { display: none; }
#integrity-hud .ih-row { display: flex; align-items: center; gap: 8px; padding: 2px 8px; background: rgba(0, 0, 0, 0.5); border: 1px solid rgba(255, 255, 255, 0.12); color: #b8ffca; }
#integrity-hud .ih-row[hidden] { display: none; }
#integrity-hud .ih-lbl { font-size: 10px; letter-spacing: 0.16em; color: #4fae63; min-width: 9ch; text-align: right; }
#integrity-hud .ih-num { min-width: 5ch; }
#integrity-hud .ih-pips { font-size: 13px; letter-spacing: 0.02em; color: rgba(255, 106, 136, 0.28); }
#integrity-hud .ih-pips b { color: #ff6a88; font-weight: 400; text-shadow: 0 0 6px rgba(255, 106, 136, 0.45); }
#integrity-hud .ih-bar { position: relative; width: 120px; height: 7px; border: 1px solid rgba(184, 255, 202, 0.45); }
#integrity-hud .ih-bar i { position: absolute; inset: 0 auto 0 0; background: #b8ffca; transition: width 0.25s linear; }
#integrity-hud .ih-low .ih-bar i { background: #ffb347; }
#integrity-hud .ih-low .ih-num, #integrity-hud .ih-heart.ih-low .ih-lbl { color: #ff6a88; }
#integrity-hud .ih-down { border-color: #ff6a88; }
#integrity-hud .ih-down .ih-lbl, #integrity-hud .ih-down .ih-num { color: #ff6a88; }
#integrity-hud .ih-worn { border-color: #ffb347; }
#integrity-hud .ih-worn .ih-lbl::after { content: ' · UNDER ATTACK'; color: #ffb347; animation: ih-blink 0.6s steps(2) infinite; }
#integrity-hud .ih-hit { animation: ih-hit 0.9s ease-out; }
@keyframes ih-hit { 0%, 30% { background: rgba(255, 60, 90, 0.75); border-color: #ff6a88; transform: scale(1.12); } 100% { background: rgba(0, 0, 0, 0.5); transform: scale(1); } }
@keyframes ih-blink { 50% { opacity: 0.25; } }
#integrity-edge { position: fixed; inset: 0; z-index: 39; pointer-events: none; opacity: 0; box-shadow: inset 0 0 120px 30px rgba(255, 40, 70, 0.85); }
#integrity-edge.ie-hit { animation: ie-hit 1.1s ease-out; }
@keyframes ie-hit { 0% { opacity: 1; } 100% { opacity: 0; } }
body.mobile-shell #integrity-hud { display: none; }
body.mobile-shell #td-stats.ih-panel-hit { animation: ih-panel 0.9s ease-out; }
@keyframes ih-panel { 0%, 30% { border-color: #ff6a88; box-shadow: 0 0 0 2px rgba(255, 60, 90, 0.8), inset 0 0 24px rgba(255, 60, 90, 0.45); } }
@media (prefers-reduced-motion: reduce) { #integrity-hud .ih-hit, #integrity-edge.ie-hit, #td-stats.ih-panel-hit { animation-duration: 0.01s; } }

/* THE SEAT GLIDE (src/fx/seat-glide.js, owner 2026-09-30): the optic's chrome is gone while the camera travels to the new post and
   fades in as it lands */
#sentry-pilot, #story-scope { transition: opacity 0.35s ease-out; }
body.seat-gliding #sentry-pilot, body.seat-gliding #story-scope, body.seat-gliding #story-monitor { opacity: 0; transition: none; }   /* and the Quiver's spotting monitor: only the HUD of what we sit in (2026-10-03) */

/* THE SEATS SAY LESS ON A PHONE (2026-09-19-phone-seats-say-less, built 2026-10-01; owner: "too much info on screen"): in a seat
   the thumb needs the gun buttons, the aim, the fire button, one line of state and the strip. The telemetry plate and the panel's
   duplicate cue line go; the ribbon under the reticle is the one state line; the shell's build tag hides while any seat is up;
   the seat hint is a footer shown once per browser (src/sentry-pilot.js, src/fx/laser-seat.js). Desktop unchanged. */
body.mobile-shell #gunship-hud .ro { display: none; }
body.mobile-shell #sentry-pilot output { display: none; }
body.mobile-shell:has(#sentry-pilot) #shell-bar, body.mobile-shell:has(#laser-seat) #shell-bar { display: none; }
body.mobile-shell #sentry-pilot footer.hint-seen, body.mobile-shell #laser-seat footer.hint-seen { display: none; }

/* A LAB EMBEDDED IN ANOTHER VIEW (?embed=1): the Units bench frames the sentry lab for every sentry (owner, 2026-10-02: "by default it
   shows the EXACT view of the sentry/impact"). The lab is untouched; only its authoring transfer panel steps aside for the bench's chrome */
body.embedded .preset-panel { display: none; }
#units-lab { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; background: #05070c; }
#units-lab[hidden] { display: none; }

/* THE PAINT SHOP (src/fx/paint-shop.js): a calm card over the paused game, the hull behind it the preview */

#units-inset::after { content: attr(data-label); position: absolute; left: 6px; top: 5px; }

/* THE SEAT'S VIGNETTE (src/sentry-pilot.js): a Rotor's or a Quiver's optic reads as a different view */
#seat-vignette { position: fixed; inset: 0; pointer-events: none; z-index: 4; background: radial-gradient(ellipse 66% 62% at 50% 50%, transparent 40%, rgba(0, 0, 0, .7) 70%, rgba(0, 0, 0, .96) 92%); }
#seat-vignette[hidden] { display: none; }
/* only once the eye is ON the optic (owner, 2026-10-02: "only once we are entirely inside a sentry, not before"): never over the glide into a seat, and it closes in as the glide lands */
#seat-vignette { animation: seat-vignette-in .6s ease-out; }
body.seat-gliding #seat-vignette { display: none; }
@keyframes seat-vignette-in { from { opacity: 0; } to { opacity: 1; } }
#seat-vignette:not([hidden]) ~ #sentry-pilot { z-index: 5; }   /* the seat's own panel and hint stay over the vignette */
.radial-center .radial-gain { display: block; margin-top: 4px; font-size: 9px; letter-spacing: .04em; color: #9fdcff; line-height: 1.3; max-width: 120px; }

/* FIRST CONTACT (src/fx/contact-card.js): Isao's scanner logs a new kind of body at the left edge, never over the middle */
.contact-card { position: absolute; left: 16px; top: 34%; z-index: 11; pointer-events: none; min-width: 190px; max-width: 250px; padding: 9px 12px 10px 14px;
  background: rgba(6, 10, 14, .82); border-left: 3px solid var(--tint); color: #e6eff2; font: 13px/1.35 'Share Tech Mono', ui-monospace, monospace;
  box-shadow: 0 0 18px rgba(0, 0, 0, .5); animation: contact-in .35s ease-out; transition: opacity .45s, transform .45s; }
.contact-card.out { opacity: 0; transform: translateX(-24px); }
.contact-head { font-size: 10px; letter-spacing: .2em; color: var(--tint); }
.contact-name { font-size: 16px; font-weight: 700; letter-spacing: .08em; margin: 2px 0 3px; }
.contact-role { opacity: .85; }
.contact-facts { margin-top: 5px; font-size: 10px; letter-spacing: .12em; color: #9fb4bd; }
@keyframes contact-in { from { opacity: 0; transform: translateX(-24px); } to { opacity: 1; transform: none; } }
@media (max-width: 600px) { .contact-card { top: 22%; min-width: 0; max-width: 60vw; } }
/* the story's sentry count under the biomass in the radial's centre (src/fx/shop-radial.js) */
#td-shop .radial-center small { display: block; font-size: 8px; letter-spacing: .04em; opacity: .8; line-height: 1.1; }
#td-shop .radial-center:has(small) { flex-direction: column; }

/* THE SUMMARY ON ONE SCREEN (src/fx/sector-debrief.js pageHero, owner 2026-10-03): tighter, every number at once, small citation chips */
.sdb-sum { gap: clamp(8px, 1.4cqi, 14px); }
.sdb-sum .sdb-word { font-size: clamp(26px, 4.6cqi, 54px); margin: 0; }
.sdb-sum-head { display: flex; align-items: baseline; gap: 18px; flex-wrap: wrap; }
.sdb-sum-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; background: var(--sdb-faint); border-block: 1px solid var(--sdb-faint); }
.sdb-sum-t { background: rgba(4, 12, 12, 0.92); padding: 8px 12px; min-width: 0; }
.sdb-sum-t > span { display: block; font-size: 12px; letter-spacing: 0.2em; color: var(--sdb-dim); white-space: nowrap; }
.sdb-sum-t .sdb-num { font-size: clamp(20px, 3cqi, 34px); }
.sdb-sum-t small { display: block; font-size: 11px; letter-spacing: 0.12em; color: var(--sdb-dim); margin-top: 2px; }
.sdb-sum-t.is-warn .sdb-num { color: #ffb26b; }
.sdb-sum-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.sdb-sum-chip { display: flex; gap: 8px; align-items: baseline; padding: 4px 10px; border: 1px solid var(--sdb-faint); font-size: 13px; letter-spacing: 0.1em; color: var(--sdb-ink); }
.sdb-sum-chip b { color: var(--sdb-hi); } .sdb-sum-chip em { font-style: normal; color: var(--sdb-hi); }
.sdb-sum-spark > span { display: block; font-size: 12px; letter-spacing: 0.2em; color: var(--sdb-dim); margin-bottom: 4px; }
.sdb-sum-spark .sdb-spark { display: block; width: 100%; height: clamp(36px, 6cqi, 56px); }
.sdb-sum-ladder { display: flex; height: 6px; margin-top: 4px; gap: 1px; }
.sdb-sum-cites { display: flex; gap: 6px; flex-wrap: wrap; }
.sdb-sum-cite { font-size: 11px; letter-spacing: 0.14em; padding: 3px 8px; border: 1px solid var(--sdb-accent); color: var(--sdb-accent); background: var(--sdb-accent-soft); transform: rotate(-1.5deg); }
.sdb-sum-cite:nth-child(2n) { transform: rotate(1.5deg); }
.sdb-sum-cite.is-none { border-color: var(--sdb-faint); color: var(--sdb-dim); transform: none; }
@container (max-width: 560px) { .sdb-sum-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* a palette swatch is two-tone: the armour over the edge (src/content/dyes.js PALETTES) */

/* THE CALL TO ACTION (owner, 2026-10-03: "larger quick CALL to Action in Red in the center ... ROTOR MANUAL OVERRIDE!"): the seat hand-over's
   order, bigger and redder than a callout, held 2.6 s; Isao's panel keeps the why */
.co-cta { color: #ff3b2f; --r: 2.4; letter-spacing: 0.12em; text-shadow: 0 0 22px rgba(255, 50, 40, 0.9), 0 0 60px rgba(255, 30, 20, 0.6), var(--shout-halo); animation-duration: 2.6s; }
/* THE ORDER (src/fx/order-callout.js): the same shout in its own centre box, a seat or not, with the keys under it in small type */
#td-order { position: absolute; top: 38%; left: 50%; transform: translateX(-50%); z-index: 12; pointer-events: none; text-align: center; width: max-content; max-width: 96vw; }
#td-order small { display: block; margin-top: 0.6em; font-size: 0.3em; letter-spacing: 0.2em; color: #ffd6cf; }

/* THE DAY DIAL (src/fx/daylight.js makeDial): top centre beside the sound toggle, a lit disc and its tick going round with the day */
#day-dial { position: absolute; top: 56px; left: calc(50% - 44px); width: 16px; height: 16px; border-radius: 50%; z-index: 11; pointer-events: none;
  border: 1px solid rgba(159, 220, 255, .55); box-shadow: 0 0 8px rgba(255, 230, 180, .25); transition: background .6s linear; }
#day-dial i { position: absolute; inset: -4px; border-radius: 50%; }
#day-dial i::before { content: ''; position: absolute; left: 50%; top: 0; width: 2px; height: 4px; margin-left: -1px; background: #cfe9ff; }

/* THE WHY, LACONIC (src/fx/mission-card.js): the beats over the landing, one word at a time, then Isao's one line */
#mission-card { position: absolute; top: 15%; left: 50%; transform: translateX(-50%); z-index: 13; pointer-events: none; text-align: center; width: max-content; max-width: 92vw; transition: opacity .6s; }
#mission-card.out { opacity: 0; }
#mission-card .mc-beats { display: flex; gap: 0.9em; justify-content: center; flex-wrap: wrap; font-family: var(--font-shout); font-size: calc(var(--shout-unit) * 1.1); letter-spacing: .16em; color: #eafff2; text-shadow: var(--shout-halo); }
#mission-card .mc-beats span { opacity: 0; animation: mc-in .35s ease-out forwards; }
#mission-card .mc-beats span:last-child { color: #ffd27a; }
#mission-card .mc-line { margin-top: 10px; font: 14px/1.4 'Share Tech Mono', ui-monospace, monospace; letter-spacing: .08em; color: #9fdcff; opacity: 0; animation: mc-in .5s ease-out forwards; }
@keyframes mc-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
/* THE PAINT SHOP'S SCREEN (owner, 2026-10-03: "a larger screen, like the Unit View"): the hull on its turntable left, the looks and paints right */
#paint-shop { position: fixed; inset: 0; z-index: 72; display: flex; align-items: center; justify-content: center; padding: 16px; background: rgba(0, 0, 0, .78); }
#paint-shop .ps-card { width: min(1100px, 100%); max-height: calc(100vh - 32px); overflow: auto; background: rgba(4, 12, 16, .96); border: 1px solid #5d7a84; padding: 16px 20px; font: 12px/1.5 ui-monospace, Menlo, monospace; color: #e8f2f4; letter-spacing: .06em; box-sizing: border-box; }
#paint-shop header { font-size: 16px; font-weight: 700; letter-spacing: .14em; color: #9fdcff; }
#paint-shop p { margin: 6px 0 12px; opacity: .85; } #paint-shop p span { display: block; }
#paint-shop .ps-body { display: grid; grid-template-columns: minmax(0, 3fr) minmax(260px, 2fr); gap: 18px; align-items: start; }
#paint-shop .ps-stand { position: relative; }
#paint-shop .ps-stand canvas { width: 100%; aspect-ratio: 4 / 3; display: block; background: radial-gradient(ellipse at 50% 40%, #1d2c36 0%, #071016 70%); border: 1px solid rgba(159, 220, 255, .2); touch-action: none; cursor: grab; }
#paint-shop .ps-name { position: absolute; left: 12px; bottom: 10px; font-size: 15px; font-weight: 700; letter-spacing: .16em; color: #9fdcff; pointer-events: none; }
#paint-shop .ps-row { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; margin: 0 0 16px; }
#paint-shop .ps-slot { color: #9fdcff; font-size: 11px; letter-spacing: .14em; }
#paint-shop .ps-sws { display: flex; gap: 8px; flex-wrap: wrap; }
#paint-shop .ps-sw { display: flex; flex-direction: column; align-items: center; gap: 5px; background: transparent; border: 1px solid rgba(159, 220, 255, .2); color: #c9d6dc; padding: 6px; min-width: 84px; font: 10px/1 ui-monospace, Menlo, monospace; letter-spacing: .08em; cursor: pointer; }
#paint-shop .ps-pal i { width: 64px; height: 36px; background: linear-gradient(135deg, var(--c) 0 62%, var(--e) 62% 100%); border: 1px solid rgba(255, 255, 255, .25); }
#paint-shop .ps-pal span { white-space: nowrap; }
#paint-shop .ps-sw.on { border-color: #6fe6ff; color: #6fe6ff; box-shadow: 0 0 0 1px #6fe6ff inset; }
#paint-shop .ps-sw.locked { opacity: .35; cursor: default; }
#paint-shop footer { display: flex; justify-content: space-between; align-items: center; margin-top: 4px; color: #5d7a84; font-size: 10px; letter-spacing: .14em; }
#paint-shop footer button { font: 12px/1 ui-monospace, Menlo, monospace; letter-spacing: .12em; padding: 8px 20px; background: #6fe6ff; color: #061018; border: 0; cursor: pointer; }
@media (max-width: 760px) { #paint-shop { padding: 10px; } #paint-shop .ps-card { padding: 12px 14px; } #paint-shop .ps-body { grid-template-columns: 1fr; } #paint-shop .ps-stand canvas { aspect-ratio: 16 / 10; } #paint-shop .ps-sw { min-width: 68px; } #paint-shop .ps-pal i { width: 52px; height: 28px; } }

/* THE START GATE, NOW THE WELCOME (src/fx/start-gate.js; owner, 2026-10-05): the A6 settlement pocket guide over the page while the
   landing loads, its paper and ink kept, every rule scoped here; START A NEW PLANET is the gesture that starts the sound */
#start-gate { --paper: #f4eedc; --ink: #203e42; --teal: #286b69; --orange: #c45431; --line: #d4c9ad; position: fixed; inset: 0; z-index: 90; overflow-y: auto; overscroll-behavior: contain; color-scheme: light; color: var(--ink); font-family: Arial, Helvetica, sans-serif; background: radial-gradient(#80704416 .65px, transparent .65px) 0 0/5px 5px, var(--paper); transition: opacity .5s; }
#start-gate.out { opacity: 0; pointer-events: none; }
#start-gate *, #start-gate *::before { box-sizing: border-box; }
#start-gate button { font: inherit; cursor: pointer; }
#start-gate button:focus-visible { outline: 3px solid var(--orange); outline-offset: 4px; }
#start-gate .wg { max-width: 1540px; margin: auto; padding: 18px 36px 24px; }
#start-gate .edition, #start-gate .eyebrow, #start-gate .phase, #start-gate .annotation, #start-gate .seal > span, #start-gate .stamp, #start-gate .towel-note span, #start-gate .completion, #start-gate .fineprint, #start-gate .wg-pop span { font-family: 'Courier New', monospace; }
#start-gate .edition { display: flex; justify-content: space-between; border-bottom: 1px solid var(--ink); padding-bottom: 10px; font-size: 10px; letter-spacing: 1px; font-weight: bold; }
#start-gate .headline { display: flex; justify-content: space-between; align-items: center; padding: 18px 0 22px; }
#start-gate .eyebrow { font-size: 10px; letter-spacing: 2px; margin: 0 0 10px; color: var(--orange); font-weight: bold; }
#start-gate h1 { font-size: clamp(38px, 4.6vw, 68px); line-height: .97; letter-spacing: -3px; margin: 0; font-weight: 900; }
#start-gate h1 em { color: var(--orange); font-style: normal; }
#start-gate .intro { font-size: 13px; margin: 13px 0 0; }
#start-gate .seal { border: 2px solid var(--orange); outline: 1px solid var(--orange); outline-offset: 5px; width: 132px; height: 132px; border-radius: 50%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px; color: var(--orange); transform: rotate(12deg); margin-right: 25px; flex-shrink: 0; }
#start-gate .seal > span { font-size: 9px; letter-spacing: 1px; }
#start-gate .seal strong { text-align: center; font-size: 24px; line-height: 1; }
#start-gate .steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); list-style: none; margin: 0; padding: 0; border: 1px solid var(--line); border-radius: 14px; overflow: hidden; background: #fffaf044; }
#start-gate .step { position: relative; padding: 16px 20px 18px; display: flex; flex-direction: column; min-width: 0; }
#start-gate .step + .step { border-left: 1px dashed #b4aa91; }
#start-gate .step-top { display: flex; align-items: center; gap: 12px; }
#start-gate .number { font-weight: 900; font-size: 34px; line-height: 1; color: var(--orange); letter-spacing: -2px; }
#start-gate .phase { font-size: 9px; letter-spacing: 1px; font-weight: bold; }
#start-gate .arrow { margin-left: auto; font-size: 22px; color: var(--orange); }
#start-gate .drawing { height: 180px; position: relative; margin: 8px -8px 12px; background: radial-gradient(ellipse at 50% 50%, #c9d9c733, transparent 68%); }
#start-gate .drawing::before { content: ''; position: absolute; inset: 22px 16px; border: 1px solid #286b6917; border-radius: 50%; transform: rotate(-18deg); }
#start-gate .drawing img { position: absolute; object-fit: contain; width: 100%; height: 100%; mix-blend-mode: multiply; }
#start-gate .annotation { position: absolute; bottom: 0; left: 0; right: 0; text-align: center; font-size: 8px; letter-spacing: 1px; color: var(--teal); }
#start-gate .duo .upper { height: 61%; width: 72%; right: 0; top: -8px; }
#start-gate .duo .lower { height: 65%; width: 78%; left: -8px; bottom: 8px; }
#start-gate .orbital .upper { height: 50%; width: 55%; right: 0; top: -4px; }
#start-gate .orbital .lower { height: 70%; width: 90%; bottom: 4px; }
#start-gate .patrol .upper { width: 70%; height: 44%; right: 0; top: 0; }
#start-gate .patrol .lower { width: 74%; height: 44%; left: 0; top: 46%; bottom: auto; }
#start-gate h2 { font-size: clamp(22px, 2vw, 30px); letter-spacing: -1.2px; line-height: 1.05; margin: 0 0 12px; }
#start-gate .step p { font-size: 13px; line-height: 1.55; margin: 0; }
#start-gate .step .aside { font-family: Georgia, serif; font-style: italic; font-size: 12px; color: #68746b; margin: 12px 0 18px; }
#start-gate .wg-key { display: inline; padding: 0 1px; border: 0; background: none; color: inherit; font-weight: 700; text-decoration: underline dotted var(--orange) 2px; text-underline-offset: 3px; cursor: help; }
#start-gate .wg-key:hover, #start-gate .wg-key:focus-visible { color: var(--orange); }
#start-gate .stamp { margin-top: auto; background: transparent; border: 1px solid #a9b4a1; color: var(--teal); border-radius: 4px; padding: 10px; text-align: left; font-size: 10px; min-height: 36px; transition: background .15s, transform .15s; }
#start-gate .stamp span { float: right; }
#start-gate .stamp:hover { background: #dce5d4; transform: rotate(-1deg); }
#start-gate .stamp[aria-pressed=true] { color: var(--orange); border: 2px solid var(--orange); padding: 9px; background: #c4543109; font-weight: bold; transform: rotate(-2deg); }
#start-gate .guide-footer { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 18px 0 14px; }
#start-gate .towel-note { display: flex; align-items: center; gap: 15px; }
#start-gate .towel-note svg { height: 49px; width: 48px; stroke: var(--orange); stroke-width: 1.8; fill: none; transform: rotate(-10deg); }
#start-gate .towel-note strong { font-family: Georgia, serif; font-style: italic; font-size: 27px; font-weight: normal; }
#start-gate .towel-note span { display: block; font-size: 8px; letter-spacing: .7px; margin-top: 5px; }
#start-gate .completion { text-align: right; font-size: 9px; letter-spacing: .5px; }
/* THE CALL TO ACTION: big, orange, breathing, the one thing on the page that starts the game */
#start-gate .cta-row { position: sticky; bottom: 0; z-index: 1; display: flex; justify-content: center; padding: 22px 0 18px; margin: 0 -36px; background: linear-gradient(transparent, var(--paper) 38%); }   /* always in sight, the guide scrolling under it */
#start-gate .wg-cta { font: 900 clamp(22px, 3.4vw, 40px)/1 Arial, Helvetica, sans-serif; letter-spacing: -.5px; color: #fff8ea; background: var(--orange); border: 0; border-radius: 999px; padding: .62em 1.5em; box-shadow: 0 0 0 4px var(--paper), 0 0 0 6px var(--orange), 0 10px 30px #c4543166; animation: wg-beckon 2.2s ease-in-out infinite; transition: transform .15s, background .15s; }
#start-gate .wg-cta span { display: inline-block; margin-left: .3em; transition: transform .2s; }
#start-gate .wg-cta:hover { background: #d8603a; transform: scale(1.04) rotate(-1deg); animation-play-state: paused; }
#start-gate .wg-cta:hover span { transform: translateX(.25em); }
@keyframes wg-beckon { 0%, 100% { transform: scale(1); box-shadow: 0 0 0 4px var(--paper), 0 0 0 6px var(--orange), 0 10px 30px #c4543166; } 50% { transform: scale(1.035); box-shadow: 0 0 0 4px var(--paper), 0 0 0 9px #c4543188, 0 14px 44px #c4543199; } }
#start-gate .fineprint { font-size: 9px; line-height: 1.5; padding-top: 10px; color: #657266; }
/* a keyword's wireframe: a small dark card, the unit turning in pale cyan (src/fx/wireframe-stage.js) */
#start-gate .wg-pop { position: fixed; z-index: 2; width: 260px; padding: 8px 8px 10px; background: #0b1a1d; border: 1px solid #2f5a62; border-radius: 10px; box-shadow: 0 12px 36px #0006; color: #bfe6ea; pointer-events: none; }
#start-gate .wg-pop canvas { display: block; width: 100%; height: 170px; border-radius: 6px; background: radial-gradient(ellipse at 50% 60%, #12313599, #071114); }
#start-gate .wg-pop b { display: block; margin-top: 8px; font-size: 15px; letter-spacing: 2px; }
#start-gate .wg-pop span { display: block; margin-top: 3px; font-size: 10px; letter-spacing: .5px; color: #8fb9bd; }
@media (min-width: 1000px) and (max-height: 800px) { #start-gate .wg { padding-top: 12px; } #start-gate .headline { padding: 10px 0 12px; } #start-gate h1 { font-size: 46px; } #start-gate .seal { width: 104px; height: 104px; } #start-gate .seal strong { font-size: 20px; } #start-gate .drawing { height: 130px; } #start-gate .step { padding: 12px 16px; } #start-gate .guide-footer { padding: 10px 0; } #start-gate .step .aside { margin: 8px 0 12px; } }
@media (max-width: 999px) { #start-gate .wg { padding: 18px 24px; } #start-gate .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } #start-gate .step:nth-child(3) { border-left: 0; } #start-gate .step:nth-child(n+3) { border-top: 1px dashed #b4aa91; } #start-gate .drawing { height: 200px; } #start-gate .edition span:last-child { display: none; } #start-gate .guide-footer { align-items: flex-start; } }
@media (max-width: 560px) { #start-gate .wg { padding: 16px; } #start-gate .seal { width: 75px; height: 75px; margin: 0 8px 0 12px; gap: 3px; } #start-gate .seal strong { font-size: 16px; } #start-gate .seal > span { font-size: 5px; letter-spacing: 0; } #start-gate h1 { font-size: 36px; letter-spacing: -1.8px; } #start-gate .steps { grid-template-columns: 1fr; } #start-gate .step + .step { border-left: 0; border-top: 1px dashed #b4aa91; } #start-gate .guide-footer { flex-direction: column; gap: 14px; } #start-gate .completion { text-align: left; } #start-gate .stamp { font-size: 12px; min-height: 44px; } #start-gate .wg-cta { width: 100%; } }
@media (prefers-reduced-motion: reduce) { #start-gate .wg-cta { animation: none; } }

/* THE ORBITAL CONSTELLATION (src/fx/orbital-finale.js): its own canvas over everything, Isao's line under it */
#orbital-finale { position: fixed; inset: 0; z-index: 2147483000; background: #020610; opacity: 0; }
#orbital-finale canvas { display: block; width: 100%; height: 100%; }
#orbital-finale .of-line { position: absolute; left: 50%; bottom: clamp(56px, 12vh, 120px); translate: -50% 0; margin: 0; max-width: min(92vw, 900px); text-align: center;
  font: 600 clamp(20px, 3.2vw, 38px)/1.2 ui-monospace, 'JetBrains Mono', monospace; letter-spacing: 0.04em; color: #f2fff9; text-shadow: 0 0 18px rgba(53, 255, 208, 0.55); opacity: 0; }
#orbital-finale .of-line.on { animation: of-line 6.5s ease forwards; }
@keyframes of-line { 0% { opacity: 0; translate: -50% 8px; } 8% { opacity: 1; translate: -50% 0; } 85% { opacity: 1; } 100% { opacity: 0; } }
#orbital-finale .of-count { position: absolute; left: 16px; top: 14px; margin: 0; font: 13px/1 ui-monospace, monospace; letter-spacing: 0.14em; color: #62978d; }
#orbital-finale .of-skip { position: absolute; right: 16px; bottom: 14px; margin: 0; font: 12px/1 ui-monospace, monospace; letter-spacing: 0.14em; color: #62978d; }
