/* ──────────────────────────────────────────────────────────
   IRON ABYSS — TERMINAL UI STYLESHEET
   See CLAUDE.md → AESTHETIC RULES before changing anything.
   Palette: amber on near-black. No smooth gradients in 3D.
   ────────────────────────────────────────────────────────── */

/* Share Tech Mono — bundled locally so the packaged Electron build
   plays offline. Pulled from Google Fonts (Apache 2.0 license). */
@font-face {
  font-family: 'Share Tech Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('./assets/fonts/share-tech-mono.woff2') format('woff2');
}

* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  --amber:      #ff8c00;
  --amber-mid:  #cc5500;
  --amber-dim:  #6b3300;
  --amber-dark: #2d1500;
  --amber-deep: #150a00;
  --bg:         #010101;
  --screen-bg:  #030106;
  --border:     #3d1e00;
  --warn:       #cc1500;

  /* Cockpit zoom factor. Applied on #cockpit via CSS `zoom` so
     the hull shell, three monitor panels, and Three.js canvas
     scale together. Overlays (commander-select, pause menu,
     story beats, etc.) render at their designed sizes — they
     are NOT descendants of #cockpit and CSS `zoom` on <body>
     was tested and interacts poorly with `position: fixed`
     layouts across browser vs Electron runtimes (the Itch web
     embed broke commander-select at zoom > 1). Scoping to
     #cockpit only avoids that entirely.
     renderer.js's resize() reads canvasWrap.clientWidth post-
     zoom so the 3D scene rebuffers at the larger resolution
     and stays crisp. Tune here in one place. */
  --ui-zoom: 1.25;

  /* Vertical offset applied to #cockpit via translateY. At the
     default 1.25 zoom the hull image overflows the viewport top+
     bottom evenly, which shows too much dark cockpit ceiling and
     hides the console/controls at the bottom. A NEGATIVE offset
     shifts the cockpit UP, trimming the top dark band and
     revealing more of the bottom controls. Tune here — more
     negative = more upward shift. */
  --cockpit-y-shift: -4vh;
}

body {
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  color: var(--amber-mid);
  overflow: hidden;
  user-select: none;
}

/* ── COCKPIT SHELL ─────────────────────────────
   The hull asset (imgs/amber_cockpit_updated.png) is a 3172×1984 image
   with three amber-glow "screen" rectangles. We use it as the cockpit
   background and absolutely position the three monitor panels over
   those zones using percentages, so the layout scales cleanly when the
   viewport changes. The amber glow bleeds out around our panel
   borders, so small gaps read as part of the hull rather than as misalignment. */
#cockpit {
  position: relative;
  display: block;
  aspect-ratio: 3172 / 1984;
  width: min(100vw, calc(100vh * 3172 / 1984));
  max-width: 1800px;
  background: url('imgs/amber_cockpit_updated.png') center/contain no-repeat;
  transform: translateY(var(--cockpit-y-shift));
  zoom: var(--ui-zoom);
}

/* ── SHARED MONITOR STYLES ─────────────────────
   The monitors are styled as CRT screens. Two overlay pseudos compose
   the effect:
     ::before  vignette darkening at the corners
     ::after   scan lines + fractal-noise fuzz (multi-background)
   Plus a phosphor halo via text-shadow on every descendant, and a slow
   irregular opacity flicker on the .monitor itself. */
.monitor {
  background: var(--screen-bg);
  border: 1px solid var(--amber-dim);
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
  text-shadow: 0 0 3px rgba(255, 140, 0, 0.30);   /* phosphor halo */
  animation: crt-flicker 7s infinite;
  box-shadow:
    0 0 0 1px #000,
    0 0 24px rgba(160,70,0,0.25),
    inset 0 0 40px rgba(0,0,0,0.7);
}

/* Vignette — darker at corners, brighter in the middle. */
.monitor::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center,
    transparent 55%,
    rgba(0, 0, 0, 0.55) 100%);
  pointer-events: none;
  z-index: 19;
}

/* Scan lines + a static fractal-noise overlay (data-URI SVG, free).
   The scan lines drift one cycle per 0.6s to fake CRT refresh wobble. */
.monitor::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    repeating-linear-gradient(
      180deg,
      transparent 0px,
      transparent 2px,
      rgba(0,0,0,0.22) 2px,
      rgba(0,0,0,0.22) 3px
    ),
    url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.55 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: auto, 120px 120px;
  pointer-events: none;
  opacity: 0.95;
  z-index: 20;
  animation: scanline-drift 0.6s linear infinite;
}

/* Slow phosphor flicker — never fully steady. */
@keyframes crt-flicker {
  0%, 92%, 100% { opacity: 1; }
  93%           { opacity: 0.93; }
  94%           { opacity: 1; }
  96%           { opacity: 0.96; }
  97%           { opacity: 1; }
}

/* Scan lines drift down 3px (one full cycle) every 0.6s. */
@keyframes scanline-drift {
  from { background-position: 0 0, 0 0; }
  to   { background-position: 0 3px, 0 0; }
}

.monitor-header {
  background: var(--amber-deep);
  border-bottom: 1px solid var(--amber-dark);
  padding: 6px 10px;
  font-size: 12px;
  letter-spacing: 3px;
  color: var(--amber);
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-shrink: 0;
  text-transform: uppercase;
}

.monitor-header .right {
  color: var(--amber-dim);
  font-size: 11px;
}

.monitor-footer {
  background: var(--amber-deep);
  border-top: 1px solid var(--amber-dark);
  padding: 5px 10px;
  font-size: 11px;
  color: var(--amber-dim);
  display: flex;
  gap: 12px;
  flex-shrink: 0;
  letter-spacing: 1px;
}

.monitor-footer .active {
  color: var(--amber);
}

/* ── BLINK ───────────────────────────────────── */
@keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}
.blink { animation: blink 1.4s step-end infinite; }

@keyframes slow-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}
.slow-blink { animation: slow-blink 2.5s step-end infinite; }

/* Monitor positions over the hull's amber-screen zones. Percentages are
   tuned by eye against imgs/cockpit_updated.png — easy to nudge.
   Side terminals get a small Y-axis rotation so they read as angled
   into the cockpit, matching the hull's perspective. */
#screen-left {
  position: absolute;
  left:  7.5%; top: 23%;
  width: 12%;  height: 56%;
  transform: perspective(1400px) rotateY(25deg);
  transform-origin: center;
}
#screen-center {
  position: absolute;
  left:  28%;  top: 25%;
  width: 44%;  height: 53%;
}
#screen-right {
  position: absolute;
  left:  80.5%; top: 23%;
  width: 12%;   height: 56%;
  transform: perspective(1400px) rotateY(-25deg);
  transform-origin: center;
}

/* Post-Sprint-21 zoom pass: hide the footer strips on the side
   panels. Removes UNIT/SLV/KIT counters on the crew manifest and
   the WASD/CLICK legend on the action terminal — reclaims the
   vertical space for pilot cards and combat buttons so all three
   pilots fit and every [key] command is visible. The center
   monitor keeps its footer (HDG / POS / THREAT / status). */
#screen-left  .monitor-footer,
#screen-right .monitor-footer { display: none; }

/* Also hide the "// EXPLORATION MODE" / "// COMBAT MODE" / "// UNIT
   LOST" label on the action terminal. The mode is already implied by
   which buttons are showing (exploration keys vs combat keys); the
   enemy name during combat is already visible on the target monitor.
   The JS in actionTerminal.js still writes to this element, so
   removing the display doesn't break anything — un-hide here to
   bring it back. */
#action-mode-label { display: none; }

/* ── CENTER MONITOR ──────────────────────────── */
#screen-center {
  border-color: var(--amber-mid);
  box-shadow:
    0 0 0 1px #000,
    0 0 40px rgba(200,100,0,0.35),
    0 0 80px rgba(150,60,0,0.15),
    inset 0 0 40px rgba(0,0,0,0.5);
}

#screen-center .monitor-header {
  border-bottom-color: var(--amber-dim);
  letter-spacing: 4px;
}

#canvas-wrap {
  flex: 1;
  position: relative;
  overflow: hidden;
  background: #000;
}

#game-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* Sprint 19.9.17 — scene-dust overlay. Sits on top of the WebGL game
   canvas at the same size, draws JS-particle dust shed from enemy
   sprites where the sweep band tears them. pointer-events: none so
   clicks fall through to the canvas. z-index above the sweep but
   below the HUD chrome (which lives at z-index ≥ 16). */
#scene-dust-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 14;
}

/* Sensor sweep — single scan line */
#sensor-sweep {
  /* Sprint 19.9.10 — visible sweep line hidden. The fullscreen VHS
     distortion band in the post-process (src/renderer.js, u_sweepPhase)
     does the sweep visually now; the literal CSS line was drifting out
     of sync with the band during fast moves (pingSweep speed-ups), so
     keeping only the fuzz reads cleaner. The element + keyframes are
     preserved so pingSweep() in targetMonitor.js keeps working and we
     can bring the line back with a single property if desired. */
  display: none;
  position: absolute;
  left: 0; right: 0;
  height: 2px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(255,120,0,0.0) 10%,
    rgba(255,140,0,0.7) 40%,
    rgba(255,160,0,0.9) 50%,
    rgba(255,140,0,0.7) 60%,
    rgba(255,120,0,0.0) 90%,
    transparent 100%
  );
  top: -2px;
  pointer-events: none;
  z-index: 15;
  animation: sensor-scan 6s linear infinite;
}

@keyframes sensor-scan {
  from { top: -2px; }
  to   { top: 100%; }
}

/* corner brackets */
.bracket {
  position: absolute;
  width: 18px;
  height: 18px;
  border-color: var(--amber-dim);
  border-style: solid;
  z-index: 16;
  opacity: 0.6;
}
.bracket.tl { top: 6px; left: 6px;  border-width: 1px 0 0 1px; }
.bracket.tr { top: 6px; right: 6px; border-width: 1px 1px 0 0; }
.bracket.bl { bottom: 6px; left: 6px;  border-width: 0 0 1px 1px; }
.bracket.br { bottom: 6px; right: 6px; border-width: 0 1px 1px 0; }

/* crosshair */
#crosshair {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 20px;
  height: 20px;
  pointer-events: none;
  z-index: 16;
  opacity: 0.4;
}
#crosshair::before, #crosshair::after {
  content: '';
  position: absolute;
  background: var(--amber-dim);
}
#crosshair::before { width: 1px; height: 100%; left: 50%; top: 0; }
#crosshair::after  { width: 100%; height: 1px; top: 50%; left: 0; }

/* ── LEFT MONITOR — CREW ─────────────────────── */
#crew-list {
  flex: 1;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow-y: auto;
}

#crew-list::-webkit-scrollbar { width: 3px; }
#crew-list::-webkit-scrollbar-thumb { background: var(--amber-dark); }

.pilot {
  border: 1px solid var(--amber-dark);
  padding: 7px 8px;
  background: rgba(0,0,0,0.4);
  transition: border-color 0.3s;
  position: relative;          /* anchor for floating damage numbers */
  overflow: visible;
}

.pilot.critical {
  border-color: #661100;
  box-shadow: inset 0 0 10px rgba(180,0,0,0.15);
  animation: critical-pulse 2s ease-in-out infinite;
}

@keyframes critical-pulse {
  0%, 100% { box-shadow: inset 0 0 10px rgba(180,0,0,0.15); }
  50% { box-shadow: inset 0 0 18px rgba(200,0,0,0.3); }
}

/* Offline state — dim the card's contents but NOT the parent itself,
   so the OFFLINE overlay below can render at full opacity on top. */
.pilot.offline {
  border-color: #0d0600;
  position: relative;
  background: rgba(0, 0, 0, 0.55);
}
.pilot.offline > .pilot-header,
.pilot.offline > .bar-label,
.pilot.offline > .bar-track,
.pilot.offline > .pilot-status,
.pilot.offline > .pilot-badge-row {
  opacity: 0.25;
}
/* Delayed OFFLINE reveal — fades in 1.5s after a pilot card flips to
   the offline state, paired with the playShortCircuit() zap in
   combat.js so the visual + audio land together. Slight glitch flicker
   in the keyframe to match the audio character. */
.pilot.offline::after {
  content: 'OFFLINE';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 26px;
  letter-spacing: 7px;
  color: #cc1500;
  text-shadow: 0 0 12px rgba(204, 21, 0, 0.7);
  background: rgba(0, 0, 0, 0.45);
  opacity: 0;
  pointer-events: none;
  z-index: 5;
  animation: pilot-offline-reveal 0.55s ease-out 1.5s forwards;
}
@keyframes pilot-offline-reveal {
  0%   { opacity: 0;   transform: scale(1.08); filter: blur(2px); }
  35%  { opacity: 1;   transform: scale(0.98); filter: blur(0); }
  45%  { opacity: 0.55; }
  60%  { opacity: 1; }
  72%  { opacity: 0.6; }
  100% { opacity: 1;   transform: scale(1); }
}

/* Active = whose turn it is in the per-pilot combat queue.
   Brightens the card and adds a soft amber glow so the eye snaps to it
   immediately when the round advances. Overrides critical's pulse. */
.pilot.active {
  border-color: var(--amber);
  box-shadow:
    0 0 12px rgba(255, 140, 0, 0.45),
    inset 0 0 10px rgba(255, 140, 0, 0.18);
  animation: none;
}

/* BREAK = pilot is out of player control. Hard red pulse + banner.
   Overrides .critical and .active because BREAK is the loudest state. */
.pilot.break {
  border-color: var(--warn);
  animation: break-pulse 0.7s ease-in-out infinite !important;
}

@keyframes break-pulse {
  0%, 100% {
    box-shadow: 0 0 16px rgba(220, 0, 0, 0.45),
                inset 0 0 12px rgba(220, 0, 0, 0.20);
    border-color: var(--warn);
  }
  50% {
    box-shadow: 0 0 28px rgba(255, 60, 0, 0.75),
                inset 0 0 18px rgba(255, 60, 0, 0.35);
    border-color: #ff3300;
  }
}

/* Red banner above the pilot's name. Persistent "WILL NOT COMPLY" by
   default; combat overrides it briefly to "FRIENDLY FIRE!" / "FROZEN" /
   "NO COMMAND" when the action resolves. */
.pilot-break-banner {
  background: var(--warn);
  color: #000;
  font-size: 11px;
  letter-spacing: 2px;
  text-align: center;
  padding: 4px 4px;
  margin-bottom: 7px;
  text-transform: uppercase;
  font-weight: bold;
  border-radius: 2px;
  animation: break-flash 0.42s step-end infinite;
}

