/* BUNNyS cockpit: tokens, hub layer stack, HUD look, boot sequence.
   Owner: css/cockpit.css (see DESIGN.md "File ownership").
   Hard rule: never set `transform` on .pano-ring, .pano-slice or .target —
   js/cockpit.js writes those inline every frame.

   Sections, in order: tokens; base; shared corner-bracket mechanism; hub layers &
   panorama; HUD SVG styling; controls (nav/sr-only/hud-btn); boot sequence; five
   canopy screens; power-on; hub HTML panels (.slew/.pop/.hudmode); sub-page screen
   & panels; sub-page HUD (.phud-*); page handover (#link); kbd.
   Each @media (prefers-reduced-motion: reduce) rule sits beside the thing it calms. */

/* ---------- tokens ---------- */
:root {
  --night: #060A12;
  --indigo: #0E1830;
  --teal: #1F4E5F;
  --sodium: #FF9A3D;
  --hud: #8CFFC1;
  --hud-dim: rgba(140, 255, 193, .45);
  --amber: #FFB02E;
  --lock: #FF3347;
  --ice: #DDE7EE;
  /* painted machine housing, its lit top edge, and the recessed display glass set into it.
     These are opaque paint and glass, not frosted panes: no backdrop-filter anywhere. */
  --housing: #131A24;
  --housing-lip: #202A37;
  --glass: #070C15;
  --edge: rgba(221, 231, 238, .20);    /* machined hairline */
  --stencil: rgba(221, 231, 238, .55); /* silkscreened part numbers; .40 measured 3.4:1 */
  --panel: rgba(6, 10, 18, .97);       /* every housing's backing plate; hud.js housing() matches it */
  /* One tight shadow, not a bloom. Spent only where it carries meaning: the boresight,
     a lock and a caution. Everything else is a crisp stroke. */
  --glow: drop-shadow(0 0 1.5px currentColor);
  --z-pano: 1;
  --z-fx: 2;
  --z-frame: 3;
  --z-hud: 4;
  --z-nav: 5;
  --z-link: 9;
  --z-boot: 10;
}

/* ---------- base ---------- */
* { box-sizing: border-box; }
/* `overflow-x` lives on html alone, not on body too: giving body its own non-visible
   overflow (on top of the old `html, body { height: 100% }`) was what turned <body> into
   a second scroll container on every sub-page, under the window's own -- pagehud.js then
   had to listen to body's scroll with a capturing listener instead of the plain window one
   every other page-scroll API expects. `clip`, not `hidden`: either stops the sideways
   scroll, but `hidden` still forces the other axis to `auto` at computed-value time (per
   the overflow spec's visible/non-visible pairing rule), which is the same coupling that
   caused this; `clip` carries no scrolling behaviour to force anywhere. */
html { overflow-x: clip; }
body {
  margin: 0;
  background: var(--night);
  color: var(--ice);
  font: 16px/1.55 'B612', 'Helvetica Neue', Arial, sans-serif;
}
body.hub { height: 100vh; overflow: hidden; } /* the hub itself never scrolls */
#cockpit { position: relative; height: 100%; background: var(--night); }

a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--hud);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
}

/* ---------- shared mechanism: corner brackets ---------- */
/* Four L-shaped brackets cut into a box's corners, as eight no-repeat background
   gradients (one long arm + one short arm per corner, so none of them tile). One rule,
   driven per use by --cnr-c (colour), --cnr-arm (long-arm length) and --cnr-th (stroke
   thickness): .target's own background, and the ::before layer on .boot-readout,
   .panel, .slew/.pop and .phud-blip.is-me/.is-lock, all drew this same eight-gradient
   trick inline at their own sizes and colours before this rule unified them. */
.target,
.boot-readout::before,
.panel::before,
.slew::before, .pop::before,
.phud-blip.is-me::before, .phud-blip.is-lock::before {
  --cnr: linear-gradient(var(--cnr-c), var(--cnr-c));
  background-image: var(--cnr), var(--cnr), var(--cnr), var(--cnr), var(--cnr), var(--cnr), var(--cnr), var(--cnr);
  background-repeat: no-repeat;
  background-position: top left, top left, top right, top right, bottom left, bottom left, bottom right, bottom right;
  background-size:
    var(--cnr-arm) var(--cnr-th), var(--cnr-th) var(--cnr-arm),
    var(--cnr-arm) var(--cnr-th), var(--cnr-th) var(--cnr-arm),
    var(--cnr-arm) var(--cnr-th), var(--cnr-th) var(--cnr-arm),
    var(--cnr-arm) var(--cnr-th), var(--cnr-th) var(--cnr-arm);
}

/* ---------- layer 1: the panorama cylinder ---------- */
#pano {
  position: fixed;
  inset: 0;
  z-index: var(--z-pano);
  overflow: hidden;
  background: var(--night);
  /* With the ring pushed onto the camera (see cockpit.js layout()), this is a real
     rectilinear field of view again: 2*atan(0.5 / 0.42) = 100deg across on landscape. */
  perspective: 42vw;
  perspective-origin: 50% 50%;
}
@media (orientation: portrait) {
  #pano { perspective: 71vw; } /* ~70deg across on portrait */
}

.pano-ring {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  will-change: transform;
}

.pano-slice {
  position: absolute;
  top: 50%;
  left: 50%;
  /* size, background-size and background-position are written by cockpit.js:
     every tile is a different size and shows a different cell of the sphere */
  background-image: url(../img/pano.svg);
  /* repeat-x, not no-repeat: tiles overlap their neighbours to hide the chord seams,
     and at the x=0 wrap that bleed runs off the image. The strip is periodic in x by
     construction, so repeating horizontally makes the wrap continuous. */
  background-repeat: repeat-x;
  transform-origin: 50% 50% 0;
  backface-visibility: hidden;
  /* no will-change here: there are ~360 of these, and promoting every one to its own
     compositor layer cost more than it saved (measured: 166 -> 266 frames across the boot). */
}