@keyframes break-flash {
  0%, 50% { background: var(--warn); color: #000; }
  51%, 100% { background: #000; color: var(--warn); }
}

/* Level-up flash — brief amber burst on the pilot's card. Overrides
   .active and .critical for the duration of the pulse. */
.pilot.leveling {
  animation: level-up-pulse 1.6s ease-out !important;
}
@keyframes level-up-pulse {
  0% {
    box-shadow: 0 0 28px rgba(255, 200, 80, 0.95),
                inset 0 0 22px rgba(255, 200, 80, 0.40);
    border-color: #ffcc44;
  }
  60% {
    box-shadow: 0 0 16px rgba(255, 160, 40, 0.55),
                inset 0 0 12px rgba(255, 160, 40, 0.20);
    border-color: var(--amber);
  }
  100% {
    box-shadow: none;
    border-color: var(--amber-dim);
  }
}

/* Top of each pilot card — portrait thumbnail + name/role/level stack. */
.pilot-header {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-bottom: 8px;
}

.pilot-portrait {
  /* Sprint 19.7.2 — landscape container forces object-fit:cover to crop
     ~16% off the bottom of the source image, killing the baked-in
     "ID NAME // ROLE" caption strip. All other portrait selectors
     follow the same width ÷ 0.88 ≈ height pattern below. */
  width: 54px;
  height: 48px;
  object-fit: cover;
  object-position: center top;
  border: 1px solid var(--amber-dark);
  background: #000;
  flex-shrink: 0;
}

.pilot-id {
  flex: 1;
  min-width: 0;
}

.pilot-name {
  font-size: 13px;
  color: var(--amber);
  letter-spacing: 2px;
  margin-bottom: 2px;
}

.pilot-level {
  font-size: 10px;
  color: var(--amber-dim);
  letter-spacing: 1px;
  font-weight: normal;
  margin-left: 4px;
}

.pilot-role {
  font-size: 10px;
  color: var(--amber-dim);
  letter-spacing: 2px;
  text-transform: uppercase;
}

.bar-label {
  font-size: 10px;
  color: #4a2500;
  letter-spacing: 1px;
  margin-bottom: 3px;
  display: flex;
  justify-content: space-between;
}

.bar-track {
  height: 5px;
  background: #0a0300;
  border: 1px solid #1a0800;
  margin-bottom: 5px;
  overflow: hidden;
}

.bar-fill {
  height: 100%;
  background: var(--amber-mid);
  transition: width 0.5s ease;
}

.bar-fill.low  { background: var(--warn); animation: blink 0.6s step-end infinite; }
.bar-fill.insanity { background: #882200; }

.pilot-status {
  font-size: 10px;
  color: var(--amber-dim);
  letter-spacing: 1px;
}

.pilot-status.warn { color: var(--warn); animation: slow-blink 1s step-end infinite; }

/* Per-round state badges (DEFEND / SHIELD) — appear after the action
   resolves, vanish when the enemy turn ends. */
.pilot-badge-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}
.pilot-badge {
  font-size: 9px;
  letter-spacing: 2px;
  padding: 2px 6px;
  border: 1px solid var(--amber-dim);
  background: rgba(0, 0, 0, 0.4);
  text-transform: uppercase;
}
.pilot-badge.defend {
  color: var(--amber-mid);
  border-color: var(--amber-mid);
}
.pilot-badge.shield {
  color: #80c0ff;
  border-color: rgba(120, 180, 240, 0.6);
  background: rgba(20, 40, 70, 0.55);
}
.pilot-badge.frozen {
  color: #c4d8ff;
  border-color: rgba(180, 200, 255, 0.7);
  background: rgba(40, 50, 90, 0.6);
  animation: slow-blink 1.2s step-end infinite;
}

/* ── RIGHT MONITOR — ACTIONS ─────────────────── */
#action-panel {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 8px;
  gap: 8px;
  overflow: hidden;
}

.action-mode {
  font-size: 12px;
  letter-spacing: 3px;
  color: var(--amber-dim);
  padding-bottom: 7px;
  border-bottom: 1px solid var(--amber-dark);
  text-transform: uppercase;
}

/* Active-pilot readout above the combat buttons. Reserves vertical space
   even when empty (via .empty) so the panel doesn't jump as the round
   advances. */
#active-pilot-line {
  font-size: 12px;
  color: var(--amber);
  letter-spacing: 2px;
  padding: 7px 8px;
  border: 1px solid var(--amber-dark);
  background: rgba(40, 20, 0, 0.35);
  min-height: 26px;
  text-transform: uppercase;
}
#active-pilot-line.empty {
  color: transparent;
  border-color: transparent;
  background: transparent;
}

.btn-group {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 7px;
}

.act-btn {
  background: rgba(0,0,0,0.5);
  border: 1px solid var(--amber-dark);
  color: var(--amber-mid);
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  font-size: 14px;
  letter-spacing: 1.5px;
  padding: 14px 6px;
  cursor: pointer;
  text-align: center;
  transition: all 0.08s;
  text-transform: uppercase;
  line-height: 1.3;
}

.act-btn:hover {
  background: rgba(80,30,0,0.4);
  border-color: var(--amber-mid);
  color: var(--amber);
  box-shadow: 0 0 8px rgba(200,80,0,0.2);
}

.act-btn:active {
  background: rgba(120,50,0,0.5);
  transform: scale(0.97);
}

.act-btn.span2 { grid-column: span 2; }

/* Sprint 19.9.19 — playable tutorial highlight. Pulses the action-
   terminal button the tutorial wants the player to press next. No
   text overlay — the button label already carries the keybind, and
   the pulse teaches "this one." Applied by src/ui/tutorialHighlight.js
   via the .tutorial-highlight class. */
.act-btn.tutorial-highlight {
  animation: tutorial-pulse 1.05s ease-in-out infinite;
  position: relative;
  z-index: 2;
}
@keyframes tutorial-pulse {
  0%, 100% {
    border-color: var(--amber);
    color: var(--amber);
    background: rgba(80, 30, 0, 0.35);
    box-shadow: 0 0 0 rgba(255, 140, 0, 0);
    transform: scale(1);
  }
  50% {
    border-color: #ffb040;
    color: #ffd070;
    background: rgba(140, 55, 0, 0.55);
    box-shadow: 0 0 18px rgba(255, 140, 0, 0.65),
                0 0 4px  rgba(255, 200, 100, 0.55) inset;
    transform: scale(1.04);
  }
}

/* Combat buttons get dimmed + click-locked while no pilot is awaiting
   input (action resolving / enemy striking / post-impact hold). */
.btn-group.locked {
  pointer-events: none;
}
.btn-group.locked .act-btn {
  opacity: 0.35;
  color: var(--amber-dark);
  border-color: var(--amber-dark);
  background: rgba(0, 0, 0, 0.55);
  box-shadow: none;
  transition: opacity 0.15s, color 0.15s, border-color 0.15s;
}

/* ACTION RESOLVING — overlay shown over the combat buttons while the
   scheduler is mid-action. Visually communicates "nothing to click,
   the system is processing." */
#action-resolving {
  margin-top: 10px;
  padding: 10px 12px;
  border: 1px dashed var(--amber-mid);
  background: rgba(20, 10, 0, 0.55);
  color: var(--amber-mid);
  text-align: center;
  font-size: 11px;
  letter-spacing: 3px;
  pointer-events: none;
  animation: ar-pulse 1.4s ease-in-out infinite;
}
.ar-row {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  text-transform: uppercase;
}
.ar-label {
  color: var(--amber);
}
.ar-dots {
  display: inline-flex;
  gap: 3px;
}
.ar-dots span {
  display: inline-block;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--amber);
  opacity: 0.2;
  animation: ar-dot 1.1s ease-in-out infinite;
}
.ar-dots span:nth-child(2) { animation-delay: 0.18s; }
.ar-dots span:nth-child(3) { animation-delay: 0.36s; }
@keyframes ar-pulse {
  0%, 100% { box-shadow: 0 0 0 rgba(255, 140, 0, 0); }
  50%      { box-shadow: 0 0 12px rgba(255, 140, 0, 0.25); }
}
@keyframes ar-dot {
  0%, 100% { opacity: 0.2; transform: scale(0.85); }
  50%      { opacity: 1;   transform: scale(1.1); }
}

.act-btn.dim {
  opacity: 0.25;
  cursor: not-allowed;
}
.act-btn.dim:hover {
  background: rgba(0,0,0,0.5);
  border-color: var(--amber-dark);
  color: var(--amber-mid);
  box-shadow: none;
}

.act-btn.danger {
  border-color: #661100;
  color: #aa2200;
}
.act-btn.danger:hover {
  background: rgba(120,0,0,0.25);
  border-color: var(--warn);
  color: var(--warn);
  box-shadow: 0 0 8px rgba(200,0,0,0.2);
}

.divider {
  height: 1px;
  background: var(--amber-dark);
  opacity: 0.5;
}

/* Mission log lives in its own [L]-toggled center-monitor modal. */
#log-modal-body {
  flex: 1;
  padding: 12px 14px;
  overflow-y: auto;
  font-size: 13px;
  line-height: 1.55;
  background: rgba(0, 0, 0, 0.4);
}
#log-modal-body::-webkit-scrollbar { width: 4px; }
#log-modal-body::-webkit-scrollbar-thumb { background: var(--amber-dark); }

.log-modal-footer {
  border-top: 1px solid var(--amber-dim);
  padding: 6px 14px;
  display: flex;
  justify-content: flex-end;
  font-size: 10px;
  letter-spacing: 2px;
}
.log-modal-hint {
  color: var(--amber-mid);
  animation: slow-blink 2s step-end infinite;
}
.screen-modal-header .log-modal-close { float: right; margin-top: -2px; }
.log-modal-close {
  background: transparent;
  border: 1px solid var(--amber-dark);
  color: var(--amber-mid);
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 1px;
  padding: 3px 8px;
  cursor: pointer;
  transition: all 0.1s;
}
.log-modal-close:hover {
  border-color: var(--amber-mid);
  color: var(--amber);
  background: rgba(80, 30, 0, 0.4);
}

.log-line { color: #4a2500; }
.log-line.sys  { color: var(--amber-mid); }
.log-line.warn { color: #aa2200; }
.log-line.disc { color: #886600; }
.log-line.move { color: #3d2500; }

/* Progression beats — XP awards, level-ups, unlocks. Bright + bold +
   a left bar so the eye snaps to them out of the strike/hit chatter. */
.log-line.level {
  color: var(--amber);
  font-weight: bold;
  padding-left: 4px;
  border-left: 2px solid var(--amber);
  background: rgba(80, 30, 0, 0.18);
  margin: 1px 0;
}

/* ── UTILITY ─────────────────────────────────── */
.hidden { display: none !important; }

/* ── CONTACT HUD (combat overlay) ────────────── */
/* Floating readout in the corner of the viewport while a contact is engaged.
   Lives inside #canvas-wrap so it overlays the 3D scene. */
#contact-hud {
  position: absolute;
  top: 110px;       /* below the turn-order strip during combat */
  right: 12px;
  z-index: 17;
  background: rgba(0, 0, 0, 0.7);
  border: 1px solid var(--amber-mid);
  padding: 6px 10px 8px;
  min-width: 150px;
  pointer-events: none;
  box-shadow: 0 0 12px rgba(200, 80, 0, 0.25);
}

/* ── CTB TURN-ORDER STRIP ──────────────────────────────────────────
   Sits at the top center of the canvas during combat. Predicts the
   next ~6 actors; the first slot is the current actor. Enemy slots
   render in red so the player can read pacing at a glance. */
#turn-order {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 17;
  background: rgba(0, 0, 0, 0.78);
  border: 1px solid var(--amber-mid);
  padding: 5px 10px 7px;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  box-shadow: 0 0 12px rgba(200, 80, 0, 0.25);
}
.turn-order-label {
  font-size: 9px;
  color: var(--amber-dim);
  letter-spacing: 3px;
}
.turn-order-list {
  display: flex;
  align-items: center;
  gap: 4px;
}
.to-slot {
  font-size: 9px;
  letter-spacing: 1.5px;
  border: 1px solid var(--amber-dim);
  color: var(--amber-mid);
  padding: 4px 6px 3px;
  background: rgba(20, 10, 0, 0.55);
  white-space: nowrap;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  animation: to-slot-in 220ms ease-out;
}
.to-portrait {
  width: 30px;
  height: 27px;                       /* Sprint 19.7.2 — landscape crops the caption strip */
  object-fit: cover;
  object-position: center top;
  border: 1px solid var(--amber-dim);
  background: #000;
  filter: sepia(0.4) saturate(1.3) brightness(0.95) contrast(1.05);
  image-rendering: pixelated;          /* matches the chunky terminal look */
}
.to-portrait-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  color: var(--amber-dim);
}
.to-slot-name {
  display: inline-block;
  text-transform: uppercase;
}
.to-current {
  border-color: var(--amber);
  color: #000;
  background: var(--amber);
  box-shadow: 0 0 12px rgba(255, 140, 0, 0.55);
  transform: scale(1.05);
}
.to-current .to-portrait {
  border-color: #000;
  filter: sepia(0.2) saturate(1.4) brightness(1.05);
}
.to-enemy {
  border-color: var(--warn);
  color: var(--warn);
}
.to-enemy .to-portrait {
  border-color: var(--warn);
  filter: sepia(0.6) saturate(1.5) hue-rotate(-30deg) brightness(0.85);
}
.to-current.to-enemy {
  color: #000;
  background: var(--warn);
  border-color: var(--warn);
  box-shadow: 0 0 12px rgba(255, 30, 30, 0.6);
}
.to-current.to-enemy .to-portrait {
  border-color: #000;
  filter: sepia(0.5) saturate(1.5) hue-rotate(-30deg) brightness(1.1);
}
.to-sep {
  color: var(--amber-dim);
  font-size: 11px;
}
/* WHAT WAS — WERE telegraph. Hangs above the enemy slot where the
   corruption strike will land, so the player reads who needs to DEFEND
   on their next turn. Pulses red so it reads as a real warning even
   against the existing crit/impact noise of combat. */
.to-signature {
  position: relative;
  border-color: var(--warn);
  box-shadow: 0 0 8px rgba(255, 30, 30, 0.45);
}
.to-signature-flag {
  position: absolute;
  top: -16px;
  left: 0;
  right: 0;
  text-align: center;
  font-size: 9px;
  letter-spacing: 1.5px;
  color: var(--warn);
  text-shadow: 0 0 4px rgba(255, 30, 30, 0.7);
  animation: to-signature-pulse 1.4s ease-in-out infinite;
  white-space: nowrap;
}
@keyframes to-signature-pulse {
  0%, 100% { opacity: 0.55; }
  50%      { opacity: 1; }
}
/* SCAN reveal (SYSTEMS solo L4). Amber, no pulse — it's a calm
   informational tag, not a warning. */
.to-scan-flag {
  color: var(--amber);
  text-shadow: 0 0 4px rgba(255, 140, 0, 0.5);
  animation: none;
}
@keyframes to-slot-in {
  from { opacity: 0; transform: translateY(-3px) scale(0.95); }
  to   { opacity: 1; transform: translateY(0)     scale(1); }
}
.to-current { animation: to-current-in 260ms ease-out; }
@keyframes to-current-in {
  from { opacity: 0; transform: scale(0.85); box-shadow: 0 0 0 rgba(255, 140, 0, 0); }
  to   { opacity: 1; transform: scale(1.05); box-shadow: 0 0 12px rgba(255, 140, 0, 0.55); }
}

.contact-name {
  font-size: 12px;
  color: var(--amber);
  letter-spacing: 3px;
  margin-bottom: 5px;
  text-transform: uppercase;
}

.contact-hull-label {
  font-size: 10px;
  color: var(--amber-dim);
  letter-spacing: 1px;
  display: flex;
  justify-content: space-between;
  margin-bottom: 3px;
}

.contact-hull-bar {
  height: 4px;
  background: #0a0300;
  border: 1px solid #1a0800;
  overflow: hidden;
}

.contact-hull-fill {
  height: 100%;
  background: var(--amber-mid);
  transition: width 0.3s ease;
}

.contact-hull-fill.low {
  background: var(--warn);
  animation: blink 0.6s step-end infinite;
}

/* Contact status badges — one per special ability the enemy has.
   Sprint 19.4 — hard-capped to 3 per row via grid (down from 5 after
   the first cut still ran too wide). Late-strata bosses (ZERO POINT
   etc.) have enough badges that they now stack down the side of the
   panel instead of stretching it. */
.contact-status-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 4px;
  margin-top: 6px;
}
.contact-status-row:empty { display: none; }

.contact-status-badge {
  font-size: 9px;
  letter-spacing: 2px;
  padding: 2px 6px;
  border: 1px solid var(--amber-dim);
  color: var(--amber-mid);
  background: rgba(0, 0, 0, 0.35);
  text-transform: uppercase;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: color 0.15s, border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.contact-status-badge.pulse {
  color: var(--amber);
  border-color: var(--amber);
  background: rgba(80, 36, 0, 0.55);
  box-shadow: 0 0 8px rgba(255, 140, 0, 0.55);
  animation: badge-pulse 0.7s ease-out;
}
@keyframes badge-pulse {
  0%   { transform: scale(1.0); }
  20%  { transform: scale(1.15); }
  100% { transform: scale(1.0); }
}

/* ── PILOT DOSSIER (modal overlay) ─────────────
   Click a crew card to open. Click backdrop / [X] / ESC to close.
   Pinned to the viewport, sits above the cockpit so the game keeps
   running underneath. */
.dossier-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(0, 0, 0, 0.78);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  color: var(--amber-mid);
  letter-spacing: 1px;
  text-shadow: 0 0 3px rgba(255, 140, 0, 0.30);
}

.dossier-frame {
  width: min(520px, 92vw);
  max-height: 90vh;
  overflow-y: auto;
  background: var(--screen-bg);
  border: 1px solid var(--amber-mid);
  border-radius: 8px;
  box-shadow:
    0 0 0 1px #000,
    0 0 40px rgba(200, 100, 0, 0.35),
    inset 0 0 60px rgba(0, 0, 0, 0.7);
  cursor: default;
}

.dossier-header {
  background: var(--amber-deep);
  border-bottom: 1px solid var(--amber-dark);
  padding: 10px 14px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 13px;
  letter-spacing: 3px;
  color: var(--amber);
  text-transform: uppercase;
}

.dossier-close {
  background: transparent;
  border: 1px solid var(--amber-dark);
  color: var(--amber-mid);
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 1px;
  padding: 3px 8px;
  cursor: pointer;
  transition: all 0.1s;
}
.dossier-close:hover {
  border-color: var(--amber-mid);
  color: var(--amber);
  background: rgba(80, 30, 0, 0.4);
}

#dossier-body {
  padding: 18px;
  flex: 1;
  overflow-y: auto;
}
#dossier-body::-webkit-scrollbar { width: 4px; }
#dossier-body::-webkit-scrollbar-thumb { background: var(--amber-dark); }

/* Close button sits on the right edge of the screen-modal header. */
.screen-modal-header .dossier-close { float: right; margin-top: -2px; }

.dossier-portrait-wrap {
  display: flex;
  justify-content: center;
  margin-bottom: 14px;
}

.dossier-portrait {
  /* Sprint 19.7.2 — landscape container forces the cover crop to drop
     the bottom ~16% of the source, removing the baked-in caption. The
     role/class line in the dossier-identity block owns the labeling. */
  width: 220px;
  height: 195px;
  max-width: 100%;
  object-fit: cover;
  object-position: center top;
  border: 1px solid var(--amber-dim);
  background: #000;
}

.dossier-identity {
  text-align: center;
  margin-bottom: 18px;
}

.dossier-name {
  font-size: 22px;
  color: var(--amber);
  letter-spacing: 4px;
  margin-bottom: 4px;
}

.dossier-level {
  font-size: 13px;
  color: var(--amber-dim);
  letter-spacing: 2px;
  font-weight: normal;
  margin-left: 6px;
}

.dossier-role {
  font-size: 11px;
  color: var(--amber-dim);
  letter-spacing: 2px;
  text-transform: uppercase;
  margin-bottom: 4px;
}

.dossier-classification {
  font-size: 9px;
  color: var(--amber-dim);
  opacity: 0.7;
  letter-spacing: 2px;
  text-transform: uppercase;
  margin-bottom: 8px;
}

.dossier-row-status {
  display: flex;
  justify-content: center;
  gap: 18px;
  font-size: 10px;
  letter-spacing: 2px;
  text-transform: uppercase;
}
.dossier-row    { color: var(--amber-dim); }
.dossier-status { color: var(--amber); }
.dossier-status.warn { color: var(--warn); }

.dossier-stats {
  border-top: 1px solid var(--amber-dark);
  padding-top: 14px;
  margin-bottom: 16px;
}

.dossier-stat {
  margin-bottom: 12px;
}

.dossier-bar-label {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--amber-mid);
  margin-bottom: 4px;
  letter-spacing: 1px;
}

.dossier-stat-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 6px;
}
.dossier-stat-grid > div {
  display: flex;
  justify-content: space-between;
  padding: 6px 10px;
  border: 1px solid var(--amber-dark);
  background: rgba(0, 0, 0, 0.4);
  font-size: 11px;
  letter-spacing: 1px;
}
.dossier-stat-grid .k { color: var(--amber-dim); }
.dossier-stat-grid .v { color: var(--amber); }

.dossier-trait {
  border-top: 1px solid var(--amber-dark);
  padding-top: 14px;
  margin-bottom: 16px;
}

.dossier-trait-name {
  font-size: 14px;
  color: var(--amber);
  letter-spacing: 3px;
  font-weight: bold;
  margin-bottom: 4px;
}

.dossier-trait-desc {
  font-size: 11px;
  color: var(--amber-mid);
  letter-spacing: 1px;
  line-height: 1.5;
}

/* Accessory section — equip / unequip controls inside the dossier. */
.dossier-accessory {
  border-top: 1px solid var(--amber-dark);
  padding-top: 14px;
  margin-bottom: 16px;
}

.dossier-equipped {
  border: 1px solid var(--amber-mid);
  background: rgba(80, 30, 0, 0.20);
  padding: 8px 10px;
  margin-bottom: 8px;
}
.dossier-equipped-name {
  font-size: 13px;
  color: var(--amber);
  letter-spacing: 2px;
  font-weight: bold;
  margin-bottom: 3px;
}
.dossier-equipped-desc {
  font-size: 10px;
  color: var(--amber-mid);
  letter-spacing: 1px;
  margin-bottom: 8px;
  line-height: 1.4;
}
.dossier-equipped-empty {
  border: 1px dashed var(--amber-dark);
  background: rgba(0, 0, 0, 0.30);
  padding: 12px;
  text-align: center;
  font-size: 11px;
  color: var(--amber-dim);
  letter-spacing: 2px;
  margin-bottom: 8px;
}

.dossier-equip-options {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.dossier-equip-btn {
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid var(--amber-dark);
  color: var(--amber-mid);
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  font-size: 11px;
  letter-spacing: 1px;
  padding: 7px 9px;
  text-align: left;
  cursor: pointer;
  transition: all 0.1s;
}
.dossier-equip-btn:hover {
  border-color: var(--amber-mid);
  color: var(--amber);
  background: rgba(80, 30, 0, 0.4);
}

.dossier-equip-count {
  color: var(--amber-dim);
  margin-left: 6px;
  font-size: 10px;
}

.dossier-equip-empty {
  font-size: 11px;
  color: var(--amber-dim);
  font-style: italic;
  padding: 6px 0;
  letter-spacing: 1px;
}

.dossier-abilities {
  border-top: 1px solid var(--amber-dark);
  padding-top: 14px;
}

/* Accessory shop section in the floor-complete overlay. */
.fc-accessory-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.fc-btn-sub {
  font-size: 10px;
  color: var(--amber-dim);
  letter-spacing: 1px;
  margin-top: 4px;
  text-transform: none;
  line-height: 1.4;
}

/* Cost-deficit badge on an accessory button the player can't yet
   afford. Sits inline next to the cost so the gap is immediately
   visible without clicking. Red so it doesn't read as "available." */
.fc-btn-need {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 6px;
  font-size: 9px;
  letter-spacing: 1.5px;
  color: #cc5500;
  border: 1px solid #cc5500;
  background: rgba(204, 85, 0, 0.08);
}

.fc-shop-note {
  font-size: 10px;
  color: var(--amber-dim);
  text-align: center;
  margin-top: 8px;
  letter-spacing: 1px;
  font-style: italic;
}

/* ── SCREEN MODAL ───────────────────────────────
   In-monitor overlay used by the advancement (level-up) screen.
   Pinned to fill the center monitor's canvas-wrap rather than the
   whole viewport — keeps the cockpit context visible behind/around it. */
.screen-modal {
  position: absolute;
  inset: 0;
  z-index: 30;
  background: rgba(3, 1, 6, 0.96);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  color: var(--amber-mid);
  text-shadow: 0 0 3px rgba(255, 140, 0, 0.30);
  letter-spacing: 1px;
}

.screen-modal-header {
  background: var(--amber-deep);
  border-bottom: 1px solid var(--amber-dim);
  padding: 8px 12px;
  font-size: 12px;
  letter-spacing: 3px;
  color: var(--amber);
  text-transform: uppercase;
  flex-shrink: 0;
}

/* ── COMMS MODAL ───────────────────────────────────────────────────────
   MGS-style transmission overlay. Speaker's portrait + name on the
   left, dialogue text on the right. Click anywhere or ENTER advances
   to the next line; closes when the line list is exhausted. */
#comms-modal-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 24px 32px;
  gap: 14px;
}
.comms-row {
  flex: 1;
  display: grid;
  grid-template-columns: 130px 1fr;
  gap: 24px;
  align-items: center;
}
.comms-portrait-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.comms-portrait {
  width: 110px;
  height: 97px;                  /* Sprint 19.7.2 — landscape crops the caption strip */
  object-fit: cover;
  object-position: center top;
  border: 1px solid var(--amber);
  background: #000;
  filter: sepia(0.35) saturate(1.3) brightness(0.95) contrast(1.05);
  box-shadow: 0 0 14px rgba(255, 140, 0, 0.35);
  image-rendering: pixelated;
}
.comms-portrait-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 36px;
  color: var(--amber-dim);
}
.comms-speaker-name {
  font-size: 14px;
  letter-spacing: 4px;
  color: var(--amber);
  text-transform: uppercase;
  text-shadow: 0 0 6px rgba(255, 140, 0, 0.55);
}
.comms-speaker-role {
  font-size: 9px;
  letter-spacing: 2px;
  color: var(--amber-dim);
  text-transform: uppercase;
}
.comms-text-wrap {
  border-left: 1px dashed var(--amber-dim);
  padding-left: 22px;
  align-self: stretch;
  display: flex;
  align-items: center;
}
.comms-text {
  font-size: 15px;
  line-height: 1.6;
  color: var(--amber);
  letter-spacing: 1.5px;
  animation: comms-text-in 320ms ease-out;
  min-height: 1.6em;                  /* hold layout while typewriter fills */
  white-space: pre-wrap;
}

/* Narration entries — Vex's internal monologue. No portrait, centered
   prose block, dimmer than dialogue so the distinction reads at a
   glance. The "▸ INTERNAL" marker mirrors the comms header treatment. */
.comms-narration {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 0 32px;
  gap: 14px;
}
.comms-narration-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.comms-narration-portrait {
  width: 90px;
  height: 80px;                  /* Sprint 19.7.2 — landscape crops the caption strip */
  object-fit: cover;
  object-position: center top;
  border: 1px solid var(--amber-mid);
  background: #000;
  filter: sepia(0.4) saturate(1.1) brightness(0.78) contrast(1.05) blur(0.4px);
  box-shadow: 0 0 10px rgba(255, 140, 0, 0.22);
  image-rendering: pixelated;
  opacity: 0.85;
}
.comms-narration-marker {
  font-size: 10px;
  letter-spacing: 4px;
  color: var(--amber-dim);
  text-transform: uppercase;
}
.comms-text-narration {
  font-style: italic;
  font-size: 14px;
  color: var(--amber-mid);
  text-align: center;
  max-width: 620px;
  line-height: 1.7;
  text-shadow: 0 0 4px rgba(255, 140, 0, 0.20);
}
.comms-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-top: 1px dashed var(--amber-dim);
  padding-top: 10px;
  font-size: 10px;
  letter-spacing: 3px;
  color: var(--amber-dim);
  text-transform: uppercase;
}
.comms-progress { color: var(--amber-mid); }
.comms-hint     { color: var(--amber); }
.comms-auto-indicator {
  color: var(--amber-dim);
  transition: color 0.2s, text-shadow 0.2s;
}
.comms-auto-indicator.on {
  color: var(--amber);
  text-shadow: 0 0 6px rgba(255, 140, 0, 0.55);
}
@keyframes comms-text-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── DOCUMENT READER ──────────────────────────────────────────────────
   Fragment recovered on a story floor. Title + body block + close hint.
   Reads as an authored note pinned to the terminal — limited width
   keeps the prose centered. */
#document-reader-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 28px 48px;
  gap: 18px;
  overflow-y: auto;
}
.dr-title {
  font-size: 16px;
  letter-spacing: 4px;
  color: var(--amber);
  text-transform: uppercase;
  text-shadow: 0 0 8px rgba(255, 140, 0, 0.5);
  border-bottom: 1px solid var(--amber-dim);
  padding-bottom: 10px;
}
.dr-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 640px;
  align-self: center;
}
.dr-line {
  font-size: 13px;
  line-height: 1.6;
  color: var(--amber-mid);
  letter-spacing: 1.5px;
}
.dr-footer {
  border-top: 1px dashed var(--amber-dim);
  padding-top: 10px;
  text-align: right;
  font-size: 10px;
  letter-spacing: 3px;
  color: var(--amber);
  text-transform: uppercase;
}

/* ── ADVANCEMENT (level-up modal) ───────────────── */
#level-up-body {
  padding: 0;
  flex: 1;
  overflow-y: auto;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  color: var(--amber-mid);
  text-shadow: 0 0 3px rgba(255, 140, 0, 0.30);
  letter-spacing: 1px;
}
#level-up-body::-webkit-scrollbar { width: 4px; }
#level-up-body::-webkit-scrollbar-thumb { background: var(--amber-dark); }

.lu-section {
  padding: 14px 18px;
  border-bottom: 1px solid var(--amber-dark);
}

.lu-header {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-bottom: 10px;
}

.lu-portrait {
  width: 52px;
  height: 46px;                  /* Sprint 19.7.2 — landscape crops the caption strip */
  object-fit: cover;
  object-position: center top;
  border: 1px solid var(--amber-dim);
  background: #000;
}

.lu-id .lu-name {
  font-size: 16px;
  color: var(--amber);
  letter-spacing: 3px;
}