/* ---------- targets (children of .pano-ring, turn with the scene) ---------- */
.target {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 72px;
  height: 72px;
  margin: -36px 0 0 -36px;
  color: var(--hud);
  cursor: pointer;
  pointer-events: auto;
  transition: width .35s steps(3, end), height .35s steps(3, end),
              margin .35s steps(3, end), color .2s ease;
  /* four corner brackets, via the shared mechanism above, pinned to the box's corners */
  --cnr-c: currentColor;
  --cnr-arm: 16px;
  --cnr-th: 2px;
}
/* the emblem: a small diamond mark centred in the bracket box */
.target::before {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 13px; height: 13px;
  margin: -6.5px 0 0 -6.5px;
  border: 1.5px solid currentColor;
  transform: rotate(45deg);
}
/* label + range readout, stacked under the box */
.target::after {
  content: attr(data-label) '\a' attr(data-readout);
  position: absolute;
  top: 100%; left: 50%;
  margin-top: 10px;
  transform: translateX(-50%);
  white-space: pre-line;
  text-align: center;
  width: max-content;
  max-width: 30ch;
  font: 700 12px/1.6 'B612 Mono', monospace;
  letter-spacing: .08em;
  opacity: .8;
  transition: opacity .3s ease;
  /* the label sits directly on the scene; without a backing it falls below
     4.5:1 wherever it crosses lit windows or water reflections (measured) */
  background: rgba(6, 10, 18, .82);
  padding: 2px 6px;
}
.target.is-locked {
  width: 56px; height: 56px; margin: -28px 0 0 -28px;
  color: var(--lock);
}
.target.is-locked::after { opacity: 1; }
@media (max-width: 480px) {
  .target { width: 56px; height: 56px; margin: -28px 0 0 -28px; }
  .target.is-locked { width: 44px; height: 44px; margin: -22px 0 0 -22px; }
  .target::after { max-width: 22ch; font-size: 11px; }
}
@media (prefers-reduced-motion: reduce) {
  .target { transition: none; }
}

/* ---------- layer 2: canvas fx ---------- */
#fx {
  position: fixed;
  inset: 0;
  z-index: var(--z-fx);
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* ---------- layer 3: the fixed cockpit frame ---------- */
#frame {
  position: fixed;
  inset: 0;
  z-index: var(--z-frame);
  width: 100%;
  height: 100%;
  pointer-events: none;
  user-select: none;
}

/* ============ HUD SVG styling ============ */
/* ---------- layer 4: the HUD overlay ---------- */
#hud {
  position: fixed;
  inset: 0;
  z-index: var(--z-hud);
  width: 100%;
  height: 100%;
  pointer-events: none;
  color: var(--hud);
}
#hud :is(path, line, polyline, polygon, circle, rect):not([fill]) {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: square;
}
/* Graticules, brackets and rules are drawn in the machine green; the values read on them
   are cool pale. Amber and red override per state, so colour always means something. */
#hud text, #hud .hdg-readout {
  font: 700 13px/1 'B612 Mono', monospace;
  letter-spacing: .08em;
  fill: var(--ice);
}
#hud .boresight { filter: var(--glow); }
#hud .warn text, #hud .is-locked text, #hud .blip.is-locked text { fill: currentColor; }
#hud .warn, #hud .is-locked { filter: var(--glow); }

/* HUD draw-in during boot: strokes draw in, the reticle drops from 3x with a snap.
   :not(.dmg-zone) excludes the damage map's 8 zone paths -- they redraw their `d`
   every rotation frame, and dash-in would thrash on top of that churn. */
#hud.hud-draw :is(path, line, polyline, circle, rect):not(.dmg-zone) {
  stroke-dasharray: 260;
  animation: hud-stroke-in 1.1s ease-out 1 both;
}
@keyframes hud-stroke-in {
  from { stroke-dashoffset: 260; }
  to   { stroke-dashoffset: 0; }
}
#hud.hud-draw .boresight,
#hud.hud-draw [data-boresight] {
  transform-box: fill-box;
  transform-origin: center;
  animation: reticle-drop .5s cubic-bezier(.2, 1.6, .4, 1) 1 both;
}
@keyframes reticle-drop {
  0%   { transform: scale(3); opacity: 0; }
  70%  { transform: scale(.94); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  #hud.hud-draw :is(path, line, polyline, circle, rect) { animation: none; stroke-dashoffset: 0; }
  #hud.hud-draw .boresight, #hud.hud-draw [data-boresight] { animation: none; transform: none; opacity: 1; }
}

/* ---------- instruments: radar scope, system gauges, caution banner ---------- */
#hud .radar text { font-size: 11px; opacity: .8; }
#hud .blip rect { fill: rgba(6, 10, 18, .6); }
#hud .blip text { font-size: 10px; }
#hud .blip.is-locked { color: var(--lock); }
#hud .warn { color: var(--amber); }
#hud .warn text { font-size: 13px; }
#hud .warn.is-blinking { animation: warn-blink 1s steps(2, end) infinite; }
@keyframes warn-blink { 0%, 60% { opacity: 1; } 61%, 100% { opacity: .45; } }
/* bigger caution banner text */
#hud .warn text.warn-text { font-size: 22px; letter-spacing: .06em; }

/* The hostile contact's alarm inverts its whole housing rather than glowing: a hard
   two-frame flash catches the eye at the edge of vision without any bloom, which is the
   look this HUD is after. Animations outrank the inline fills hud.js writes, so the text
   and brackets invert with the box.
   steps(1, end), not steps(2): the timing function runs per keyframe segment, so two
   steps put a blended half-colour between the two states and softened the flash into a
   pulse. One step holds each state flat for its whole segment -- a square wave. */
/* Two static states, flipped together by drawAlarmFlash() in hud.js. These were three
   separate CSS animations; each kept its own clock from whenever its element was last
   attached, so box and ink drifted apart and the alarm went red-on-red or dark-on-dark.
   Driving the flip from the one tick() clock keeps the inversion exact by construction. */
/* !important because hud.js writes fill into each element's inline style: the animations
   these rules replaced outranked it for free, a plain rule does not. */
#hud .hx-alarm.is-inv rect { fill: var(--lock) !important; }
#hud .hx-alarm.is-inv text { fill: var(--night) !important; }
#hud .hx-alarm.is-inv line { stroke: var(--night) !important; }

/* damage map: zones sit at a permanent state colour (set below) and amber/red ones
   flash to it from a dim "off" tone -- steps(1, end), same hard square-wave as the
   hostile alarm above, not steps(2) which blends a half-colour into a soft pulse. */
#hud .dmg-zone.is-caution { fill: var(--amber); animation: dmg-zone-flash .8s steps(1, end) infinite; }
#hud .dmg-zone.is-hit { fill: var(--lock); animation: dmg-zone-flash .8s steps(1, end) infinite; }
@keyframes dmg-zone-flash { 50% { fill: rgba(140, 255, 193, .16); } }
@media (prefers-reduced-motion: reduce) {
  #hud .warn.is-blinking { animation: none; opacity: 1; }
  /* the flash is JS-driven now; drawAlarmFlash() never sets .is-inv under reduced motion,
     which leaves the legible base state (dark box, red ink) showing */
  #hud .dmg-zone.is-caution, #hud .dmg-zone.is-hit { animation: none; }
}

/* target popup: raised under the reticle when a contact is latched */
#hud .dossier { transition: opacity .18s ease-out; }
#hud .dossier text { font-size: 14px; text-anchor: middle; }
#hud .dossier .dim { font-size: 11px; opacity: .75; }
#hud .dossier .dos-title { font-size: 22px; letter-spacing: .1em; }
#hud .dossier .dos-hint { font-size: 11px; opacity: .7; letter-spacing: .14em; }

/* ---------- controls: nav, sr-only, hud buttons ---------- */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.hud-btn {
  font: 700 12px/1 'B612 Mono', monospace;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ice);
  background: linear-gradient(var(--housing-lip), var(--housing) 2px);
  border: 1px solid var(--edge);
  border-radius: 0;
  min-width: 44px;
  min-height: 44px;
  padding: 10px 16px;
  cursor: pointer;
}
.hud-btn:hover { background: rgba(140, 255, 193, .14); }
@media (max-width: 480px) {
  .hud-btn { padding: 10px 12px; font-size: 11px; }
}

#targets-nav {
  position: fixed;
  top: 16px; left: 16px;
  z-index: var(--z-nav);
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
}
#targets-nav:focus-within {
  width: auto; height: auto;
  overflow: visible;
  clip: auto;
  clip-path: none;
  max-width: calc(100vw - 32px);
  background: linear-gradient(var(--housing-lip), var(--housing) 2px);
  border: 1px solid var(--edge);
  padding: 12px 16px;
}
#targets-nav ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
#targets-nav a {
  display: block;
  min-height: 44px;
  padding: 6px 4px;
  font: 700 13px/1.3 'B612 Mono', monospace;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--hud);
  text-decoration: none;
  white-space: nowrap;
}
#targets-nav a:hover, #targets-nav a:focus-visible { color: var(--ice); }
@media (max-width: 480px) {
  #targets-nav { top: 10px; left: 10px; }
  #targets-nav:focus-within { max-width: calc(100vw - 20px); padding: 10px 12px; }
}

#tilt { position: fixed; right: 16px; bottom: 16px; z-index: var(--z-nav); }
#lock-status { z-index: var(--z-nav); }
@media (max-width: 480px) {
  #tilt { right: 10px; bottom: 10px; }
}

/* ============ boot sequence ============ */
/* The boot is seen through the cockpit's five canopy screens and frame, which stay on screen
   the whole time. Its picture is canvas#boot-scene, a fixed layer at the panorama's z-index but
   later in the document, so it paints over the panorama and under #fx/#screens, #frame and
   #hud. #boot on top carries only the type, which stays crisp at any size. */
#boot-scene {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: var(--z-pano);
  background: var(--night);   /* dark from first paint, before boot.js draws */
  pointer-events: none;
  transition: opacity .6s ease;
}
#boot {
  position: fixed;
  inset: 0;
  z-index: var(--z-boot);
  color: var(--hud);
  pointer-events: none;
  transition: opacity .6s ease; /* boot.js removes the node 650ms after adding .boot-out */
}
#boot #skip { pointer-events: auto; }
#boot.boot-out, #boot-scene.boot-out { opacity: 0; pointer-events: none; }
/* The instruments stay dark through the boot -- only the screens and the frame are lit -- and
   power on at the hand-over (hud.js powerOn), the moment the boot starts to fade. */
body:has(> #boot:not(.boot-out)) :is(#hud, #targets-nav, #slew, #hudmode, #comms, #toast, #tilt) { visibility: hidden; }

/* The stage title and log share one housing, like every HUD box: an opaque plate, so the canopy
   grid and the top seam never run through the type, corner brackets, and a header rule with a
   12px tick scale. Fixed at four log rows and the longest row's width, so it never resizes as
   lines arrive. */
.boot-readout {
  position: absolute;
  left: 24px;
  top: 20px;
  padding: 13px 14px 11px;
  background: var(--panel);
  border-radius: 3px;
  color: var(--hud);
}
.boot-readout::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  --cnr-c: var(--hud); --cnr-arm: 10px; --cnr-th: 1.5px;
}
.boot-stage {
  margin: 0;
  padding-bottom: 9px;
  display: flex;
  align-items: center;
  gap: 12px;
  font: 700 12px/1 'B612 Mono', monospace;
  letter-spacing: .3em;
  /* the header rule and its tick scale */
  background:
    linear-gradient(rgba(140, 255, 193, .55), rgba(140, 255, 193, .55)) bottom / 100% 1px no-repeat,
    repeating-linear-gradient(90deg, rgba(140, 255, 193, .55) 0 1px, transparent 1px 12px) bottom / 100% 4px no-repeat;
}
.boot-stage .pips { display: flex; gap: 4px; }
.boot-stage .pips i { width: 14px; height: 3px; background: rgba(140, 255, 193, .2); transition: background .3s; }
.boot-stage .pips i.on { background: var(--hud); }
#boot-log {
  margin: 9px 0 0;
  width: 33ch;
  height: calc(4 * 1.7em);
  overflow: hidden;
  font: 12px/1.7 'B612 Mono', monospace;
  letter-spacing: .02em;
  opacity: .85;
  white-space: pre;
}
@media (max-width: 480px) {
  .boot-readout { left: 12px; top: 12px; padding: 11px 12px 9px; }
  #boot-log { font-size: 11px; }
  #boot-log, .boot-stage { left: 12px; }
}

/* captions under the monitor; boot.js sets `top` so they clear the panel at any size */
.boot-cap { position: absolute; left: 16px; right: 16px; text-align: center; transition: opacity .4s ease, transform .6s cubic-bezier(.65, 0, .35, 1); }
.boot-title {
  margin: 0;
  min-height: 1.1em;
  font: 700 clamp(22px, 4.4vw, 44px)/1.1 'B612', sans-serif;
  letter-spacing: .08em;
  color: var(--ice);
  text-shadow: 0 0 14px rgba(6, 10, 18, .9);
}
.boot-title[data-tone=hud] { color: var(--hud); filter: var(--glow); }
.boot-note {
  margin: 10px 0 0;
  min-height: 1.4em;
  font: 700 12px/1.4 'B612 Mono', monospace;
  letter-spacing: .4em;
  text-indent: .4em;
  color: rgba(221, 231, 238, .7);
  text-shadow: 0 0 10px rgba(6, 10, 18, .9);
}
.boot-note[data-tone=hud] { color: var(--hud); }
.boot-note[data-tone=amber] { color: var(--amber); }
.boot-note[data-tone=go] { color: var(--amber); font-size: clamp(15px, 2.4vw, 22px); letter-spacing: .6em; text-indent: .6em; }
.boot-stage, .boot-title, .boot-note { transition: opacity .16s ease; }
.boot-cap.is-out, .boot-stage.is-out, .boot-title.is-out, .boot-note.is-out { opacity: 0; }

.boot-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: rgba(140, 255, 193, .12); }
/* No CSS animation: boot.js sets scaleX every frame from the boot clock, the same clock the
   scene and captions read, so they can never drift apart. */
.boot-bar i {
  display: block;
  height: 100%;
  width: 100%;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: var(--hud);
}

#boot #skip {
  position: absolute;
  right: 20px;
  bottom: 20px;
}

/* frame seam glow (spec 6): "the frame seams glow green then settle". boot.js adds
   seam-glow as the boot hands over to the hub and removes it 1.3s later. */