.lu-id .lu-tier {
  font-size: 12px;
  color: var(--amber-dim);
  letter-spacing: 2px;
  margin-top: 3px;
}

.lu-stats {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  letter-spacing: 1px;
}

.lu-stats th {
  text-align: left;
  padding: 6px 8px;
  border-bottom: 1px solid var(--amber-dark);
  color: var(--amber-dim);
  font-weight: normal;
  font-size: 10px;
  letter-spacing: 2px;
}

.lu-stats td {
  padding: 7px 8px;
  border-bottom: 1px solid rgba(45, 21, 0, 0.5);
}

.lu-stat-label    { color: var(--amber-mid); }
.lu-stat-from     { color: var(--amber-dim); }
.lu-stat-to       { color: var(--amber-mid); }
.lu-stat-delta    { color: var(--amber); font-weight: bold; }

.lu-stats tr.changed .lu-stat-to    { color: var(--amber); }
.lu-stats tr.changed .lu-stat-label { color: var(--amber); }

.lu-unlock {
  margin-top: 14px;
  padding: 9px 12px;
  border: 1px solid var(--amber-mid);
  background: rgba(80, 30, 0, 0.20);
  color: var(--amber);
  font-size: 12px;
  letter-spacing: 2px;
}

.lu-actions {
  padding: 12px 18px 16px;
}

/* ── COMBAT SCREEN EFFECTS ───────────────────────
   hit-flash: brief amber bleach on the center monitor when a pilot's
   strike connects. Pops over the 3D canvas without obscuring it. */
#screen-center.hit-flash::before,
#screen-center.hit-flash-crit::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 22;
  animation: hit-flash 0.14s ease-out forwards;
}
#screen-center.hit-flash::before {
  background: radial-gradient(ellipse at center,
    rgba(255, 200, 80, 0.55) 0%,
    rgba(255, 140, 0, 0.25) 40%,
    transparent 80%);
}
/* Crit variant — brighter, longer, fills more of the screen. */
#screen-center.hit-flash-crit::before {
  background: radial-gradient(ellipse at center,
    rgba(255, 240, 180, 0.90) 0%,
    rgba(255, 180, 60, 0.55) 30%,
    rgba(255, 140, 0, 0.20) 70%,
    transparent 100%);
  animation: hit-flash 0.24s ease-out forwards;
}
@keyframes hit-flash {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* Pilot-offline flash — full white wash when a pilot's HP hits 0. The
   crit/hit flashes are amber-radial (warm, contained); this is white
   and full-bleed so the moment lands as cold sensor whiteout. */
#screen-center.pilot-offline-flash::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 24;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.95) 0%,
    rgba(220, 230, 255, 0.85) 50%,
    rgba(255, 255, 255, 0.95) 100%
  );
  animation: pilot-offline-flash 0.32s ease-out forwards;
}
@keyframes pilot-offline-flash {
  0%   { opacity: 1; }
  40%  { opacity: 0.65; }
  100% { opacity: 0; }
}

/* IMPACT readout — blinks over the viewport on an incoming hit. The
   variant- classes restyle text + color for normal vs critical events. */
#impact-readout {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 26;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  font-weight: bold;
  letter-spacing: 12px;
  pointer-events: none;
  animation: impact-blink 0.22s step-end infinite;
  white-space: nowrap;
  text-align: center;
}

/* Normal IMPACT — red, mid size. */
#impact-readout.variant-impact {
  font-size: 56px;
  color: var(--warn);
  text-shadow:
    0 0 8px rgba(255, 0, 0, 0.85),
    0 0 18px rgba(220, 0, 0, 0.55);
}

/* CRITICAL IMPACT — red, brighter, wider letter spacing. Sized to fit
   the center monitor without clipping the sides. */
#impact-readout.variant-critical-impact {
  font-size: 54px;
  color: var(--warn);
  letter-spacing: 10px;
  text-shadow:
    0 0 12px rgba(255, 30, 30, 1.0),
    0 0 28px rgba(255, 0, 0, 0.75),
    0 0 50px rgba(220, 0, 0, 0.45);
  animation: impact-blink 0.16s step-end infinite;
}

/* CRITICAL STRIKE — amber (we hit them), slightly smaller than the
   enemy crit so the two read as distinct events. Sized to fit the
   center monitor without clipping the sides. */
#impact-readout.variant-critical-strike {
  font-size: 48px;
  color: var(--amber);
  letter-spacing: 10px;
  text-shadow:
    0 0 10px rgba(255, 200, 80, 1.0),
    0 0 24px rgba(255, 140, 0, 0.70),
    0 0 44px rgba(220, 100, 0, 0.40);
  animation: impact-blink 0.18s step-end infinite;
}

/* SLASH effect — pops over the enemy when a player strike lands. The
   character is a diagonal box-drawing glyph drawn huge over the
   crosshair, then scaled / faded out in ~200ms. */
#slash-effect {
  position: absolute;
  top: 42%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 25;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  font-size: 220px;
  line-height: 1;
  color: var(--amber);
  text-shadow:
    0 0 10px rgba(255, 200, 80, 0.95),
    0 0 22px rgba(255, 140, 0, 0.65),
    0 0 38px rgba(255, 100, 0, 0.40);
  pointer-events: none;
}
#slash-effect.fire {
  animation: slash-flash 0.22s ease-out forwards;
}
#slash-effect.fire-crit {
  font-size: 320px;
  color: #fff3c8;
  text-shadow:
    0 0 14px rgba(255, 240, 180, 1.0),
    0 0 30px rgba(255, 180, 60, 0.85),
    0 0 60px rgba(255, 100, 0, 0.55);
  animation: slash-flash 0.34s ease-out forwards;
}
@keyframes slash-flash {
  0%   { opacity: 0;   transform: translate(-50%, -50%) rotate(-25deg) scale(0.6); }
  25%  { opacity: 1;   transform: translate(-50%, -50%) rotate(0deg)   scale(1.05); }
  100% { opacity: 0;   transform: translate(-50%, -50%) rotate(10deg)  scale(1.30); }
}

/* OBTAINED — fires when a cache pops something into the inventory.
   Amber, mid-size, slow blink — a positive readout, not a danger one. */
#impact-readout.variant-obtained {
  font-size: 44px;
  color: var(--amber);
  letter-spacing: 10px;
  text-shadow:
    0 0 10px rgba(255, 200, 80, 1.0),
    0 0 22px rgba(255, 140, 0, 0.65),
    0 0 38px rgba(220, 100, 0, 0.35);
  animation: impact-blink 0.30s step-end infinite;
}

/* NEED ACCESS CODE — fires when the player steps onto the locked exit.
   Steady red, slower blink than impacts so it reads as a sign rather
   than a hit. */
#impact-readout.variant-access-denied {
  font-size: 50px;
  color: var(--warn);
  letter-spacing: 8px;
  text-shadow:
    0 0 8px rgba(255, 0, 0, 0.85),
    0 0 20px rgba(220, 0, 0, 0.50);
  animation: impact-blink 0.45s step-end infinite;
}

@keyframes impact-blink {
  0%, 50%   { opacity: 1; }
  51%, 100% { opacity: 0.20; }
}

/* Cockpit shake — whole-frame jitter when the mech takes a hit.
   Applied to #cockpit so every monitor + the hull image rattle together. */
#cockpit.shake {
  animation: cockpit-shake 0.34s ease-in-out;
}
/* Bigger shake variant for critical impacts — larger offsets, longer. */
#cockpit.shake-big {
  animation: cockpit-shake-big 0.52s ease-in-out;
}
@keyframes cockpit-shake {
  0%   { transform: translate(0, 0); }
  10%  { transform: translate(-4px, 2px); }
  20%  { transform: translate(4px, -3px); }
  30%  { transform: translate(-3px, 3px); }
  40%  { transform: translate(3px, -1px); }
  50%  { transform: translate(-4px, 0); }
  60%  { transform: translate(3px, 2px); }
  70%  { transform: translate(-2px, -2px); }
  80%  { transform: translate(2px, 3px); }
  90%  { transform: translate(-2px, -1px); }
  100% { transform: translate(0, 0); }
}
@keyframes cockpit-shake-big {
  0%   { transform: translate(0, 0); }
  7%   { transform: translate(-8px, 4px); }
  14%  { transform: translate(7px, -6px); }
  21%  { transform: translate(-6px, 5px); }
  28%  { transform: translate(7px, -3px); }
  35%  { transform: translate(-8px, 2px); }
  42%  { transform: translate(6px, 4px); }
  50%  { transform: translate(-5px, -5px); }
  58%  { transform: translate(5px, 5px); }
  66%  { transform: translate(-4px, -3px); }
  74%  { transform: translate(4px, 3px); }
  82%  { transform: translate(-2px, -2px); }
  90%  { transform: translate(2px, 1px); }
  100% { transform: translate(0, 0); }
}

.dossier-section-header {
  font-size: 11px;
  color: var(--amber);
  letter-spacing: 3px;
  margin-bottom: 8px;
}

.dossier-ability {
  font-size: 12px;
  color: var(--amber-mid);
  letter-spacing: 1px;
  padding: 5px 10px;
  margin-bottom: 4px;
  border: 1px solid var(--amber-dark);
  background: rgba(0, 0, 0, 0.4);
}

.dossier-ability.locked {
  opacity: 0.4;
  color: var(--amber-dim);
}

/* Sprint 19.4 — full ABILITIES tab in the pilot dossier. Every class
   ability sorted by unlock level with cost / CTB cost / target /
   description. Locked rows dim. */
.dossier-ability-fixed {
  display: flex;
  gap: 18px;
  font-size: 10px;
  color: var(--amber-dim);
  letter-spacing: 2px;
  margin-bottom: 10px;
  padding-bottom: 6px;
  border-bottom: 1px dashed var(--amber-dark);
}
.dossier-ability-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.dossier-ability-row {
  border: 1px solid var(--amber-dark);
  background: rgba(0, 0, 0, 0.45);
  padding: 8px 10px;
  font-size: 10px;
  letter-spacing: 1px;
  color: var(--amber-mid);
}
.dossier-ability-row.locked {
  opacity: 0.40;
  border-style: dashed;
}
.dossier-ability-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}
.dossier-ability-name {
  flex: 1;
  font-size: 12px;
  letter-spacing: 2px;
  color: var(--amber);
}
.dossier-ability-row.locked .dossier-ability-name {
  color: var(--amber-dim);
}
.dossier-ability-kind {
  font-size: 9px;
  letter-spacing: 2px;
  color: var(--amber-dim);
}
.dossier-ability-level {
  font-size: 9px;
  letter-spacing: 2px;
  color: var(--amber-dim);
}
.dossier-ability-meta {
  display: flex;
  gap: 14px;
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--amber-dim);
  margin-bottom: 4px;
}
.dossier-ability-desc {
  font-size: 10px;
  letter-spacing: 1px;
  color: var(--amber-mid);
  line-height: 1.5;
}
.dossier-ability-empty {
  font-size: 10px;
  color: var(--amber-dim);
  letter-spacing: 2px;
  font-style: italic;
}

/* Pilot cards in the left panel get a click-cursor since they open the dossier. */
.pilot { cursor: pointer; }
.pilot:hover { border-color: var(--amber-mid); }

/* ── FLOOR-COMPLETE OVERLAY ─────────────────────
   Reuses .dossier-overlay + .dossier-frame styles for layout — only the
   inner body needs its own structure. */
#floor-complete-body {
  padding: 22px 24px 18px;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  color: var(--amber-mid);
  text-shadow: 0 0 3px rgba(255, 140, 0, 0.30);
  letter-spacing: 1px;
}

.fc-floor-name {
  font-size: 22px;
  color: var(--amber);
  letter-spacing: 5px;
  text-align: center;
  margin-bottom: 4px;
  text-transform: uppercase;
}
.fc-status-line {
  font-size: 11px;
  color: var(--amber-dim);
  letter-spacing: 3px;
  text-align: center;
  margin-bottom: 22px;
}

.fc-section {
  border-top: 1px solid var(--amber-dark);
  padding-top: 14px;
  margin-bottom: 16px;
}
.fc-section:last-child { margin-bottom: 0; }

.fc-section-header {
  font-size: 11px;
  color: var(--amber);
  letter-spacing: 3px;
  margin-bottom: 10px;
}

.fc-stores-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 12px;
}
.fc-stores-row > div {
  flex: 1;
  padding: 8px 12px;
  border: 1px solid var(--amber-dark);
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  justify-content: space-between;
  letter-spacing: 1px;
}
.fc-k { color: var(--amber-dim); }
.fc-v { color: var(--amber); }

.fc-btn {
  display: block;
  width: 100%;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid var(--amber-dark);
  color: var(--amber-mid);
  font-family: inherit;
  font-size: 12px;
  letter-spacing: 2px;
  padding: 11px;
  cursor: pointer;
  text-transform: uppercase;
  transition: all 0.1s;
}
.fc-btn:hover:not(:disabled) {
  border-color: var(--amber-mid);
  color: var(--amber);
  background: rgba(80, 30, 0, 0.4);
  box-shadow: 0 0 8px rgba(200, 80, 0, 0.2);
}
.fc-btn:active:not(:disabled) {
  background: rgba(120, 50, 0, 0.5);
  transform: scale(0.99);
}
.fc-btn.dim, .fc-btn:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

/* Pick-one rule visual states for the floor-complete accessory shop. */
.fc-btn.fc-acquired {
  opacity: 1;
  border-color: var(--amber);
  background: rgba(80, 36, 0, 0.45);
  color: var(--amber);
  box-shadow: inset 0 0 10px rgba(255, 140, 0, 0.30);
  cursor: default;
}
.fc-btn.fc-acquired:hover {
  background: rgba(80, 36, 0, 0.45);
  border-color: var(--amber);
  box-shadow: inset 0 0 10px rgba(255, 140, 0, 0.30);
}
.fc-btn.fc-locked {
  cursor: not-allowed;
}

.fc-section-sub {
  float: right;
  font-size: 10px;
  letter-spacing: 3px;
  color: var(--amber-mid);
  background: rgba(80, 36, 0, 0.4);
  border: 1px solid var(--amber-dim);
  padding: 2px 8px;
}

.fc-btn.fc-primary {
  border-color: var(--amber);
  color: var(--amber);
  font-size: 14px;
  letter-spacing: 4px;
  padding: 13px;
}

.fc-descend-note {
  font-size: 10px;
  color: var(--amber-dim);
  text-align: center;
  margin-top: 8px;
  letter-spacing: 2px;
}

/* ── DEV CONSOLE ───────────────────────────────
   Toggle with ` (backtick). Pinned to bottom of viewport. */
#dev-console {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 200;
  background: rgba(0, 0, 0, 0.94);
  border-top: 1px solid var(--amber-mid);
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  color: var(--amber-mid);
  padding: 10px 16px 12px;
  box-shadow: 0 -8px 24px rgba(255, 140, 0, 0.10);
}

#dev-console-output {
  font-size: 12px;
  line-height: 1.55;
  max-height: 220px;
  overflow-y: auto;
  margin-bottom: 8px;
  letter-spacing: 0.5px;
}
#dev-console-output::-webkit-scrollbar { width: 4px; }
#dev-console-output::-webkit-scrollbar-thumb { background: var(--amber-dark); }

.dev-line {
  color: var(--amber-mid);
  padding: 1px 0;
}

.dev-prompt {
  display: flex;
  align-items: center;
  gap: 10px;
  border-top: 1px solid var(--amber-dark);
  padding-top: 8px;
}

.dev-arrow {
  color: var(--amber);
  font-size: 14px;
  font-weight: bold;
}

#dev-console-input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: var(--amber);
  font-family: inherit;
  font-size: 13px;
  letter-spacing: 1px;
  padding: 4px 0;
}

#dev-console-input::placeholder {
  color: var(--amber-dim);
}

/* ── FLOATING DAMAGE NUMBERS ──────────────────────────────────────────
   Pops over the center monitor when an enemy takes a hit, or inside
   the pilot card when a pilot takes one. Floats up + fades. */
.damage-number {
  position: absolute;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  font-weight: bold;
  pointer-events: none;
  z-index: 27;
  white-space: nowrap;
  letter-spacing: 2px;
}

/* Enemy variants — pop near the center of the viewport. */
.damage-number.on-enemy {
  font-size: 30px;
  color: var(--amber);
  text-shadow: 0 0 8px rgba(255, 140, 0, 0.75), 0 0 16px rgba(255, 140, 0, 0.45);
  animation: damage-float-up 1.3s ease-out forwards;
  transform: translate(-50%, -50%);
}
.damage-number.on-enemy.crit {
  font-size: 48px;
  color: #fff3c8;
  text-shadow:
    0 0 10px rgba(255, 220, 120, 1.0),
    0 0 22px rgba(255, 160, 40, 0.85),
    0 0 40px rgba(255, 100, 0, 0.55);
  animation: damage-float-up-crit 1.5s ease-out forwards;
}
/* Pilot variants — anchored to a corner of the pilot card. */
.damage-number.on-pilot {
  top: 6px;
  right: 8px;
  font-size: 22px;
  color: var(--warn);
  text-shadow: 0 0 6px rgba(255, 0, 0, 0.8);
  animation: pilot-damage-float 1.3s ease-out forwards;
}
.damage-number.on-pilot.crit {
  font-size: 30px;
  color: #ff7755;
  text-shadow:
    0 0 8px rgba(255, 40, 40, 1.0),
    0 0 18px rgba(220, 0, 0, 0.75);
  animation: pilot-damage-float-crit 1.5s ease-out forwards;
}
.damage-number.on-pilot.heal {
  color: #ffd070;
  text-shadow: 0 0 8px rgba(255, 200, 80, 0.85);
}

@keyframes damage-float-up {
  0%   { opacity: 0;   transform: translate(-50%, -30%); }
  12%  { opacity: 1;   transform: translate(-50%, -55%); }
  100% { opacity: 0;   transform: translate(-50%, -120%); }
}
@keyframes damage-float-up-crit {
  0%   { opacity: 0;   transform: translate(-50%, -30%) scale(0.7); }
  10%  { opacity: 1;   transform: translate(-50%, -55%) scale(1.15); }
  20%  { transform: translate(-50%, -58%) scale(1.0); }
  100% { opacity: 0;   transform: translate(-50%, -130%) scale(1.0); }
}
@keyframes pilot-damage-float {
  0%   { opacity: 0;   transform: translate(0, 4px); }
  15%  { opacity: 1;   transform: translate(0, -2px); }
  100% { opacity: 0;   transform: translate(0, -30px); }
}
@keyframes pilot-damage-float-crit {
  0%   { opacity: 0;   transform: translate(0, 4px) scale(0.7); }
  10%  { opacity: 1;   transform: translate(0, -2px) scale(1.2); }
  22%  { transform: translate(0, -4px) scale(1.0); }
  100% { opacity: 0;   transform: translate(0, -34px) scale(1.0); }
}

/* ── MINIMAP ──────────────────────────────────────────────────────────
   Classic DC corner-map. Pinned bottom-left of the target monitor's
   viewport — the top is reserved for the CTB turn-order strip during
   combat, and the contact HUD lives top-right out of combat. */
#minimap {
  position: absolute;
  bottom: 12px;
  left: 12px;
  z-index: 18;
  background: rgba(3, 1, 6, 0.88);
  border: 1px solid var(--amber-dim);
  padding: 4px 4px 2px 4px;
  pointer-events: none;
  box-shadow: 0 0 8px rgba(255, 140, 0, 0.18) inset;
}
#minimap-canvas {
  display: block;
  width: 156px;
  height: 156px;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}
.minimap-label {
  font-size: 9px;
  color: var(--amber-dim);
  letter-spacing: 2px;
  text-align: center;
  padding: 1px 0 3px 0;
  text-transform: uppercase;
}

/* ── SCRAP TRACKER ─────────────────────────────────────────────────────
   Mirror of the minimap on the right side. Always visible. Sits at the
   bottom-right alongside the minimap to keep the top free for the
   contact HUD (out of combat) and the turn-order strip (in combat). */
#scrap-tracker {
  position: absolute;
  bottom: 12px;
  right: 12px;
  z-index: 18;
  background: rgba(3, 1, 6, 0.88);
  border: 1px solid var(--amber-dim);
  padding: 6px 14px 8px;
  min-width: 140px;
  pointer-events: none;
  box-shadow: 0 0 8px rgba(255, 140, 0, 0.18) inset;
  text-align: center;
}
.scrap-tracker-label {
  font-size: 9px;
  color: var(--amber-dim);
  letter-spacing: 3px;
  text-transform: uppercase;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--amber-dim);
  margin-bottom: 4px;
}
.scrap-tracker-value {
  font-size: 22px;
  color: var(--amber);
  letter-spacing: 4px;
  font-weight: bold;
  text-shadow: 0 0 6px rgba(255, 140, 0, 0.5);
  transition: color 0.2s, text-shadow 0.2s;
}
.scrap-tracker-value.bump {
  color: #ffd070;
  text-shadow: 0 0 10px rgba(255, 220, 120, 0.9);
}

/* ── REWARD MODAL ─────────────────────────────────────────────────────
   Fires after every combat win. Sits below the access-code modal in
   the chain so the boss-specific reward beat lands last. */
#reward-modal-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 24px 28px 20px 28px;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  letter-spacing: 2px;
  font-size: 14px;
}
.reward-header {
  padding: 12px 14px;
  border-left: 2px solid var(--amber-dim);
  background: rgba(0, 0, 0, 0.4);
}
.reward-header-line {
  color: var(--amber);
  font-size: 15px;
  letter-spacing: 3px;
}
.reward-header-sub {
  color: var(--amber-dim);
  font-size: 10px;
  letter-spacing: 3px;
  margin-top: 4px;
}
.reward-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.reward-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 12px;
  align-items: baseline;
  padding: 10px 16px;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid var(--amber-dim);
}
.reward-row-label {
  color: var(--amber-mid);
  font-size: 12px;
  letter-spacing: 3px;
}
.reward-row-value {
  color: var(--amber);
  font-size: 16px;
  letter-spacing: 3px;
  font-weight: bold;
}
.reward-row-suffix {
  color: var(--amber-dim);
  font-size: 10px;
  letter-spacing: 2px;
}
.reward-row.empty {
  display: block;
  text-align: center;
  color: var(--amber-dim);
  font-size: 12px;
  letter-spacing: 3px;
  padding: 14px 18px;
}
.reward-ack-btn {
  margin-top: auto;
  background: transparent;
  border: 1px solid var(--amber-mid);
  color: var(--amber);
  font-family: inherit;
  font-size: 14px;
  letter-spacing: 3px;
  padding: 14px 8px;
  cursor: pointer;
  text-transform: uppercase;
  transition: all 0.1s;
}
.reward-ack-btn:hover {
  background: rgba(80, 30, 0, 0.5);
  border-color: var(--amber);
  box-shadow: 0 0 12px rgba(255, 140, 0, 0.35);
}

/* ── ACCESS CODE MODAL ────────────────────────────────────────────────
   Fires after a successful boss kill. Same screen-modal pattern as the
   level-up screen; sits above level-up in DOM order so when both fire
   simultaneously the access code is acknowledged first. */
#access-code-modal {
  z-index: 31;   /* above level-up so it shows first when both fire */
}
#access-code-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 26px 32px 22px 32px;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  color: var(--amber-mid);
  letter-spacing: 2px;
  font-size: 14px;
}
.ac-block {
  padding: 14px 18px;
  border-left: 2px solid var(--amber-dim);
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ac-block.ac-code {
  border-left-color: var(--amber);
  background: rgba(80, 36, 0, 0.35);
  align-items: center;
}
.ac-line {
  color: var(--amber-mid);
}
.ac-line.bright {
  color: var(--amber);
  font-size: 16px;
  letter-spacing: 3px;
}
.ac-label {
  font-size: 11px;
  letter-spacing: 3px;
  color: var(--amber-dim);
}
.ac-code-string {
  font-size: 20px;
  letter-spacing: 5px;
  color: var(--amber);
  text-shadow: 0 0 8px rgba(255, 140, 0, 0.6);
  font-weight: bold;
}
.ac-ack-btn {
  margin-top: auto;
  background: transparent;
  border: 1px solid var(--amber-mid);
  color: var(--amber);
  font-family: inherit;
  font-size: 14px;
  letter-spacing: 3px;
  padding: 14px 8px;
  cursor: pointer;
  text-transform: uppercase;
  transition: all 0.1s;
}
.ac-ack-btn:hover {
  background: rgba(80, 30, 0, 0.5);
  border-color: var(--amber);
  box-shadow: 0 0 12px rgba(255, 140, 0, 0.35);
}

/* ── ITEMS MODAL ──────────────────────────────────────────────────────
   [I] entry point. Two-step UI: item list → target picker. Same screen
   modal pattern as map / log / dossier. */
#items-modal-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 20px 24px 18px 24px;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  letter-spacing: 1px;
}
.items-modal-sub {
  font-size: 11px;
  letter-spacing: 4px;
  color: var(--amber-dim);
  text-transform: uppercase;
}
.items-modal-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.items-modal-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid var(--amber-dim);
  color: var(--amber);
  font-family: inherit;
  font-size: 14px;
  letter-spacing: 2px;
  padding: 12px 18px;
  cursor: pointer;
  text-align: left;
  transition: all 0.1s;
}
.items-modal-row:hover:not(:disabled) {
  background: rgba(80, 30, 0, 0.5);
  border-color: var(--amber);
  box-shadow: 0 0 10px rgba(255, 140, 0, 0.3);
}
.items-modal-row.dim, .items-modal-row:disabled {
  opacity: 0.30;
  cursor: not-allowed;
  color: var(--amber-dim);
}
.items-modal-row-name {
  letter-spacing: 3px;
}
.items-modal-row-count {
  font-size: 12px;
  color: var(--amber-mid);
  letter-spacing: 2px;
  justify-self: end;
}
.items-modal-row-count.low {
  color: var(--warn);
}
.items-modal-row-desc {
  grid-column: 1 / -1;
  font-size: 10px;
  color: var(--amber-dim);
  letter-spacing: 1px;
  margin-top: 4px;
  text-transform: none;
}
.items-modal-footer {
  margin-top: auto;
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding-top: 4px;
}
.items-modal-btn {
  background: transparent;
  border: 1px solid var(--amber-dark);
  color: var(--amber-dim);
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 3px;
  padding: 8px 14px;
  cursor: pointer;
  text-transform: uppercase;
  transition: all 0.1s;
}
.items-modal-btn:hover {
  border-color: var(--amber-mid);
  color: var(--amber-mid);
  background: rgba(40, 18, 0, 0.4);
}
.items-modal-empty {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 20px 18px;
  border-left: 2px solid var(--amber-dim);
  background: rgba(0, 0, 0, 0.4);
}
.items-modal-empty-line {
  color: var(--amber-mid);
  font-size: 13px;
  letter-spacing: 2px;
}
.items-modal-empty-sub {
  color: var(--amber-dim);
  font-size: 11px;
  letter-spacing: 1px;
}

/* ── SPECIALS MODAL ──────────────────────────────────────────────────
   Same screen-modal pattern as items-modal; opens on [2] in combat,
   lists every ability available to the active pilot. Rows show name /
   kind (BASE / SOLO / BREAKDOWN) / cost / condition in a four-column
   grid. Disabled rows (no scrap, not CRITICAL for breakdowns) dim. */
/* Body padding mirrors items-modal-body so the pilot label has
   breathing room from the modal header (was cut off pre-playtest). */
#specials-modal-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 20px 24px 18px 24px;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  letter-spacing: 1px;
  min-height: 0;
}
.specials-modal-pilot {
  font-size: 12px;
  letter-spacing: 3px;
  color: var(--amber-mid);
  margin-bottom: 12px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--amber-dark);
  flex-shrink: 0;
}
.specials-modal-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1 1 auto;
  overflow-y: auto;
  min-height: 0;
  padding-right: 4px;
}
.specials-modal-list::-webkit-scrollbar {
  width: 6px;
}
.specials-modal-list::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, 0.4);
}
.specials-modal-list::-webkit-scrollbar-thumb {
  background: var(--amber-dim);
}
.specials-modal-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid var(--amber-dim);
  color: var(--amber);
  font-family: inherit;
  font-size: 14px;
  letter-spacing: 2px;
  padding: 12px 18px;
  cursor: pointer;
  text-align: left;
  transition: all 0.1s;
}
.specials-modal-row:hover:not(:disabled) {
  background: rgba(80, 30, 0, 0.5);
  border-color: var(--amber);
  box-shadow: 0 0 10px rgba(255, 140, 0, 0.3);
}
.specials-modal-row.dim, .specials-modal-row:disabled {
  opacity: 0.30;
  cursor: not-allowed;
  color: var(--amber-dim);
}
.specials-modal-row-head {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  gap: 16px;
  align-items: center;
}
.specials-modal-row-name {
  letter-spacing: 3px;
}
.specials-modal-row-kind {
  font-size: 10px;
  color: var(--amber-mid);
  letter-spacing: 2px;
}
.specials-modal-row-cost {
  font-size: 12px;
  color: var(--amber-mid);
  letter-spacing: 2px;
  justify-self: end;
}
.specials-modal-row-cond {
  font-size: 10px;
  color: var(--amber-dim);
  letter-spacing: 1.5px;
  justify-self: end;
}
.specials-modal-row-desc {
  font-size: 10px;
  color: var(--amber-mid);
  letter-spacing: 1px;
  line-height: 1.5;
  text-transform: none;
}
.specials-modal-footer {
  margin-top: auto;
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding-top: 4px;
  flex-shrink: 0;
}
.specials-modal-btn {
  background: transparent;
  border: 1px solid var(--amber-dark);
  color: var(--amber-dim);
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 3px;
  padding: 8px 14px;
  cursor: pointer;
  text-transform: uppercase;
  transition: all 0.1s;
}
.specials-modal-btn:hover {
  border-color: var(--amber-mid);
  color: var(--amber-mid);
  background: rgba(40, 18, 0, 0.4);
}
.specials-modal-empty {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 20px 18px;
  border-left: 2px solid var(--amber-dim);
  background: rgba(0, 0, 0, 0.4);
}
.specials-modal-empty-line {
  color: var(--amber-mid);
  font-size: 13px;
  letter-spacing: 2px;
}
.specials-modal-empty-sub {
  color: var(--amber-dim);
  font-size: 11px;
  letter-spacing: 1px;
}