#frame.seam-glow { animation: frame-seam-glow 1.2s ease-out 1; }
@keyframes frame-seam-glow {
  0%, 100% { filter: none; }
  50% { filter: drop-shadow(0 0 6px var(--hud)) brightness(1.2); }
}
@media (prefers-reduced-motion: reduce) {
  #boot { transition: none; }
  .boot-bar i { transform: scaleX(1); }
  #frame.seam-glow { animation: none; filter: none; }
}

/* ============ five canopy screens ============ */
/* The canopy is five screens, not one pane (hud.js buildScreens): between the panorama and the
   frame; never takes input. The shutter is each screen's "off" state; .powering runs the bring-up. */
#screens { position: fixed; inset: 0; width: 100%; height: 100%; z-index: var(--z-fx); pointer-events: none; color: var(--hud); }
#screens .shutter { fill: #03060B; opacity: 0; }
#screens .seam { fill: none; stroke: var(--hud); stroke-opacity: 0; stroke-width: 1; }
#screens .calib { opacity: 0; }
#screens .calib line { stroke: var(--hud); stroke-width: 1; }
#screens .calib-horizon { stroke-opacity: .85; }
#screens .calib-rung, #screens .calib-tick { stroke-opacity: .5; }
#screens .calib-spin { transform-box: view-box; transform-origin: 0 0; transform: translate(var(--cx), var(--cy)); }
/* every seam -- the console edge and the lines between screens -- is one machined line */
#screens .seam-line { fill: none; stroke: var(--hud); stroke-opacity: .6; stroke-width: 1.5; }
#screens .seam-bevel { fill: none; stroke: var(--ice); stroke-opacity: .14; stroke-width: 1; }
#screens .console-ticks line { stroke: var(--hud); stroke-opacity: .35; stroke-width: 1; }
#screens .seam-bolts { opacity: .45; }
#screens.powering .shutter { opacity: 1; animation: scr-shutter .8s var(--d) forwards; }
#screens.powering .calib { animation: scr-calib .8s var(--d) forwards; }
#screens.powering .calib-spin { animation: scr-level .8s var(--d) forwards; }
#screens.powering .seam { stroke-dasharray: 6000; stroke-dashoffset: 6000; animation: scr-trace .8s var(--d) forwards; }
/* dark -> outline traces in -> dim picture with a calibration grid at 30deg -> grid swings
   level -> screen clears */
@keyframes scr-shutter { 0%, 20% { opacity: 1; } 28%, 76% { opacity: .8; } 100% { opacity: 0; } }
@keyframes scr-calib { 0%, 20% { opacity: 0; } 26%, 78% { opacity: 1; } 100% { opacity: 0; } }
@keyframes scr-level {
  0%, 26% { transform: translate(var(--cx), var(--cy)) rotate(30deg); animation-timing-function: cubic-bezier(.12, .9, .25, 1); }
  66%, 100% { transform: translate(var(--cx), var(--cy)) rotate(0deg); }
}
@keyframes scr-trace {
  0% { stroke-dashoffset: 6000; stroke-opacity: .8; }
  26% { stroke-dashoffset: 0; stroke-opacity: .8; }
  100% { stroke-dashoffset: 0; stroke-opacity: 0; }   /* the permanent .seam-line takes over */
}
@media (prefers-reduced-motion: reduce) {
  #screens.powering .shutter, #screens.powering .calib, #screens.powering .calib-spin,
  #screens.powering .seam { animation: none; }
}

/* ============ power-on ============ */
/* Cockpit power-on: each instrument group flickers on in turn (delay set per group by hud.js
   powerOn()). Stepped, not faded -- a display striking up, not a dissolve. `backwards` only, so
   after its run the group's own opacity attribute is back in charge. */
.pw { animation: power-on .3s steps(1, end) var(--d, 0ms) backwards; }
@keyframes power-on {
  0%   { opacity: 0; }
  25%  { opacity: .7; }
  50%  { opacity: .15; }
  75%  { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .pw { animation: none; }
}

/* ============ hub HTML panels: .slew / .pop / .hudmode ============ */
/* Matches the SVG instrument housings it now sits under: the same dark backing, corner
   registration brackets, a rule under the header and a stencilled part number. It is HTML
   rather than SVG because its children are real buttons, so the brackets are drawn as
   background gradients instead of stroked lines. */
.slew {
  position: fixed;
  right: 22px;
  bottom: 26px;
  top: auto;
  left: auto;
  transform: none;
  /* left/width are driven from place() so this lands on the same lane, and at the same
     width, as the SVG instruments above it -- see the two-column block there */
  width: 232px;
  z-index: var(--z-nav);
  background: var(--panel);
  border: none;
  padding: 14px 14px 20px;   /* G_PAD in js/hud.js: same text inset as every other panel */
  color: var(--ice);
  font: 700 11px/1 'B612 Mono', monospace;
  letter-spacing: .12em;
  /* a column, so when place() stretches the panel to the radar's height the button rows take
     the extra */
  display: flex;
  flex-direction: column;
}
/* four corner brackets, 10px arms, via the shared mechanism above -- same geometry as
   corners() draws in the SVG panels */
.slew::before, .pop::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  --cnr-c: var(--hud); --cnr-arm: 10px; --cnr-th: 1px;
}
.slew::after {
  content: 'RX-124 · BNS-NAV-118A';
  position: absolute;
  right: 14px; bottom: 7px;
  font: 400 7px/1 'B612 Mono', monospace;
  letter-spacing: .08em;
  color: var(--ice);
  opacity: .55;
}
/* Matched to the SVG panel headers: same pale ink, same rule, same tick scale under it.
   It was red, which reads as a lock or a warning -- this is a navigation control, and the
   odd colour plus a missing tick rule is what made the box look unlike the rest of the
   group it sits in. */
.slew-head {
  position: relative;
  margin: 0 0 13px;
  padding-bottom: 6px;
  color: var(--hud);
  font-size: 10px;
  letter-spacing: .18em;
  border-bottom: 1px solid rgba(140, 255, 193, .55);
}
.slew-head::after {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 100%;
  height: 4px;
  background: repeating-linear-gradient(to right, var(--hud) 0 1px, transparent 1px 12px);
  opacity: .5;
}
.slew ul { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; grid-auto-rows: 1fr; gap: 6px; flex: 1; }
.slew li { display: flex; }
.slew button {
  display: block;
  width: 100%;
  /* the WCAG 2.5.8 floor, not the real height: at this padding the button's own
     content sets it taller (~31px) than this minimum ever has to act on */
  min-height: 24px;
  padding: 6px 8px;
  font: inherit;
  text-align: left;
  color: var(--hud);
  background: transparent;
  border: 1px solid rgba(140, 255, 193, .4);
  cursor: pointer;
}
.slew button:hover, .slew button:focus-visible { background: var(--hud); color: var(--night); }
@media (max-width: 860px), (max-height: 520px) { .slew { display: none; } }