/* ── BOSS CONFIRMATION PROMPT ─────────────────────────────────────────
   Screen-modal pattern; fires when the player enters a tile flagged
   isBoss. [Y] engages, [N] withdraws and exploration continues. */
#boss-prompt-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 28px 32px 22px 32px;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  color: var(--amber-mid);
  letter-spacing: 2px;
  font-size: 14px;
}
.boss-prompt-block {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 18px;
  border-left: 2px solid var(--amber-dim);
  background: rgba(0, 0, 0, 0.4);
}

/* Tutorial prompt — same screen-modal frame as the boss prompt for
   visual consistency. Lives in the cockpit so atmosphere is preserved. */
#tutorial-prompt-body {
  padding: 28px 24px 22px 24px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  height: calc(100% - 32px);
  justify-content: center;
}
.tp-header {
  font-size: 11px;
  letter-spacing: 3px;
  color: var(--amber-dim);
  text-align: center;
}
.tp-prompt {
  font-size: 16px;
  letter-spacing: 3px;
  color: var(--amber);
  text-align: center;
  line-height: 1.7;
}
.tp-arc {
  font-size: 18px;
  letter-spacing: 4px;
  color: var(--amber);
}
.tp-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.tp-btn {
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid var(--amber-mid);
  color: var(--amber);
  font-family: inherit;
  font-size: 12px;
  letter-spacing: 3px;
  padding: 12px 16px;
  cursor: pointer;
  text-align: center;
  transition: background 90ms, border-color 90ms;
}
.tp-btn:hover {
  background: rgba(80, 36, 0, 0.5);
  border-color: var(--amber);
}
.tp-footer {
  font-size: 9px;
  letter-spacing: 3px;
  color: var(--amber-dim);
  text-align: center;
}

/* Tutorial SCREEN — full panel for each tutorial beat (movement,
   combat, insanity, etc.). Same screen-modal frame as the prompt; this
   has a title + body block + footer. Brief enough that the player
   reads it and dismisses with Enter/Space. */
#tutorial-screen-body {
  padding: 26px 26px 20px 26px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  height: calc(100% - 32px);
}
.ts-eyebrow {
  font-size: 10px;
  letter-spacing: 3px;
  color: var(--amber-dim);
}
.ts-title {
  font-size: 18px;
  letter-spacing: 4px;
  color: var(--amber);
  text-shadow: 0 0 10px rgba(255, 140, 0, 0.30);
  margin-bottom: 4px;
}
.ts-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}
.ts-line {
  font-size: 13px;
  letter-spacing: 1px;
  color: var(--amber-mid);
  line-height: 1.6;
}
.ts-keys {
  display: inline-block;
  color: var(--amber);
  letter-spacing: 2px;
}
.ts-emph {
  color: var(--amber);
  font-weight: normal;
}
.ts-footer {
  font-size: 9px;
  letter-spacing: 2.5px;
  color: var(--amber-dim);
  text-align: center;
  border-top: 1px solid var(--amber-dim);
  padding-top: 10px;
  margin-top: auto;
}
.boss-prompt-line {
  color: var(--amber-mid);
}
.boss-prompt-line .warn {
  color: var(--warn);
  font-weight: bold;
}
.boss-prompt-line.bright {
  color: var(--amber);
  font-size: 16px;
  letter-spacing: 3px;
  margin-top: 4px;
}
.boss-prompt-buttons {
  display: flex;
  gap: 14px;
  margin-top: auto;
  padding-top: 8px;
}
.boss-prompt-btn {
  flex: 1;
  background: rgba(0, 0, 0, 0.5);
  border: 1px solid var(--amber-mid);
  color: var(--amber);
  font-family: inherit;
  font-size: 14px;
  letter-spacing: 3px;
  padding: 14px 8px;
  cursor: pointer;
  text-transform: uppercase;
  transition: all 0.1s;
}
.boss-prompt-btn:hover {
  background: rgba(80, 30, 0, 0.5);
  border-color: var(--amber);
  box-shadow: 0 0 10px rgba(255, 140, 0, 0.3);
}
.boss-prompt-btn.danger {
  border-color: #aa2200;
  color: var(--warn);
}
.boss-prompt-btn.danger:hover {
  background: rgba(120, 20, 0, 0.4);
  border-color: var(--warn);
  box-shadow: 0 0 12px rgba(204, 21, 0, 0.45);
}

/* ── TREASURE PROMPT ──────────────────────────────────────────────────
   Floats above the crosshair when the player stands on an unopened
   chest tile. Slow blink to draw the eye, terminal amber to match. */
/* Sprint 19.8 — Scene overlay (corner PiP for inline dialogue beats).
   Lives bottom-right of the center viewport opposite the minimap.
   Non-blocking: pointer-events: none so clicks pass through to the
   canvas. Hidden by default; .visible fades it in over 350ms,
   .fading-out fades it back out over 500ms (numbers match
   FADE_OUT_MS in sceneOverlay.js). */
#scene-overlay {
  position: absolute;
  bottom: 12px;
  right: 12px;
  /* Sprint 19.9.23 — bumped from 19 → 40 so commander lines fired
     during a screen modal (e.g. the tutorial's "Close out of the
     logs, please." while the log modal is open) sit ABOVE the modal
     instead of behind it. .screen-modal is at z-index 30. Overlay
     stays pointer-events: none so clicks still pass through to the
     modal beneath it. */
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px 8px 8px;
  width: 320px;
  background: rgba(3, 1, 6, 0.92);
  border: 1px solid var(--amber-dim);
  box-shadow: 0 0 14px rgba(255, 140, 0, 0.22) inset,
              0 0 12px rgba(255, 140, 0, 0.18);
  pointer-events: none;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 350ms ease-out, transform 350ms ease-out;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
}
#scene-overlay.visible {
  opacity: 1;
  transform: translateY(0);
}
#scene-overlay.fading-out {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 500ms ease-in, transform 500ms ease-in;
}
#scene-portrait {
  width: 56px;
  height: 50px;                  /* landscape head-crop, matches Sprint 19.7.2 */
  object-fit: cover;
  object-position: center top;
  border: 1px solid var(--amber-mid);
  background: #000;
  filter: sepia(0.35) saturate(1.3) brightness(0.95) contrast(1.05);
  image-rendering: pixelated;
  flex-shrink: 0;
}
#scene-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
#scene-speaker {
  font-size: 10px;
  letter-spacing: 3px;
  color: var(--amber);
  text-transform: uppercase;
}
#scene-text {
  font-size: 12px;
  letter-spacing: 1px;
  color: var(--amber-mid);
  line-height: 1.4;
  /* Keep long lines from overflowing the 320px frame; word-wrap with
     a tight max-height in case a future scene runs longer. */
  max-height: 64px;
  overflow: hidden;
}

#treasure-prompt {
  position: absolute;
  left: 50%;
  bottom: 22%;
  transform: translateX(-50%);
  z-index: 24;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  font-size: 14px;
  letter-spacing: 3px;
  color: var(--amber);
  text-shadow: 0 0 8px rgba(255, 140, 0, 0.7);
  background: rgba(0, 0, 0, 0.65);
  border: 1px solid var(--amber-dim);
  padding: 6px 14px;
  /* Clickable — Enter/E key still works; mouse click routes through
     the same openChestFromPrompt path. */
  pointer-events: auto;
  cursor: pointer;
  white-space: nowrap;
  animation: slow-blink 1.4s step-end infinite;
}
#treasure-prompt:hover {
  background: rgba(60, 30, 0, 0.8);
  border-color: var(--amber);
}

/* ── SECTOR MAP MODAL (M) ──────────────────────────────────────────────
   Same overlay pattern as the LEVEL UP modal — fills the center monitor,
   crew + action panels stay visible. */
.screen-modal-header .map-modal-floor {
  float: right;
  color: var(--amber-mid);
  font-size: 11px;
  letter-spacing: 2px;
  font-weight: normal;
}
#map-modal-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 18px 14px 12px 14px;
  gap: 12px;
}
#map-modal-canvas {
  background: #000;
  border: 1px solid var(--amber-dim);
  box-shadow: 0 0 12px rgba(255, 140, 0, 0.18) inset;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  flex-shrink: 0;
}
.map-modal-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  justify-content: center;
  font-size: 10px;
  letter-spacing: 2px;
  color: var(--amber-dim);
  padding: 0 8px;
}
.map-modal-legend span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.legend-mark {
  display: inline-block;
  width: 14px;
  text-align: center;
  font-weight: bold;
  font-size: 12px;
}
.legend-player    { color: #ffd070; }
.legend-exit      { color: #ffaa00; }
.legend-boss      { color: #cc1500; font-weight: bold; }
.legend-encounter { color: #cc1500; }
.legend-spectre   { color: var(--amber-mid); }

.map-modal-footer {
  margin-top: auto;
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 4px 8px 0 8px;
  font-size: 10px;
  letter-spacing: 2px;
  color: var(--amber-dim);
}
.map-modal-hint {
  color: var(--amber-mid);
  animation: slow-blink 2s step-end infinite;
}

/* ── MAIN MENU / MISSION SUCCESS overlays ─────────────────────────────
   Full-cockpit takeover screens. Pre-game (main menu) and end-of-run
   (mission success). Share the same dark backdrop + frame styling. */
.main-menu-overlay {
  position: fixed;
  inset: 0;
  z-index: 220;
  background: rgba(0, 0, 0, 0.96);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  color: var(--amber-mid);
  letter-spacing: 1px;
}

/* ── TITLE CARD ─────────────────────────────────────────────────────
   Fullscreen black overlay that fades a centered quote in, holds, and
   fades it back out. Used as the intro card before a new run and the
   outro card before the credits roll. Higher z-index than every other
   overlay so it sits on top of the cockpit, menus, and credits modal.
   The keyframe handles the entire envelope (in / hold / out) so the JS
   only needs to listen for `animationend`. */
.title-card-overlay {
  position: fixed;
  inset: 0;
  z-index: 250;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  cursor: pointer;
}
#title-card-body {
  max-width: min(720px, 88vw);
  text-align: center;
  font-size: 17px;
  letter-spacing: 3px;
  line-height: 2;
  color: var(--amber);
  text-shadow: 0 0 14px rgba(255, 140, 0, 0.30);
  opacity: 0;     /* start invisible; animation handles the reveal */
}
.title-card-overlay.visible #title-card-body {
  animation: title-card-roll 7.5s ease-in-out forwards;
}
#title-card-body p { margin: 0 0 16px 0; }
#title-card-body p:last-child { margin-bottom: 0; }

@keyframes title-card-roll {
  0%   { opacity: 0; }
  18%  { opacity: 1; }   /* ~1.35s fade-in */
  82%  { opacity: 1; }   /* ~4.8s hold     */
  100% { opacity: 0; }   /* ~1.35s fade-out */
}

/* Intro variant — three-beat theatrical epigraph.
   Two stills crossfade into each other, then into a video with its
   Ken Burns pan. The video fades to black and the final line lingers
   alone on the black — the punchline — before it too fades.

   Timing (16s total, matches JS durationSec):
     0.0 – 1.0s   pure black (music beats under)
     1.0 – 2.5s   BEAT 1 (alt_1 still) fades in slowly
     2.5 – 3.5s   LINE 1 types out over alt_1
     3.5 – 4.5s   LINE 1 held
     4.5 – 5.5s   BEAT 1 → BEAT 2 crossfade; LINE 1 fades out
     5.5 – 6.5s   LINE 2 types out over 2_alt
     6.5 – 8.0s   LINE 2 held
     8.0 – 9.0s   BEAT 2 → BEAT 3 crossfade; insert.mp4 begins playing
                  (data-play-after=8000); LINE 2 fades out
     9.0 – 10.0s  LINE 3 types out over the video
    10.0 – 13.0s  LINE 3 held (Ken Burns pan runs 8→13s)
    13.0 – 14.0s  BEAT 3 fades to black; LINE 3 still visible
    14.0 – 15.0s  LINE 3 alone on black — the linger
    15.0 – 16.0s  LINE 3 fades out; card ends → onComplete fires

   The outro card is untouched — keeps its amber-on-black treatment. */

.title-card-overlay.intro #title-card-body {
  max-width: none;
  width: 100vw;
  height: 100vh;
  position: relative;
  padding: 0;
  color: #ffffff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
}
/* Body animation is a no-op — its only job is to fire animationend at
   the durationSec passed by the caller so the onComplete callback
   runs. Actual visuals live on media + line elements below. */
.title-card-overlay.intro.visible #title-card-body {
  animation-name: title-card-intro-body;
}
@keyframes title-card-intro-body {
  0%, 100% { opacity: 1; }
}

/* Common base for all three inserts (two stills + one mp4). Native
   aspect preserved; letterbox bars form naturally against the black
   overlay. All three stack in the same absolute position so crossfades
   occur in place. */
.title-card-overlay.intro .title-card-media {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100vw;
  height: auto;
  transform: translateY(-50%);
  z-index: 0;
  opacity: 0;
}

/* BEAT 1 — alt_1 still (figure in amber halo on black).
   1s→5.5s: slow 1.5s fade in, 2s hold, 1s crossfade to beat 2. */
.title-card-overlay.intro .beat-1 {
  animation: title-card-beat-1 4.5s ease-in-out 1s forwards;
}
@keyframes title-card-beat-1 {
  0%     { opacity: 0; }
  33.33% { opacity: 1; }   /* 1.5s fade in (1→2.5s absolute) */
  77.78% { opacity: 1; }   /* held through 4.5s absolute */
  100%   { opacity: 0; }   /* 1s crossfade to beat 2 (4.5→5.5s) */
}

/* BEAT 2 — 2_alt still (pilot in corridor).
   4.5s→9s: 1s crossfade in, 2.5s hold, 1s crossfade to beat 3. */
.title-card-overlay.intro .beat-2 {
  animation: title-card-beat-2 4.5s ease-in-out 4.5s forwards;
}
@keyframes title-card-beat-2 {
  0%     { opacity: 0; }
  22.22% { opacity: 1; }   /* 1s crossfade in (4.5→5.5s absolute) */
  77.78% { opacity: 1; }   /* held through 8s absolute */
  100%   { opacity: 0; }   /* 1s crossfade to beat 3 (8→9s) */
}

/* BEAT 3 — insert.mp4 (the woman's portrait, "the last video").
   Video.play() fires at 8s (data-play-after=8000). 8s→14s: 1s
   crossfade in, 4s hold + Ken Burns pan (8→13s), 1s FADE TO BLACK
   (not crossfade — line 3 lingers past this). */
.title-card-overlay.intro .beat-3 {
  animation: title-card-beat-3 6s ease-in-out 8s forwards;
}
@keyframes title-card-beat-3 {
  0%     { opacity: 0; }
  16.67% { opacity: 1; }   /* 1s fade in (8→9s absolute) */
  83.33% { opacity: 1; }   /* held through 13s absolute (pan complete) */
  100%   { opacity: 0; }   /* 1s fade to black (13→14s) */
}