/* HUD MODE: a second .slew panel, so it inherits the housing, brackets, header rule and
   button style outright -- only its stencil code differs. place() stacks it above SLEW TO
   on the same lane. A pressed toggle stays lit, the way a latched cockpit switch does. */
.hudmode { bottom: auto; }
.hudmode::after { content: 'RX-124 · BNS-HUD-031M'; }
.slew button[aria-pressed="true"] { background: rgba(140, 255, 193, .16); border-color: var(--hud); }

/* Popups in the open sky either side of TARGET ID. Same housing as every panel: the .97
   backing, 10px corner brackets, 14px inset. Positioned by place(). */
.pop {
  position: fixed;
  z-index: var(--z-nav);
  margin: 0;
  background: var(--panel);
  color: var(--ice);
  font: 700 10px/1.45 'B612 Mono', monospace;
  letter-spacing: .1em;
}
.pop[hidden] { display: none; }
/* HTML panels placed in HUD units: hud.js sets --ui on a short laptop screen and scales the
   SVG HUD by the same factor, so these zoom with it (see uiScale() there) */
.slew, .pop { zoom: var(--ui, 1); }
.comms { padding: 14px 14px 12px; }
.pop-head {
  display: flex;
  justify-content: space-between;
  margin: 0 0 8px;
  padding-bottom: 6px;
  color: var(--amber);            /* amber: something wants your attention, not a threat */
  border-bottom: 1px solid rgba(140, 255, 193, .55);
  letter-spacing: .18em;
}
.comms-ch { color: var(--ice); opacity: .55; }
.comms-msg { margin: 0 0 10px; min-height: 2.9em; }
.comms-ack {
  font: inherit;
  padding: 5px 14px;
  min-height: 24px;
  color: var(--hud);
  background: transparent;
  border: 1px solid rgba(140, 255, 193, .4);
  cursor: pointer;
}
.comms-ack:hover, .comms-ack:focus-visible { background: var(--hud); color: var(--night); }
.toast { padding: 9px 14px; color: var(--hud); letter-spacing: .16em; }
@media (prefers-reduced-motion: no-preference) {
  .pop:not([hidden]) { animation: pop-in .18s steps(3, end); }   /* a stepped raster wipe, not a soft fade */
}
@keyframes pop-in { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0); } }

/* DECLUTTER strips the secondary instruments and leaves the flight HUD: reticle, tape,
   SPD/ALT, radar, status line. CSS beats the opacity attributes hud.js sets. On a hostile
   lock it keeps TARGET ID, the reticle readouts and the anchor warning, but drops the UNIT
   DATA and ARMAMENT boxes and the COMMS popup (display, so its ACK leaves the tab order). */
.declutter #hud .panel, .declutter #hud .dmgmap,
.declutter #hud .foot, .declutter #hud .lane,
.declutter #hud .hx-spec, .declutter #hud .hx-arms { opacity: 0; }
.declutter #comms { display: none; }

/* NIGHT VISION tints the scene only -- both layers sit at --z-fx, under the frame and the
   HUD, so the instruments stay crisp. No filter on the sphere: a filter flattens
   preserve-3d and would collapse the panorama. `color` recolours the scene green while
   keeping its luminance; `screen` lifts the shadows and lays the scanlines. */
.nv-tint, .nv-scan { position: fixed; inset: 0; z-index: var(--z-fx); pointer-events: none; display: none; }
.nv-tint { background: rgb(70, 255, 130); mix-blend-mode: color; }
.nv-scan {
  mix-blend-mode: screen;
  background: repeating-linear-gradient(to bottom, rgba(60, 190, 100, .22) 0 2px, rgba(60, 190, 100, .07) 2px 4px);
}
.nv .nv-tint, .nv .nv-scan { display: block; }

/* ============ sub-page screen & panels ============ */
/* No height or overflow of its own: body.page is exactly as tall as its content, and the
   window is its scroller (see the base section's note on html's overflow-x). */
/* Out through the canopy: each sub-page looks at the cockpit's own panorama at the bearing its
   target sits on (--brg on <body>, in degrees: pilot 308, missions 0, hangar 52; pano.svg is 26.667
   units a degree). Shown 170vh tall, so 0.085vh per unit, horizon (y 1150) at 60vh; the box runs 4vw
   past each side so the drift never shows an edge. Dimmed and vignetted: the panels stay the read. */
body.page::before {
  content: ""; position: fixed; inset: 0 -4vw; z-index: 0; pointer-events: none;
  background:
    radial-gradient(ellipse 80% 70% at 50% 58%, rgba(6, 10, 18, .2), rgba(6, 10, 18, .78)),
    linear-gradient(rgba(6, 10, 18, .6), rgba(6, 10, 18, 0) 38%, rgba(6, 10, 18, 0) 70%, rgba(6, 10, 18, .7)),
    url(../img/pano.svg) calc(54vw - var(--brg, 0) * 26.6667 * .085vh) -37.75vh / auto 170vh repeat-x;
}
/* a slow drift, only where there's a mouse: on a phone it would keep the compositor awake for 1px/s */
@media (hover: hover) { body.page::before { animation: pano-drift 70s ease-in-out infinite alternate; } }
@keyframes pano-drift { from { transform: translateX(-2.5vw); } to { transform: translateX(2.5vw); } }
@media (prefers-reduced-motion: reduce) { body.page::before { animation: none; } }

.strip {
  /* above .screen (z 6), or the page scrolls up over the nav */
  position: fixed; inset: 0 0 auto 0; z-index: var(--z-nav);
  display: flex; flex-wrap: wrap; gap: 10px 22px; align-items: center;
  padding: 12px 20px;
  background: linear-gradient(var(--housing-lip), var(--housing) 2px, var(--housing));
  border-bottom: 1px solid var(--edge);
  font: 700 12px/1 'B612 Mono', monospace; letter-spacing: .1em;
}
.strip ul { display: flex; gap: 18px; list-style: none; margin: 0; padding: 0; }
.strip a { color: var(--ice); text-decoration: none; padding: 6px 8px; min-height: 32px; display: inline-flex; align-items: center; }
.strip a:hover { background: var(--hud); color: var(--night); }
.strip a[aria-current="page"] { color: var(--ice); border-bottom: 2px solid var(--amber); }
.strip .ret { color: var(--amber); }

.screen {
  position: relative; z-index: 1;   /* over the panorama (body.page::before, z 0); page chrome is --z-nav */
  max-width: 1080px; margin: 0 auto; padding: 136px 20px 80px;
  color: var(--ice); font: 400 15px/1.6 'B612', system-ui, sans-serif;
}
/* A panel is a painted housing: a lit top lip, a machined hairline edge, four bracket
   corners cut into the face, and a silkscreened part number in the bottom right. */
.panel {
  position: relative;
  background: linear-gradient(var(--housing-lip), var(--housing) 3px, var(--housing));
  padding: 22px 24px 30px;
  border: 1px solid var(--edge);
  margin: 0 0 26px;
}
.panel::before {
  content: ""; position: absolute; inset: 5px; pointer-events: none;
  --cnr-c: var(--hud); --cnr-arm: 13px; --cnr-th: 1px;
  opacity: .55;
}
.panel[data-ref]::after {
  content: attr(data-ref);
  position: absolute; right: 26px; bottom: 7px;
  font: 400 10px/1 'B612 Mono', monospace; letter-spacing: .16em;
  color: var(--stencil); pointer-events: none;
}
/* the subsystem this panel belongs to, stencilled on the top lip */
.sub {
  font: 700 10px/1 'B612 Mono', monospace; letter-spacing: .2em;
  color: var(--hud); margin: 0 0 10px;
}
/* a readout set into the housing: recessed glass, not another housing */
.rows {
  background: var(--glass);
  border: 1px solid var(--edge);
  padding: 14px 16px;
  display: grid; grid-template-columns: max-content 1fr; gap: 8px 22px; margin: 0; font-size: 14px;
}
.bar {
  font: 700 12px/1 'B612 Mono', monospace; letter-spacing: .16em; color: var(--hud);
  padding: 0 0 14px; margin: 0 0 18px; border-bottom: 1px solid var(--edge);
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
}
.bar .unit { color: var(--stencil); letter-spacing: .12em; font-weight: 400; }
.screen h1 { font: 700 clamp(28px, 5vw, 52px)/1.05 'B612 Mono', monospace; letter-spacing: .04em; margin: 0 0 18px; }
.screen h2, .screen h3 {
  font: 700 18px/1.3 'B612 Mono', monospace; letter-spacing: .06em;
  color: var(--ice); margin: 0 0 12px; padding-bottom: 7px;
  border-bottom: 1px solid var(--edge);
}
.screen p { margin: 0 0 14px; max-width: 68ch; }
/* Links are the words themselves (a name, the unit, a count), never a URL or an "open the..."
   label, so they read as text: same ink, a faint underline to find them by (colour alone would not
   be enough to tell them apart), HUD green on hover and keyboard focus. */
.screen a { color: inherit; text-decoration: underline 1px rgba(221, 231, 238, .3); text-underline-offset: 3px; }
.screen a:hover, .screen a:focus-visible { color: var(--hud); text-decoration-color: var(--hud); }
.meta { font: 400 12px/1.5 'B612 Mono', monospace; letter-spacing: .08em; color: var(--hud); opacity: .85; }
.rows dt { font: 700 12px/1.5 'B612 Mono', monospace; letter-spacing: .1em; color: var(--hud); }
.rows dd { margin: 0; }
.tag { display: inline-block; border: 1px solid var(--hud); padding: 2px 8px; font: 700 11px/1.4 'B612 Mono', monospace; letter-spacing: .12em; }
.tag.is-caution { border-color: var(--amber); color: var(--amber); }
.tag.is-done { border-color: var(--edge); color: var(--stencil); }
/* short items (skills, roles, interests) as stencilled plates that wrap */
.chips { display: flex; flex-wrap: wrap; gap: 6px; list-style: none; margin: 0; padding: 0; }
.chips li { border: 1px solid var(--edge); background: var(--glass); padding: 3px 9px; font: 700 12px/1.5 'B612 Mono', monospace; letter-spacing: .04em; }
.list, .rows ul:not(.chips) { margin: 0; padding-left: 1.1em; }
.list li + li, .rows ul:not(.chips) li + li { margin-top: 6px; }
.list li::marker, .rows ul:not(.chips) li::marker { color: var(--hud); }
/* the mission log's Active / Archive dividers */
.screen h2.log-head {
  font: 700 12px/1 'B612 Mono', monospace; letter-spacing: .2em; text-transform: uppercase;
  color: var(--hud); margin: 34px 0 14px; padding-bottom: 9px;
}
@media (max-width: 700px) {
  .screen { padding: 124px 14px 60px; }
  .rows { grid-template-columns: 1fr; gap: 2px 0; }
  .rows dd { margin: 0 0 10px; }
}

/* a rail of subsystem readouts: glass cells on a machined grid, hairline dividers made
   by letting the housing edge show through a 1px gap rather than by drawing borders */
.rail {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: 1px; list-style: none; margin: 0 0 26px; padding: 0;
  background: var(--edge); border: 1px solid var(--edge);
}
.rail li { background: var(--glass); padding: 11px 13px; display: grid; gap: 5px; }
.rail b { font: 700 10px/1.2 'B612 Mono', monospace; letter-spacing: .14em; color: var(--hud); }
.rail span { font: 700 15px/1 'B612 Mono', monospace; letter-spacing: .04em; color: var(--ice); }
.rail .is-caution span { color: var(--amber); }
/* two to a row on a phone: an odd one out takes the whole row instead of leaving a blank cell */
@media (max-width: 500px) { .rail li:last-child:nth-child(odd) { grid-column: 1 / -1; } }

/* mission entries slide in as they scroll into view */
.reveal { opacity: 0; transform: translateY(14px); transition: opacity .45s ease-out, transform .45s ease-out; }
.reveal.is-in { opacity: 1; transform: none; }

/* a mission's role line: the role in full ink, its place in a quieter one */
.role { margin: -4px 0 14px; font-size: 14px; color: rgba(221, 231, 238, .78); }
.role b { color: var(--ice); }

/* missions: the flight record. One bar per mission on a shared axis, May 2021 (0) to
   October 2026 (66) in months, set inline as --a (start) and --b (end). */
.flight { margin: 0 0 6px; padding: 16px 18px 10px; border: 1px solid var(--edge); background: var(--glass); }
.flight ol { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.flight li { display: grid; grid-template-columns: 19ch 1fr; gap: 14px; align-items: center; }
.flight li > span { font: 700 11px/1.2 'B612 Mono', monospace; letter-spacing: .06em; white-space: nowrap; color: rgba(221, 231, 238, .8); }
.flight small { font-size: inherit; }
.flight b { position: relative; display: block; height: 14px; background: rgba(221, 231, 238, .04); }
.flight i {
  position: absolute; top: 0; bottom: 0;
  left: calc(var(--a) / 66 * 100%); width: max(6px, calc((var(--b) - var(--a)) / 66 * 100%));
  background: rgba(221, 231, 238, .14); border: 1px solid rgba(221, 231, 238, .4);
}
.flight .is-active > span { color: var(--hud); }
.flight .is-active i { background: rgba(140, 255, 193, .32); border-color: var(--hud); box-shadow: 0 0 8px rgba(140, 255, 193, .35); }
.flight .axis b { background: none; height: 12px; }
.flight em {
  position: absolute; top: 0; left: calc(var(--a) / 66 * 100%); padding-left: 4px;
  border-left: 1px solid var(--edge); font: 400 10px/1.2 'B612 Mono', monospace; font-style: normal;
  letter-spacing: .06em; color: var(--stencil);
}
@media (max-width: 500px) {
  .flight { padding: 12px 12px 8px; }
  .flight li { grid-template-columns: 6ch 1fr; gap: 10px; }
  .flight small { display: none; }
  .flight em { font-size: 9px; padding-left: 2px; letter-spacing: 0; }
  .flight em:nth-child(2n) { font-size: 0; }   /* every other year: the ticks stay, the labels don't collide */
}

/* pilot: short panels pair up side by side, and stack on a phone */
.duo { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr)); gap: 0 26px; }
/* two dl panels side by side: label over text, or a long label crushes the text into a sliver */
.duo .rows { grid-template-columns: 1fr; gap: 2px 0; }
.duo .rows dd { margin: 0 0 12px; }