/* Text container hovers at the vertical middle of the screen, anchored
   to the LEFT (~8vw in) so lines sit in the darker negative space of
   each insert. Grid + grid-area 1/1 stacks the three lines in the same
   box — they hover in the exact same position; only one is visible at
   a time via clip-path + opacity. */
.title-card-overlay.intro .title-card-text {
  position: absolute;
  top: 50%;
  left: 8%;
  transform: translateY(-50%);
  z-index: 1;
  color: #ffffff;
  font-size: 22px;
  letter-spacing: 2px;
  text-align: left;
  display: grid;
  grid-template-columns: max-content;
  grid-template-rows: auto;
}
.title-card-overlay.intro .title-card-text p {
  grid-area: 1 / 1;
  margin: 0;
  white-space: nowrap;
  clip-path: inset(0 100% 0 0);
  opacity: 1;
}

/* Per-line animations: each line has its own typewriter (clip-path
   steps() over 1s) and its own fade-out (opacity 1→0 over 1s).
   `steps(N)` matches the character count; Share Tech Mono is monospace
   so each step is ~one glyph. Delays place each pair at its beat. */

/* LINE 1: types 2.5→3.5s (over beat 1 held), fades 4.5→5.5s
   (with beat 1 → beat 2 crossfade). */
.title-card-overlay.intro .title-card-text .line-1 {
  animation:
    title-card-line-type 1s steps(38) 2.5s forwards,
    title-card-line-fade 1s ease-in-out 4.5s forwards;
}

/* LINE 2: types 5.5→6.5s (over beat 2 held), fades 8→9s
   (with beat 2 → beat 3 crossfade). */
.title-card-overlay.intro .title-card-text .line-2 {
  animation:
    title-card-line-type 1s steps(30) 5.5s forwards,
    title-card-line-fade 1s ease-in-out 8s forwards;
}

/* LINE 3: types 9→10s (over the mp4), fades 15→16s — outlasting
   beat 3's fade to black at 14s by a full second (the "linger"). */
.title-card-overlay.intro .title-card-text .line-final {
  animation:
    title-card-line-type 1s steps(38) 9s forwards,
    title-card-line-fade 1s ease-in-out 15s forwards;
}

@keyframes title-card-line-type {
  to { clip-path: inset(0 0 0 0); }
}
@keyframes title-card-line-fade {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* ─────────────────────────────────────────────────────────────────────
   STORY BEAT OVERLAY — mid-game cutscene, every third floor.
   Visually mirrors the intro-card treatment but data-driven from
   src/data/storyBeats.js. Separate keyframes so the two systems can
   evolve independently.

   Multi-line envelope (per-beat, scales with line count N):
     0.0 – 1.0s              pure black
     1.0 – 3.0s              image fades in
     3.0 – 3 + (N-1)*4 + 3s  lines cycle 4s each:
                               · 1s typewriter reveal
                               · 2s hold on the image
                               · 1s fade out
                              All lines hover in the SAME position
                              (grid-area 1/1 stacks them in place); the
                              FINAL line stays visible past its "hold"
                              — through the image fade and a 1s
                              alone-on-black linger — before it fades.
     6 + (N-1)*4s             image fades to black; final line stays
     +1s                      final line alone on black — the linger
     +1s                      final line fades; card ends → onComplete

   Total = 9 + (N-1) * 4 seconds.
     N=1 →  9s   N=2 → 13s   N=3 → 17s   N=4 → 21s
   ───────────────────────────────────────────────────────────────────── */

.story-beat-overlay {
  position: fixed;
  inset: 0;
  z-index: 250;                                  /* matches title-card */
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  cursor: pointer;
}
#story-beat-body {
  width: 100vw;
  height: 100vh;
  position: relative;
  padding: 0;
  color: #ffffff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
  opacity: 0;
}
.story-beat-overlay.visible #story-beat-body {
  opacity: 1;
}

/* Body's own animation is a no-op — its only job is to fire
   animationend at the durationSec set inline by playBeat() so the
   onComplete callback runs. All actual visuals live on the .media +
   .line elements. Shared between single-line and dialogue variants. */
.story-beat-overlay.visible:is(.single-line, .dialogue) #story-beat-body {
  animation-name:            story-beat-body;
  animation-timing-function: linear;
  animation-fill-mode:       forwards;
}
@keyframes story-beat-body {
  0%, 100% { opacity: 1; }
}

/* Image slot — every beat is a stack of images at the same absolute
   position; each image has its own inline fade-in/fade-out animation.
   Single-line beats have ONE image; dialogue beats crossfade between
   several as they cycle through shots. */
.story-beat-overlay:is(.single-line, .dialogue) .story-beat-media {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100vw;
  height: auto;
  transform: translateY(-50%);
  z-index: 0;
  opacity: 0;
  /* animations set inline per-image by the renderer. */
}
@keyframes story-beat-media-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes story-beat-media-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* Narration hovers at vertical middle, anchored LEFT (~8vw in) so it
   sits in the darker negative space of most inserts. Grid + grid-area
   1/1 stacks the N lines in the SAME box so each line hovers where
   the previous was — only one is visible at a time via clip-path and
   opacity animations. Same trick the intro card uses. */
.story-beat-overlay:is(.single-line, .dialogue) .story-beat-text {
  position: absolute;
  top: 50%;
  left: 8%;
  transform: translateY(-50%);
  z-index: 1;
  color: #ffffff;
  font-size: 22px;
  letter-spacing: 2px;
  line-height: 1.6;
  text-align: left;
  display: grid;
  grid-template-columns: max-content;
  grid-template-rows: auto;
}
.story-beat-overlay:is(.single-line, .dialogue) .story-beat-line {
  grid-area: 1 / 1;
  margin: 0;
  white-space: nowrap;
  clip-path: inset(0 100% 0 0);
  opacity: 1;
  /* animation is set INLINE by the renderer — steps() gets the exact
     character count, delays get per-line offsets based on index. */
}

@keyframes story-beat-line-type {
  to { clip-path: inset(0 0 0 0); }
}
@keyframes story-beat-line-fade {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* Confirm-to-skip prompt — shared by the intro title card and story
   beats. Hidden by default; fades in when its parent overlay carries
   the `.confirming` class (set by the first skip input in
   src/ui/skipConfirm.js). Sits in the bottom-right letterbox area
   as a subtle system prompt so it reads as UI, not dialogue. */
.skip-confirm-prompt {
  position: absolute;
  bottom: 3vh;
  right: 3vw;
  z-index: 2;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  font-size: 13px;
  letter-spacing: 3px;
  color: #ffffff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
  opacity: 0;
  transition: opacity 240ms ease-out;
  pointer-events: none;
}
.title-card-overlay.confirming  .skip-confirm-prompt,
.story-beat-overlay.confirming  .skip-confirm-prompt {
  opacity: 0.7;
}

#pause-menu-body, #game-over-body {
  width: min(640px, 92vw);
  padding: 60px 56px 44px 56px;
  background: var(--screen-bg);
  border: 1px solid var(--amber-mid);
  box-shadow:
    0 0 0 1px #000,
    0 0 40px rgba(200, 100, 0, 0.35),
    inset 0 0 60px rgba(0, 0, 0, 0.7);
  display: flex;
  flex-direction: column;
  gap: 36px;
}

/* Mission-complete body is its own layout — fullscreen black canvas
   that hosts the scrolling credit roll. No framed-box; the credits
   are the frame. */
#mission-complete-body {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: #000;
  overflow: hidden;
  padding: 0;
}

/* Main menu is its own beast — fullscreen title art with the title +
   buttons overlaid, the way arcade attract screens did it. No framed
   box; the image IS the frame. */
#main-menu-body {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: 6vh 4vw 4vh 4vw;
  background: #000;
  overflow: hidden;
}

/* — Main menu — */
/* Title-screen art fades in over ~3s when the menu opens. Title text,
   buttons, and footer skip the fade so they're immediately readable
   and clickable. */
@keyframes mm-reveal {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.mm-image {
  animation: mm-reveal 3s ease-out both;
  position: absolute;
  /* Slight inset overshoot so the JS shimmer's 1-2 px translate never
     reveals the page background at the edges. The visible portion is
     still the centered crop. */
  top: -4px;
  bottom: -4px;
  left: -4px;
  right: -4px;
  width: calc(100% + 8px);
  height: calc(100% + 8px);
  object-fit: cover;
  object-position: center;
  z-index: 0;
  /* Sensor-scan art is chunky on purpose — keep the pixel edges crisp
     so the shimmer translate snaps cleanly instead of smearing. */
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
  /* Subtle ember pull so the wireframe doesn't read flat against pure
     black, plus a slight darken so overlaid text stays legible. */
  filter: brightness(0.85) contrast(1.05);
  /* `will-change` hints the browser to GPU-composite the translate so
     each ticker frame is cheap. */
  will-change: transform;
}
/* Top + bottom dark gradient — fades the wireframe behind the title and
   the buttons so the amber text sits cleanly. Sides untouched so the
   mech silhouette stays whole. */
.mm-vignette {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(to bottom,
      rgba(0,0,0,0.85) 0%,
      rgba(0,0,0,0.30) 22%,
      rgba(0,0,0,0.20) 60%,
      rgba(0,0,0,0.85) 100%);
  pointer-events: none;
}
.mm-title-block {
  position: relative;
  z-index: 2;
  text-align: center;
}
.mm-title {
  display: block;
  height: clamp(80px, 13vh, 150px);
  width: auto;
  max-width: 90vw;
  margin: 0 auto 14px;
  /* Drop-shadow instead of text-shadow — text-shadow doesn't render on
     img elements. Same amber glow layers as the prior text treatment. */
  filter:
    drop-shadow(0 0 10px rgba(255, 200, 80, 0.85))
    drop-shadow(0 0 26px rgba(255, 140, 0, 0.55))
    drop-shadow(0 0 50px rgba(0, 0, 0, 0.95));
}
.mm-demo-tag {
  font-size: 14px;
  letter-spacing: 4px;
  color: var(--amber);
  margin-bottom: 10px;
  text-shadow: 0 0 8px rgba(0, 0, 0, 0.95);
}
.mm-sub {
  font-size: 11px;
  letter-spacing: 3px;
  color: var(--amber-dim);
  text-shadow: 0 0 8px rgba(0, 0, 0, 0.95);
}
.mm-actions {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: min(420px, 86vw);
}
.mm-btn {
  background: rgba(0, 0, 0, 0.75);
  border: 1px solid var(--amber-mid);
  color: var(--amber);
  font-family: inherit;
  font-size: 16px;
  letter-spacing: 5px;
  padding: 16px 24px;
  cursor: pointer;
  text-align: center;
  transition: all 0.1s;
  backdrop-filter: blur(2px);
}
.mm-btn:hover:not(:disabled) {
  background: rgba(80, 36, 0, 0.55);
  border-color: var(--amber);
  box-shadow: 0 0 14px rgba(255, 140, 0, 0.4);
}
.mm-btn.dim, .mm-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
  border-color: var(--amber-dark);
  color: var(--amber-dim);
}
.mm-btn-sub {
  font-size: 10px;
  letter-spacing: 2px;
  color: var(--amber-dim);
  margin-top: 6px;
}
.mm-footer {
  position: relative;
  z-index: 2;
  text-align: center;
}
.mm-credits {
  font-size: 11px;
  letter-spacing: 4px;
  color: var(--amber-dim);
  text-shadow: 0 0 8px rgba(0, 0, 0, 0.95);
}
.mm-copyright {
  margin-top: 8px;
  font-size: 9px;
  letter-spacing: 2.5px;
  color: var(--amber-dark);
  text-shadow: 0 0 8px rgba(0, 0, 0, 0.95);
}

/* — Mission success / credit roll —
   Classic cinema credits: a long centered column scrolls up from the
   bottom of the screen over ~75s. The "RETURN TO MAIN MENU" button
   stays fixed at the bottom so the player can skip at any moment.
   ESC / SPACE / ENTER also exit (handled in missionComplete.js). */
@keyframes mc-roll {
  from { transform: translateY(100vh); }
  to   { transform: translateY(-100%); }
}
.mc-roll {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 90px;
  padding: 0 20px 200px 20px;        /* 200px bottom so credits clear the fixed button */
  text-align: center;
  color: var(--amber);
  animation: mc-roll 75s linear forwards;
  will-change: transform;
}
.mc-roll-marquee {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin-bottom: 30px;
}
.mc-roll-title {
  font-size: 46px;
  letter-spacing: 12px;
  color: var(--amber);
  text-shadow:
    0 0 10px rgba(255, 200, 80, 0.9),
    0 0 26px rgba(255, 140, 0, 0.55);
}
.mc-roll-sub {
  font-size: 11px;
  letter-spacing: 3px;
  color: var(--amber-mid);
}
.mc-roll-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  max-width: 560px;
}
.mc-roll-block-tight { gap: 14px; }
.mc-roll-thanks {
  font-size: 13px;
  letter-spacing: 2.5px;
  color: var(--amber-mid);
  line-height: 1.9;
}
.mc-roll-thanks-title {
  color: var(--amber);
  letter-spacing: 5px;
  font-size: 17px;
}
/* The full-version teaser sits below the THANK YOU line. Smaller +
   dimmer than the main thanks so it reads as a deferred promise, not
   as a CTA the player has to act on right now. */
.mc-roll-thanks-sub {
  margin-top: 18px;
  font-size: 10px;
  letter-spacing: 2.5px;
  color: var(--amber-dim);
  line-height: 1.8;
}
.mc-roll-label {
  font-size: 10px;
  letter-spacing: 4px;
  color: var(--amber-dim);
}
.mc-roll-name {
  font-size: 20px;
  letter-spacing: 5px;
  color: var(--amber);
}
.mc-roll-sub-line {
  font-size: 10px;
  letter-spacing: 2px;
  color: var(--amber-dim);
}
.mc-roll-sub-line-pad { margin-top: 8px; }

/* Studio logos at the very end of the roll. No glow per request — just
   the source artwork. OTRHVN's source is black on transparent, so we
   invert it so the text reads on the black canvas; YG3 is already
   white-on-dark and renders cleanly as-is. */
.mc-roll-logos {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin-top: 10px;
}
.mc-roll-logo {
  width: auto;
}
/* New OTRHVN logo is already white-on-transparent, so no invert needed.
   Square aspect (1080×1080) renders bigger than the text-style YG3
   mark — sized larger so the visual weights match. */
.mc-roll-logo-otrhvn {
  height: 56px;
}
.mc-roll-logo-yg3 {
  height: 34px;
}
.mc-roll-logos-x {
  font-size: 14px;
  letter-spacing: 2px;
  color: var(--amber-dim);
}
/* Final copyright line at the tail of the credit roll. Dim and small —
   reads as legal boilerplate after the studio logos, not a credit. */
.mc-roll-copyright {
  margin-top: 40px;
  font-size: 10px;
  letter-spacing: 3px;
  color: var(--amber-dark);
}

/* Skip button — fixed at the bottom center, always visible above the
   scrolling roll. Semi-transparent backdrop so credits behind read
   through faintly. */