/* hangar: the owner's own model on a blueprint grid (js/hangar.js draws it) */
.suit-view { position: relative; margin: 0; border: 1px solid var(--edge);
  background:
    repeating-linear-gradient(0deg, rgba(140, 255, 193, .06) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(90deg, rgba(140, 255, 193, .06) 0 1px, transparent 1px 32px),
    radial-gradient(ellipse at 50% 45%, #10203A, var(--glass) 70%); }
#suit { display: block; width: 100%; aspect-ratio: 16 / 10; cursor: grab; touch-action: pan-y; }
#suit:active { cursor: grabbing; }
#suit:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }
.suit-hdg { position: absolute; left: 12px; top: 10px; margin: 0; pointer-events: none;
  font: 700 11px/1 'B612 Mono', monospace; letter-spacing: .16em; color: var(--hud); }
.suit-nogl { position: absolute; inset: 0; display: grid; place-items: center; margin: 0; color: var(--stencil); }
.suit-nogl[hidden] { display: none; }
.suit-ctl { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0 24px; }
@media (max-width: 600px) { #suit { aspect-ratio: 1 / 1; } }

.callouts { display: flex; flex-wrap: wrap; gap: 8px; margin: 18px 0 0; padding: 0; list-style: none; }
.callouts button {
  font: 700 11px/1 'B612 Mono', monospace; letter-spacing: .1em;
  background: transparent; color: var(--hud); border: 1px solid rgba(140, 255, 193, .5);
  padding: 9px 12px; min-height: 36px; cursor: pointer;
}
.callouts button:hover, .callouts button:focus-visible { background: var(--amber); color: var(--night); border-color: var(--amber); }

/* hangar history: one disclosure per account, a HUD row that opens on click */
details.log { border-top: 1px solid var(--edge); }
details.log:last-child { border-bottom: 1px solid var(--edge); }
details.log summary {
  cursor: pointer; list-style: none; padding: 12px 0; display: flex; gap: 12px; align-items: center;
  font: 700 12px/1.3 'B612 Mono', monospace; letter-spacing: .12em; text-transform: uppercase; color: var(--hud);
}
details.log summary::-webkit-details-marker { display: none; }
details.log summary::before { content: ""; width: 0; height: 0; border-style: solid; border-width: 4px 0 4px 6px; border-color: transparent transparent transparent currentColor; transition: transform .15s; }
details.log[open] summary::before { transform: rotate(90deg); }
details.log summary:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
details.log > p { margin: 0 0 14px; }
@media (prefers-reduced-motion: reduce) { details.log summary::before { transition: none; } }

/* entrance: a scanline wipe down the screen */
@keyframes screen-wipe { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0 0 0 0); } }
body.page .screen { animation: screen-wipe .4s ease-out both; }
/* arriving through the shutters (js/link.js), the shutters are the entrance */
html.linked body.page .screen { animation: none; }
@media (prefers-reduced-motion: reduce) {
  body.page .screen { animation: none; }
  .reveal { opacity: 1; transform: none; transition: none; }
}

/* ---------- hover magnifies text on the content pages ---------- */
/* Scaling type reflows and blurs it, so step the font-size instead and let the
   panel grow with it. transform-based zoom would smear the glyph edges. */
.screen .panel { transition: font-size .18s ease-out, border-color .18s ease-out, background-color .18s ease-out; }
.screen .panel:hover, .screen .panel:focus-within {
  font-size: 1.12em;
  border-color: rgba(140, 255, 193, .6);
  background: var(--panel);
}
.screen .rows dd, .screen p, .screen li { transition: font-size .18s ease-out; }
.screen a { transition: font-size .15s ease-out, background-color .15s ease-out; display: inline-block; }
.screen a:hover, .screen a:focus-visible { font-size: 1.18em; }
.strip a { transition: font-size .15s ease-out; }
.strip a:hover, .strip a:focus-visible { font-size: 1.15em; }
.callouts button { transition: font-size .15s ease-out; }
.callouts button:hover, .callouts button:focus-visible { font-size: 1.2em; }
@media (prefers-reduced-motion: reduce) {
  .screen .panel, .screen a, .strip a, .callouts button,
  .screen .rows dd, .screen p, .screen li { transition: none; }
}

/* ============ sub-page HUD (js/pagehud.js): instruments for reading, not flying ============ */
.screen .panel { scroll-margin-top: 96px; }
.phud-ladder, .phud-scope { display: none; }
/* The side instruments live in the gutters beside the screen. From 1200px wide the screen gives up
   width (never below 180px a side) so they always fit -- 1366 laptops included -- and they sit
   24px off its edge, or 20px off the window's on a narrow one. */
@media (min-width: 1200px) {
  :root { --screen-w: min(1080px, 100vw - 360px); }
  .screen { max-width: var(--screen-w); }
  .phud-ladder, .phud-scope { display: block; position: fixed; top: 132px; z-index: var(--z-nav); width: 140px; }
  .phud-ladder { left: max(20px, calc(50vw - var(--screen-w) / 2 - 164px)); }
  .phud-scope { right: max(20px, calc(50vw - var(--screen-w) / 2 - 164px)); }
}
.phud-cap {
  margin: 0 0 10px; padding-bottom: 7px; border-bottom: 1px solid rgba(140, 255, 193, .4);
  font: 700 10px/1 'B612 Mono', monospace; letter-spacing: .22em; color: var(--hud);
}
/* sector ladder: a lit track down the left, one rung per panel */
.phud-track { position: absolute; left: 0; top: 30px; bottom: 4px; width: 1px; background: rgba(140, 255, 193, .18); }
.phud-fill { position: absolute; inset: 0; background: var(--hud); transform-origin: top; transform: scaleY(0); box-shadow: 0 0 6px rgba(140, 255, 193, .6); }
.phud-ladder ol { list-style: none; margin: 0; padding: 0 0 0 14px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 1px; }
.phud-ladder a {
  position: relative; display: flex; gap: 8px; align-items: baseline; padding: 4px 0; min-height: 24px;
  text-decoration: none; color: var(--stencil); text-shadow: 0 0 6px var(--night);
  font: 700 10px/1.3 'B612 Mono', monospace; letter-spacing: .08em; text-transform: uppercase;
}
.phud-ladder a i { font-style: normal; color: rgba(140, 255, 193, .45); }
.phud-ladder a span { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.phud-ladder a:hover, .phud-ladder a:focus-visible { color: var(--ice); }
.phud-ladder a.is-on, .phud-ladder a.is-on i { color: var(--hud); }
.phud-ladder a.is-on::before {
  content: ""; position: absolute; left: -14px; top: 50%; margin-top: -4px;
  border: 4px solid transparent; border-left: 6px solid var(--hud); border-right: 0;
}
/* contact scope: the cockpit's three contacts at their real bearings, this page locked */
.phud-dial {
  position: relative; width: 140px; height: 140px; margin: 0 0 14px; border-radius: 50%;
  border: 1px solid rgba(140, 255, 193, .5);
  background:
    radial-gradient(circle, transparent 0 32%, rgba(140, 255, 193, .2) 32.5% 33.2%, transparent 33.7% 65%, rgba(140, 255, 193, .2) 65.5% 66.2%, transparent 66.7%),
    linear-gradient(rgba(140, 255, 193, .14), rgba(140, 255, 193, .14)) center / 1px 100% no-repeat,
    linear-gradient(rgba(140, 255, 193, .14), rgba(140, 255, 193, .14)) center / 100% 1px no-repeat,
    rgba(6, 10, 18, .72);
}
.phud-sweep {
  position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(transparent 0 290deg, rgba(140, 255, 193, .3) 360deg);
  animation: phud-sweep 6s linear infinite;
}
@keyframes phud-sweep { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .phud-sweep { animation: none; } }
.phud-blip {
  position: absolute; width: 8px; height: 8px; margin: -4px 0 0 -4px;
  border: 1.5px solid var(--hud); background: var(--night); text-decoration: none;
}
.phud-blip span { position: absolute; left: 11px; top: -2px; font: 700 9px/1 'B612 Mono', monospace; letter-spacing: .1em; color: var(--hud); }
a.phud-blip::after { content: ""; position: absolute; inset: -9px; }   /* a 26px target for an 8px mark */
a.phud-blip:hover, a.phud-blip:focus-visible { background: var(--hud); outline: none; }
a.phud-blip:focus-visible span { color: var(--amber); }
/* lock brackets: this page amber, a 1-4 lock red (the cockpit's lock-on colour) */
.phud-blip.is-me { --bc: var(--amber); }
.phud-blip.is-lock { --bc: var(--lock); opacity: 1; }
.phud-blip.is-me, .phud-blip.is-lock { border-color: var(--bc); background: var(--bc); }
.phud-blip.is-me span, .phud-blip.is-lock span { color: var(--bc); }
.phud-blip.is-me::before, .phud-blip.is-lock::before {
  content: ""; position: absolute; inset: -7px;
  --cnr-c: var(--bc); --cnr-arm: 6px; --cnr-th: 1.5px;
}
.phud-blip.is-unk { border-color: var(--lock); opacity: .55; }
.phud-blip.is-unk span { color: var(--lock); }
.phud-tel { display: grid; grid-template-columns: max-content 1fr; gap: 5px 12px; margin: 0; font: 700 10px/1.3 'B612 Mono', monospace; letter-spacing: .1em; }
.phud-tel dt { color: var(--hud); }
.phud-tel dd { margin: 0; color: var(--ice); text-align: right; font-variant-numeric: tabular-nums; }
/* along the foot, at every size: where you are and how far through */
.phud-status {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-nav); margin: 0; padding: 20px 16px 11px;
  pointer-events: none; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font: 700 11px/1 'B612 Mono', monospace; letter-spacing: .18em; color: var(--hud);
  background: linear-gradient(transparent, rgba(6, 10, 18, .92) 55%);
}
.phud-prog {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-nav); height: 2px; pointer-events: none;   /* after the status in the DOM, so on top of it */
  background: var(--hud); box-shadow: 0 0 8px rgba(140, 255, 193, .6); transform-origin: left; transform: scaleX(0);
}
@media (max-width: 480px) { .phud-status { font-size: 10px; letter-spacing: .1em; } }

/* ============ page handover: canopy shutters (js/link.js) ============ */
/* An arriving page is dark from its first paint, until link.js puts up the closed glass. */
html.link-in body::after { content: ""; position: fixed; inset: 0; z-index: calc(var(--z-boot) + 1); background: #03060B; }
#link { position: fixed; inset: 0; z-index: var(--z-link); pointer-events: none; }
#link .link-shut { position: absolute; inset: 0; }
#link .link-blade {
  position: absolute; inset: 0; transform: translateY(-100%);
  background:
    repeating-linear-gradient(0deg, rgba(140, 255, 193, .05) 0 1px, transparent 1px 24px),
    repeating-linear-gradient(90deg, rgba(140, 255, 193, .05) 0 1px, transparent 1px 24px),
    linear-gradient(#0B1322, var(--night));
}
#link svg { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; }
/* the svg's 0-100 viewBox is stretched independently per axis onto the real (non-square)
   viewport (link.js sets preserveAspectRatio="none"); non-scaling-stroke keeps this stroke a
   constant px width despite that anisotropic stretch, instead of smearing into an ellipse-like
   line thickness that drifts with the viewport's own aspect ratio. */
#link polygon { fill: none; stroke: var(--hud); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
#link .link-label {
  position: absolute; left: 0; right: 0; margin: 0; transform: translateY(-50%); text-align: center;
  font: 700 26px/1 'B612 Mono', monospace; letter-spacing: .32em; color: var(--hud); opacity: 0;
}
#link .link-label small { display: block; margin-top: 14px; font-size: 12px; letter-spacing: .24em; color: rgba(140, 255, 193, .6); }
@media (max-width: 480px) { #link .link-label { font-size: 18px; letter-spacing: .24em; } }
/* firing: the lock blinks lock/HUD twice before the shutters start */
.target.is-fired { animation: fire-blink .16s steps(1, end) 1; }
@keyframes fire-blink { 0%, 50% { color: var(--lock); } 25%, 75% { color: var(--hud); } }

/* ============ keyboard shortcuts ============ */
/* shown where they act (cockpit.js, pagehud.js, hangar.js, boot.js own the keys).
   Hidden on a touch screen, which has no keys to press. */
kbd {
  display: inline-block; min-width: 1.6em; margin-right: .6em; padding: 2px 4px 1px; text-align: center;
  border: 1px solid currentColor; border-radius: 2px; opacity: .6;
  font: 700 9px/1.1 'B612 Mono', monospace; letter-spacing: .04em; vertical-align: 1px;
}
/* a key before its label (the default) or after it */
#skip kbd, .strip .ret kbd, .phud-cap kbd { margin: 0 0 0 .5em; }
.strip li:has(> kbd) { display: flex; align-items: center; }
.strip li > kbd { margin: 0; }
@media (hover: none) { kbd { display: none; } }