.mc-ack-btn {
  position: absolute;
  left: 50%;
  bottom: 36px;
  transform: translateX(-50%);
  z-index: 2;
  background: rgba(0, 0, 0, 0.75);
  border: 1px solid var(--amber-mid);
  color: var(--amber);
  font-family: inherit;
  font-size: 12px;
  letter-spacing: 4px;
  padding: 12px 28px;
  cursor: pointer;
  text-transform: uppercase;
  transition: all 0.1s;
  backdrop-filter: blur(2px);
}
.mc-ack-btn:hover {
  background: rgba(80, 30, 0, 0.7);
  border-color: var(--amber);
  box-shadow: 0 0 12px rgba(255, 140, 0, 0.35);
}

/* — Pause menu — */
#pause-menu-body {
  width: min(440px, 92vw);
  padding: 36px 36px 24px 36px;
  gap: 18px;
}
.pm-header {
  font-size: 22px;
  letter-spacing: 8px;
  color: var(--amber);
  text-align: center;
  text-shadow: 0 0 8px rgba(255, 140, 0, 0.5);
}
.pm-flash {
  text-align: center;
  font-size: 13px;
  letter-spacing: 4px;
  color: var(--amber);
  padding: 6px 12px;
  background: rgba(80, 36, 0, 0.45);
  border: 1px solid var(--amber-mid);
  box-shadow: 0 0 12px rgba(255, 140, 0, 0.35);
}
.pm-flash-spacer { min-height: 30px; }
.pm-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.pm-btn {
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid var(--amber-mid);
  color: var(--amber);
  font-family: inherit;
  font-size: 14px;
  letter-spacing: 4px;
  padding: 12px 18px;
  cursor: pointer;
  text-align: left;
  transition: all 0.1s;
}
.pm-btn:hover {
  background: rgba(80, 30, 0, 0.55);
  border-color: var(--amber);
  box-shadow: 0 0 12px rgba(255, 140, 0, 0.35);
}
.pm-footer {
  text-align: center;
  font-size: 10px;
  letter-spacing: 3px;
  color: var(--amber-dim);
}

/* ── GAME OVER (party wipe) ───────────────────────────────────────────
   Red-tinted because this is the one screen where the terminal lets the
   horror leak. Cockpit hum is muted underneath by gameOver.js — silent
   under the prompt is the point. */
.go-title {
  text-align: center;
  font-size: 36px;
  letter-spacing: 8px;
  color: #cc1500;
  margin-bottom: 12px;
  text-shadow: 0 0 8px rgba(204, 21, 0, 0.4);
}
.go-sub {
  text-align: center;
  font-size: 11px;
  letter-spacing: 3px;
  color: var(--amber-dim);
  margin-bottom: 36px;
}
.go-prompt {
  text-align: center;
  font-size: 16px;
  letter-spacing: 4px;
  color: var(--amber);
  margin-bottom: 22px;
}
.go-actions {
  display: flex;
  gap: 16px;
  justify-content: center;
  margin-bottom: 30px;
}
.go-btn {
  background: transparent;
  border: 1px solid var(--amber-mid);
  color: var(--amber);
  font-family: inherit;
  font-size: 13px;
  letter-spacing: 3px;
  padding: 12px 28px;
  cursor: pointer;
  transition: border-color 90ms, background 90ms, color 90ms;
}
.go-btn:hover {
  border-color: var(--amber);
  background: rgba(255, 140, 0, 0.10);
  color: #fff;
}
.go-footer {
  text-align: center;
  font-size: 9px;
  letter-spacing: 3px;
  color: var(--amber-dim);
}

/* ── COMMAND SELECT (pre-descent briefing) ────────────────────────────
   Full-cockpit takeover with the four candidate commanders. Closes when
   the player confirms; the chosen commander's trait then applies to the
   crew and a small badge appears in the action terminal. */
.commander-select-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0, 0, 0, 0.94);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Share Tech Mono', 'Courier New', monospace;
  color: var(--amber-mid);
  letter-spacing: 1px;
}
.cmdr-select-frame {
  width: min(820px, 94vw);
  max-height: 92vh;
  display: flex;
  flex-direction: column;
  background: var(--screen-bg);
  border: 1px solid var(--amber-mid);
  box-shadow:
    0 0 0 1px #000,
    0 0 40px rgba(200, 100, 0, 0.35),
    inset 0 0 60px rgba(0, 0, 0, 0.7);
}
.cmdr-select-header {
  background: var(--amber-deep);
  border-bottom: 1px solid var(--amber-dim);
  padding: 10px 14px;
  font-size: 13px;
  letter-spacing: 4px;
  color: var(--amber);
  text-transform: uppercase;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.cmdr-select-sub {
  font-size: 10px;
  letter-spacing: 3px;
  color: var(--amber-mid);
}
.cmdr-select-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  padding: 18px;
  flex: 1;
  overflow-y: auto;
}
#commander-roster {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  align-content: start;
}
.cmdr-card {
  border: 1px solid var(--amber-dark);
  background: rgba(0, 0, 0, 0.55);
  padding: 8px 8px 6px 8px;
  cursor: pointer;
  text-align: center;
  transition: border-color 0.15s, background 0.15s;
}
.cmdr-card:hover {
  border-color: var(--amber-mid);
  background: rgba(40, 18, 0, 0.55);
}
.cmdr-card.selected {
  border-color: var(--amber);
  background: rgba(80, 36, 0, 0.55);
  box-shadow: inset 0 0 12px rgba(255, 140, 0, 0.25);
}
.cmdr-portrait {
  /* Sprint 19.7.2 — landscape aspect (≈ 0.88) forces object-fit:cover
     to crop the caption strip off the bottom of the source. */
  width: 100%;
  aspect-ratio: 220 / 195;
  object-fit: cover;
  object-position: center top;
  display: block;
  background: #000;
  border: 1px solid var(--amber-dim);
  image-rendering: auto;
}
.cmdr-card-name {
  margin-top: 6px;
  font-size: 14px;
  letter-spacing: 4px;
  color: var(--amber);
}
.cmdr-card-role {
  font-size: 9px;
  letter-spacing: 2px;
  color: var(--amber-dim);
  text-transform: uppercase;
}

/* Right column — detail panel for the highlighted commander. */
#commander-detail {
  border: 1px solid var(--amber-dim);
  background: rgba(0, 0, 0, 0.5);
  padding: 18px 18px 22px 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: 12px;
}
.cmdr-detail-name {
  font-size: 22px;
  letter-spacing: 6px;
  color: var(--amber);
}
.cmdr-detail-role {
  font-size: 10px;
  letter-spacing: 3px;
  color: var(--amber-dim);
  text-transform: uppercase;
  margin-bottom: 14px;
}
.cmdr-detail-trait-label {
  font-size: 13px;
  letter-spacing: 3px;
  color: var(--amber);
  border-bottom: 1px solid var(--amber-dark);
  padding-bottom: 4px;
  margin-top: 4px;
}
.cmdr-detail-trait-desc {
  font-size: 12px;
  letter-spacing: 1px;
  color: var(--amber-mid);
  line-height: 1.5;
}

.cmdr-select-footer {
  padding: 12px 18px 16px 18px;
  border-top: 1px solid var(--amber-dim);
  display: flex;
  justify-content: flex-end;
}
.cmdr-confirm-btn {
  background: transparent;
  border: 1px solid var(--amber-mid);
  color: var(--amber);
  font-family: inherit;
  font-size: 12px;
  letter-spacing: 3px;
  padding: 8px 18px;
  cursor: pointer;
  text-transform: uppercase;
  transition: all 0.1s;
}
.cmdr-confirm-btn:hover {
  background: rgba(80, 30, 0, 0.55);
  border-color: var(--amber);
  box-shadow: 0 0 12px rgba(255, 140, 0, 0.35);
}

/* ── ACTIVE COMMANDER BADGE (action terminal header) ──────────────── */
#commander-badge {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0 8px 0;
  padding: 5px 6px;
  border: 1px solid var(--amber-dim);
  background: rgba(0, 0, 0, 0.5);
  cursor: pointer;
  transition: border-color 80ms, background 80ms;
}
#commander-badge:not(.hidden):hover {
  border-color: var(--amber);
  background: rgba(255, 140, 0, 0.10);
}
.cmdr-badge-portrait {
  width: 30px;
  height: 27px;                      /* Sprint 19.7.2 — landscape crops the caption strip */
  object-fit: cover;
  object-position: center top;
  border: 1px solid var(--amber-dim);
  background: #000;
  flex-shrink: 0;
}
.cmdr-badge-text {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
  overflow: hidden;
}
.cmdr-badge-name {
  font-size: 10px;
  color: var(--amber);
  letter-spacing: 2px;
}
.cmdr-badge-trait {
  font-size: 8px;
  color: var(--amber-dim);
  letter-spacing: 1.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── COMMANDER DOSSIER ─────────────────────────────────────────────── */
#commander-dossier-body {
  padding: 16px 20px 20px 20px;
  color: var(--amber);
  font-size: 11px;
  letter-spacing: 1px;
  height: calc(100% - 32px);
  overflow-y: auto;
}
#commander-dossier-body::-webkit-scrollbar { width: 4px; }
#commander-dossier-body::-webkit-scrollbar-thumb { background: var(--amber-dark); }
.dossier-frame::-webkit-scrollbar { width: 4px; }
.dossier-frame::-webkit-scrollbar-thumb { background: var(--amber-dark); }
#document-reader-body::-webkit-scrollbar { width: 4px; }
#document-reader-body::-webkit-scrollbar-thumb { background: var(--amber-dark); }
.cmdr-select-body::-webkit-scrollbar { width: 4px; }
.cmdr-select-body::-webkit-scrollbar-thumb { background: var(--amber-dark); }
.cmdr-dossier-portrait-wrap {
  display: flex;
  justify-content: center;
  margin-bottom: 12px;
}
.cmdr-dossier-portrait {
  width: 140px;
  height: 124px;                     /* Sprint 19.7.2 — landscape crops the caption strip */
  object-fit: cover;
  object-position: center top;
  border: 1px solid var(--amber-dim);
  background: #000;
}
.cmdr-dossier-identity {
  text-align: center;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--amber-dim);
  padding-bottom: 12px;
}
.cmdr-dossier-name {
  font-size: 16px;
  letter-spacing: 3px;
  color: var(--amber);
  margin-bottom: 4px;
}
.cmdr-dossier-trait {
  font-size: 11px;
  letter-spacing: 2px;
  color: var(--amber);
}
.cmdr-dossier-trait-desc {
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--amber-dim);
  margin-top: 4px;
  line-height: 1.4;
}
.cmdr-dossier-section-header {
  font-size: 10px;
  letter-spacing: 2px;
  color: var(--amber);
  margin-bottom: 10px;
}
.cmdr-dossier-acc {
  border: 1px solid var(--amber-dim);
  padding: 8px 10px;
  margin-bottom: 8px;
  background: rgba(0, 0, 0, 0.4);
}
.cmdr-dossier-acc-name {
  font-size: 11px;
  letter-spacing: 2px;
  color: var(--amber);
  margin-bottom: 3px;
}
.cmdr-dossier-acc-desc {
  font-size: 9px;
  letter-spacing: 0.5px;
  color: var(--amber-dim);
  line-height: 1.5;
}
.cmdr-dossier-empty {
  font-size: 10px;
  letter-spacing: 1px;
  color: var(--amber-dim);
  text-align: center;
  padding: 18px 0;
  border: 1px dashed var(--amber-dim);
  line-height: 1.6;
}
.cmdr-dossier-footer {
  margin-top: 18px;
  text-align: center;
  font-size: 8px;
  letter-spacing: 2px;
  color: var(--amber-dim);
}
.cmdr-dossier-close {
  background: none;
  border: none;
  color: var(--amber);
  font-family: inherit;
  font-size: 11px;
  cursor: pointer;
  letter-spacing: 1px;
  padding: 0 4px;
}
.cmdr-dossier-close:hover { color: #fff; }

/* ── HELP / OPERATIONS HANDBOOK ──────────────────────────────────────
   Reads like a printed crew manual stamped FOR THE COMMANDER. Cover
   page at the top with classification line, then numbered §-sections
   below. Same screen-modal frame as the dossiers; body scrolls. */
#help-menu-body {
  padding: 18px 22px 22px 22px;
  color: var(--amber);
  font-size: 11px;
  letter-spacing: 0.6px;
  height: calc(100% - 32px);
  overflow-y: auto;
  line-height: 1.55;
}
#help-menu-body::-webkit-scrollbar { width: 4px; }
#help-menu-body::-webkit-scrollbar-thumb { background: var(--amber-dark); }
.help-cover {
  text-align: center;
  border: 1px solid var(--amber-dim);
  padding: 14px 16px 16px 16px;
  margin-bottom: 18px;
  background: rgba(255, 140, 0, 0.04);
}
.help-stamp {
  font-size: 9px;
  letter-spacing: 3px;
  color: var(--amber-dim);
  margin-bottom: 10px;
}
.help-title {
  font-size: 16px;
  letter-spacing: 4px;
  color: var(--amber);
  margin-bottom: 4px;
}
.help-sub {
  font-size: 10px;
  letter-spacing: 2px;
  color: var(--amber-dim);
  margin-bottom: 8px;
}
.help-classification {
  font-size: 8px;
  letter-spacing: 2.5px;
  color: #cc5500;
  border-top: 1px dashed var(--amber-dim);
  padding-top: 6px;
}
.help-section {
  margin-bottom: 18px;
  border-left: 1px solid var(--amber-dim);
  padding-left: 12px;
}
.help-section-header {
  font-size: 13px;
  letter-spacing: 2.5px;
  color: var(--amber);
  margin-bottom: 8px;
}
.help-section p {
  font-size: 13px;
  letter-spacing: 0.5px;
  color: #ffffff;
  margin: 5px 0 8px 0;
  line-height: 1.45;
}
.help-list {
  list-style: none;
  padding-left: 4px;
  margin: 5px 0 8px 0;
  font-size: 13px;
  color: #ffffff;
  line-height: 1.45;
}
.help-list > li {
  margin: 3px 0;
  padding-left: 8px;
  border-left: 1px solid transparent;
}
.help-list ul {
  list-style: none;
  padding-left: 14px;
  margin: 3px 0;
}
.help-list ul li { margin: 2px 0; }
.help-list .k {
  display: inline-block;
  min-width: 44px;
  color: var(--amber);
  margin-right: 4px;
}
.help-list b { color: var(--amber); font-weight: normal; }
.help-list .amb { color: var(--amber); }
.help-section .amb { color: var(--amber); }
.help-note {
  font-size: 12px;
  letter-spacing: 1px;
  color: #d0d0d0;
  border-top: 1px dashed var(--amber-dim);
  padding-top: 5px;
  margin-top: 8px;
  font-style: italic;
  line-height: 1.4;
}
.help-footer {
  text-align: center;
  border-top: 1px solid var(--amber-dim);
  padding-top: 12px;
  margin-top: 6px;
  font-size: 12px;
  letter-spacing: 2px;
  color: var(--amber);
}
.help-footer-sub {
  font-size: 10px;
  letter-spacing: 2.5px;
  color: #a0a0a0;
}
.help-close {
  background: none;
  border: none;
  color: var(--amber);
  font-family: inherit;
  font-size: 11px;
  cursor: pointer;
  letter-spacing: 1px;
  padding: 0 4px;
}
.help-close:hover { color: #fff; }
