/* ============================================================================
   BIOS 6.0 — interface system
   ----------------------------------------------------------------------------
   Built on the discipline a souls-like menu uses, because that discipline is the
   answer to the problem this dashboard had: eleven panels all shouting at once.

   Three tiers, and nothing lives outside them.

     TIER 1  THE WORLD    always full-bleed, never cropped by a panel
     TIER 2  THE HUD      always on, corners only, readable in one glance
     TIER 3  THE MENU     summoned, darkens the world without hiding it,
                          five categories, list on the left, detail on the right

   The palette is the one the generated sprite set is lit in — cyan on deep navy —
   so the UI and the canvas read as one system rather than a canvas dropped into a
   page. Cyan is the identity and carries attention: the mission bar, the active
   tab, the launch control, the selection marker. --violet is its counterpoint and
   means energy; --ok / --warn / --bad are semantic and are never spent on
   decoration. Everything structural is a desaturated steel blue, so an accent
   anywhere on the screen means something.

   No border-radius anywhere. The menu discipline is still the souls one - cut,
   not rounded - and the squareness is most of why it reads as engineered rather
   than decorated.
   ========================================================================== */

/* ------------------------------------------------------------------ tokens */

:root {
  /* ground. Only the deepest one is a token, because every other surface on this
     screen is laid over the warehouse and needs its own alpha - so the panel
     fills are written as rgba() literals at their point of use rather than as
     opaque tokens nothing could see through. Their opaque equivalents, for
     reference, are #080f18 / #0c1520 / #131e2b / #1c2a3a. */
  --void:      #050b12;

  /* structure — desaturated steel, so an accent anywhere means something */
  --edge-dim:  #1e3a4d;
  --edge:      #2c5670;
  --rim:       #4d9fc4;
  --rim-lit:   #7fd4f5;
  --accent:    #35c6f4;

  /* text */
  --text:      #c3d4e4;
  --text-hi:   #e8eef6;
  --text-max:  #ffffff;
  --dim:       #8fa3b8;
  --dimmer:    #5c6f85;

  /* meaning — never spent on decoration */
  --bad:       #ff5f57;
  --warn:      #f5b843;
  --ok:        #46d39a;
  --violet:    #b98cf5;   /* energy: the counterpoint to the cyan identity */

  /* type */
  /* Palatino, not Georgia, and for one measurable reason: Georgia sets old-style
     figures, so its zero is x-height (ascent 21 against a cap height of 28) and
     task IDs come out reading "TOO_oo" instead of "TOO_00". A judge squinting at
     an identifier is a self-inflicted wound. Palatino is also the closer match to
     the Garamond a souls menu is set in - Georgia is a screen face, Palatino is a
     book face - so the fix costs nothing in character. `lining-nums` was tried
     first and does nothing here: Georgia ships no lining alternates. */
  --serif: "Palatino Linotype", "Book Antiqua", Palatino, Georgia, "Times New Roman", serif;
  --mono: ui-monospace, "Cascadia Mono", Consolas, "SFMono-Regular", monospace;

  /* rhythm */
  --ease: cubic-bezier(.22,.68,.28,1);
  --t-fast: 140ms;
  --t: 220ms;
  --t-slow: 420ms;

  /* the hairline. It fades at both ends — the menu never closes a box. */
  --rule: linear-gradient(90deg, transparent, var(--edge-dim) 14%, var(--edge) 50%, var(--edge-dim) 86%, transparent);
}

* { box-sizing: border-box; }

/* Half the components here set an explicit display, which silently beats the
   hidden attribute. main.js hides four blocks that way - the pedestrian proof
   pip, the BIOS_4 panel, the training progress - so without this they all stay
   on screen claiming things that are not true of the current run. */
[hidden] { display: none !important; }

/* A focus ring, not a filled box. The browser default lands a pale slab on a tab
   the moment it is clicked and leaves it there after the pointer has gone. */
:focus { outline: none; }
:focus-visible { outline: 1px solid var(--rim); outline-offset: 2px; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--void);
  color: var(--text);
  font-family: var(--serif);
  font-size: 15px;
  -webkit-font-smoothing: antialiased;
}

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: 0; }
::selection { background: var(--edge); color: var(--text-max); }

/* Small caps with air between the letters. Every label on this screen is set
   this way and every value is not — that contrast alone carries the hierarchy,
   so the labels never need to be loud. */
.eyebrow, .label {
  font-family: var(--serif);
  font-size: 10px;
  letter-spacing: .34em;
  text-transform: uppercase;
  color: var(--dim);
  font-weight: 400;
}

.num { font-family: var(--mono); font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.rule { height: 1px; background: var(--rule); border: 0; margin: 0; }
.muted { color: var(--dimmer); font-size: 12px; font-style: italic; margin: 0; }
.good { color: var(--ok); }
.bad { color: #ff5f57; }

/* ------------------------------------------------------------ TIER 1 world */

#world {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: var(--void);
}
#world canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
#world canvas.is-hidden { display: none; }

/* The diagnostic view is drawn from the sprite atlas, and the shell's palette is
   taken from that same atlas - so it needs no correction to sit in the same
   product. It was knocked back while the shell was warm; that is no longer true. */

/* Two vignettes: a wide soft one that sinks the corners so HUD text always has
   ground under it, and a top/bottom scrim so the corner clusters never fight a
   bright patch of warehouse floor. */
.vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 120% 90% at 50% 45%, transparent 38%, rgba(5,11,18,.55) 82%, rgba(5,11,18,.9) 100%),
    linear-gradient(180deg, rgba(5,11,18,.62) 0%, transparent 22%, transparent 70%, rgba(5,11,18,.74) 100%);
  transition: opacity var(--t) var(--ease);
}

/* ------------------------------------------------------------- TIER 2 HUD */

#hud {
  position: fixed;
  inset: 0;
  z-index: 10;
  pointer-events: none;
  transition: opacity var(--t) var(--ease);
}
/* The children opt back in through :where(), which contributes NO specificity -
   and that is the whole point. Written the obvious way, `#hud > *` carries ID
   weight that no class-level rule can outrank, so every "step aside" rule below
   lost to it silently: `opacity: 0` applied, `pointer-events: none` did not, and
   the cluster went invisible while still swallowing the clicks meant for the
   menu underneath it. Invisible and clickable is the worst of both. Keep the
   :where() - the bug it fixes leaves no trace on screen. */
:where(#hud) > * { pointer-events: auto; }

/* Switching the parent off is not enough on its own: a child that says `auto`
   is hit-testable inside a `none` parent, so the states that hide the whole HUD
   have to name the children too. */
body.hud-hidden #hud { opacity: 0; }
body.hud-hidden #hud, body.hud-hidden #hud > * { pointer-events: none; }
body.hud-hidden .vignette { opacity: .3; }

/* Stage one keeps the HUD up. In the reference the health bars and the compass
   stay visible while the rails are open - you are still in the world, so the
   readouts that describe it should still be there. Only the clusters the rails
   would physically land on top of step aside.

   A sheet is different: that is a thing you stopped to read, so the HUD goes. */
body.menu-open .messages,
body.menu-open .perspective,
body.menu-open .link-state,
body.menu-open .pip,
body.menu-open .menu-prompt { opacity: 0; pointer-events: none; }
body.sheet-open #hud { opacity: 0; }
body.sheet-open #hud, body.sheet-open #hud > * { pointer-events: none; }

/* --- vitals, top left. The HP/FP/Stamina stack, in the same order of
       importance: what the fleet achieved, what it has left, how safe it was. */

.vitals {
  position: absolute;
  top: 22px;
  left: 26px;
  width: 268px;
  display: grid;
  gap: 9px;
}
.vital { display: grid; gap: 4px; }
.vital-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.vital-head b {
  font-family: var(--serif);
  font-size: 9.5px;
  letter-spacing: .3em;
  text-transform: uppercase;
  font-weight: 400;
  color: var(--dim);
}
.vital-head span {
  font-family: var(--mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* The trough is inset and the frame is a single hairline: a bar that looks
   stamped into the panel rather than drawn on top of it. */
.vital-track {
  position: relative;
  height: 9px;
  background: linear-gradient(180deg, #070d14, #0e1a26);
  border: 1px solid var(--edge-dim);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.9);
  overflow: hidden;
}
.vital-track::after {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(90deg, transparent 0 24px, rgba(0,0,0,.5) 24px 25px);
  pointer-events: none;
}
/* Workload is the headline number of the whole demo, so its bar is the brightest
   thing in the corner - lit gold rather than the dim bronze the frame is drawn
   in, which at nine pixels tall was close to invisible against the trough. */
.vital-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  transition: width var(--t-slow) var(--ease);
  background: linear-gradient(180deg, #c9f0ff, var(--accent) 48%, #12586f);
  box-shadow: 0 0 12px rgba(53,198,244,.45);
}
/* The ghost lags behind the fill so a change registers as motion, not just as a
   different number one frame later. */
.vital-ghost {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: rgba(232,238,246,.16);
  transition: width 1.1s var(--ease) 180ms;
}
.vital.charge .vital-fill { background: linear-gradient(180deg, #dfc6ff, var(--violet) 45%, #4a2a7a); box-shadow: 0 0 10px rgba(185,140,245,.3); }
.vital.margin .vital-fill { background: linear-gradient(180deg, #7ee8bd, var(--ok) 45%, #1a6b4c); box-shadow: 0 0 10px rgba(70,211,154,.28); }
.vital.is-low  .vital-fill { background: linear-gradient(180deg, #ffd07a, var(--warn) 45%, #7a4c08); box-shadow: 0 0 12px rgba(245,184,67,.4); }
.vital.is-crit .vital-fill { background: linear-gradient(180deg, #ff5f57, var(--bad) 45%, #7a1c17); box-shadow: 0 0 14px rgba(255,95,87,.5); }
.vital.is-crit .vital-head span { color: #ff8a83; }

/* --- link state, top right */

.link-state {
  position: absolute;
  top: 22px;
  right: 26px;
  display: grid;
  justify-items: end;
  gap: 6px;
  text-align: right;
  max-width: 320px;
}
.link-mark {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: var(--serif);
  font-size: 13px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--rim-lit);
}
.link-mark em {
  font-style: normal;
  color: var(--accent);
  text-shadow: 0 0 14px rgba(53,198,244,.45);
}
#linkState {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  letter-spacing: .1em;
  color: var(--dim);
}
#linkState b { font-weight: 400; }
.dot {
  width: 6px;
  height: 6px;
  background: var(--dimmer);
  transform: rotate(45deg);
  flex: none;
  transition: background var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
.dot.up   { background: var(--ok);  box-shadow: 0 0 8px rgba(70,211,154,.8); }
.dot.p2p  { background: var(--accent); box-shadow: 0 0 8px rgba(53,198,244,.7); }
.dot.down { background: var(--bad); box-shadow: 0 0 8px rgba(255,95,87,.8); }

.proof-pips { display: flex; gap: 12px; flex-wrap: wrap; justify-content: flex-end; }
.proof-pips span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 9.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--dimmer);
}
.proof-pips i { width: 4px; height: 4px; transform: rotate(45deg); background: var(--edge); }
.proof-pips i.energy { background: var(--ok); }
.proof-pips i.predictive { background: var(--accent); }
.proof-pips i.human { background: var(--warn); }
.proof-pips i.evidence { background: var(--violet); }

/* --- clock and transport, top centre: the compass slot, the one thing you
       glance at without deciding to. */

.transport {
  position: absolute;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  display: grid;
  justify-items: center;
  gap: 7px;
  width: min(440px, 34vw);
}
.transport-row { display: flex; align-items: center; gap: 12px; width: 100%; }
.play-control {
  width: 30px;
  height: 30px;
  flex: none;
  display: grid;
  place-items: center;
  border: 1px solid var(--edge);
  background: linear-gradient(180deg, rgba(28,42,58,.82), rgba(8,15,24,.82));
  color: var(--rim-lit);
  font-size: 12px;
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.play-control:hover { border-color: var(--rim); color: var(--accent); box-shadow: 0 0 14px rgba(77,159,196,.28); }
.clock {
  font-family: var(--mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  white-space: nowrap;
  flex: none;
}
.clock i { font-style: normal; color: var(--dimmer); margin: 0 2px; }
.clock b { color: var(--text-hi); font-weight: 400; }
.play-state-label { display: none; }

input[type=range]#scrub {
  -webkit-appearance: none;
  appearance: none;
  flex: 1 1 auto;
  min-width: 0;
  height: 14px;
  background: none;
}
#scrub::-webkit-slider-runnable-track {
  height: 3px;
  background: linear-gradient(90deg, var(--edge-dim), var(--edge));
  border-top: 1px solid rgba(0,0,0,.8);
}
#scrub::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 9px;
  height: 9px;
  margin-top: -3px;
  background: var(--accent);
  transform: rotate(45deg);
  box-shadow: 0 0 10px rgba(53,198,244,.7);
  border: 0;
}
#scrub::-moz-range-track { height: 3px; background: var(--edge); }
#scrub::-moz-range-thumb { width: 9px; height: 9px; background: var(--accent); border: 0; border-radius: 0; }

.speed-select, .cam-select, .field select, .field input {
  background: rgba(8,15,24,.86);
  border: 1px solid var(--edge-dim);
  color: var(--text);
  font-family: var(--mono);
  font-size: 11px;
  padding: 4px 6px;
  outline: none;
  border-radius: 0;
}
.speed-select:focus, .cam-select:focus, .field select:focus, .field input:focus { border-color: var(--rim); }
.speed-select { flex: none; }

/* --- the message rail, left middle. Souls games put pickups and events here as
       lines that arrive and leave. Nothing accumulates; nothing needs dismissing. */

.messages {
  position: absolute;
  left: 26px;
  top: 47%;
  transform: translateY(-50%);
  width: 320px;
  display: grid;
  gap: 7px;
  align-content: center;
}
.message {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 9px;
  align-items: baseline;
  padding-left: 10px;
  border-left: 1px solid var(--edge-dim);
  animation: msg-in var(--t-slow) var(--ease) both;
}
.message small {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: .1em;
  color: var(--dimmer);
}
.message span { font-size: 12.5px; color: var(--text); line-height: 1.35; }
.message b { font-weight: 400; color: var(--rim-lit); }
.message.kind-AW { border-left-color: var(--accent); }
.message.kind-AW b { color: var(--accent); }
.message.kind-TD { border-left-color: var(--ok); }
.message.kind-TD b { color: #7ee8bd; }
.message.kind-BD b { color: var(--violet); }
/* The rail fades with age rather than scrolling anything away. The ramp stops
   well short of invisible - a line you can see is faint still tells you the
   fleet is busy, and one at 20% over a dark floor just reads as a smudge. */
.message.age-1 { opacity: .84; }
.message.age-2 { opacity: .68; }
.message.age-3 { opacity: .54; }
.message.age-4 { opacity: .42; }
/* Slide only. The obvious entry animation fades from opacity 0, which means a row
   is unreadable until its animation has run - and an animation is not something
   to depend on for legibility: a throttled tab, a paused document timeline or a
   dropped frame all leave the rail blank with the text sitting right there in the
   DOM. Opacity belongs to the .age-N rules alone, so a line is readable the
   instant it exists and the animation only says "this one is new". */
@keyframes msg-in { from { transform: translateX(-14px); } to { transform: none; } }

/* --- perspective cluster, bottom left: the quickslot corner. What camera you
       are in and who you are looking at, always answerable without a menu. */

.perspective {
  position: absolute;
  left: 26px;
  bottom: 26px;
  display: grid;
  gap: 9px;
  width: 268px;
}
.persp-modes { display: flex; gap: 6px; }
.cam-btn {
  flex: 1;
  padding: 6px 4px 5px;
  font-family: var(--serif);
  font-size: 9px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--dim);
  border: 1px solid transparent;
  border-bottom-color: var(--edge-dim);
  background: linear-gradient(180deg, transparent, rgba(8,15,24,.5));
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.cam-btn:hover { color: var(--text); border-bottom-color: var(--rim); }
.cam-btn.active {
  color: var(--accent);
  border-color: var(--edge);
  border-bottom-color: var(--accent);
  background: linear-gradient(180deg, rgba(18,58,80,.42), rgba(8,15,24,.7));
  text-shadow: 0 0 12px rgba(53,198,244,.5);
}
.persp-target { display: flex; align-items: center; gap: 8px; }
.persp-target .cam-select { flex: 1; min-width: 0; }
.cam-btn-icon {
  width: 24px;
  height: 24px;
  flex: none;
  display: grid;
  place-items: center;
  border: 1px solid var(--edge-dim);
  color: var(--rim);
  font-size: 13px;
  line-height: 1;
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.cam-btn-icon:hover { border-color: var(--rim); color: var(--accent); background: rgba(18,58,80,.4); }
.cam-val { font-family: var(--mono); font-size: 10.5px; color: var(--dim); flex: none; min-width: 34px; text-align: center; }

/* --- the mission bar, bottom centre. A boss bar: one long stroke with flared
       ends and the name underneath, and the only element on screen allowed to
       span this much width. */

.mission {
  position: absolute;
  bottom: 30px;
  left: 50%;
  transform: translateX(-50%);
  width: min(720px, 48vw);
  display: grid;
  gap: 9px;
  justify-items: center;
}
.mission-track {
  position: relative;
  width: 100%;
  height: 9px;
  background: linear-gradient(180deg, #070d14, #0d1a26);
  border-top: 1px solid rgba(0,0,0,.9);
  border-bottom: 1px solid var(--edge-dim);
}
/* The flares. Two rotated squares pinned to the ends: the cheapest honest way to
   get an ornamental cap without shipping an image into an offline demo. */
.mission-track::before,
.mission-track::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 9px;
  height: 9px;
  transform: translateY(-50%) rotate(45deg);
  background: linear-gradient(135deg, var(--rim), var(--edge));
  border: 1px solid var(--edge-dim);
}
.mission-track::before { left: -6px; }
.mission-track::after { right: -6px; }
.mission-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: linear-gradient(180deg, #d9f6ff, var(--accent) 42%, #12586f);
  box-shadow: 0 0 20px rgba(53,198,244,.55);
  transition: width var(--t-slow) var(--ease);
}
.mission-name {
  font-family: var(--serif);
  font-size: 13px;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--text-hi);
  text-align: center;
  text-shadow: 0 2px 10px rgba(0,0,0,.9);
}
/* The caption under the boss bar: what the fleet just did, in one sentence. The
   tag is the loud half and the sentence is the quiet half, which is the reverse
   of how the old ticker did it and the reason this one is readable in motion. */
.mission-sub {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 11px;
  max-width: 100%;
  text-align: center;
}
.mission-sub span {
  flex: none;
  font-size: 9px;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--accent);
}
.mission-sub strong {
  font-family: var(--serif);
  font-size: 12.5px;
  font-weight: 400;
  letter-spacing: .02em;
  color: var(--text);
  text-shadow: 0 2px 10px rgba(0,0,0,.95);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- perspective toast. Minecraft tells you where the camera went for a moment
       and then gets out of the way; a camera mode does not need a permanent
       readout when a transient one is clearer. */

.toast {
  position: fixed;
  left: 50%;
  top: 44%;
  transform: translate(-50%, -50%);
  z-index: 60;
  pointer-events: none;
  display: grid;
  justify-items: center;
  gap: 6px;
  opacity: 0;
  transition: opacity var(--t) var(--ease);
}
.toast.show { opacity: 1; }
.toast b {
  font-family: var(--serif);
  font-size: 26px;
  font-weight: 400;
  letter-spacing: .42em;
  text-indent: .42em;
  text-transform: uppercase;
  color: var(--text-hi);
  text-shadow: 0 0 30px rgba(0,0,0,1), 0 0 60px rgba(53,198,244,.25);
}
.toast small { font-size: 10px; letter-spacing: .3em; text-transform: uppercase; color: var(--dim); }

/* --- picture-in-picture viewfinder */

.pip {
  position: absolute;
  right: 26px;
  bottom: 26px;
  width: 300px;
  border: 1px solid var(--edge-dim);
  background: linear-gradient(180deg, rgba(12,21,32,.94), rgba(5,11,18,.96));
  box-shadow: 0 18px 46px rgba(0,0,0,.7);
}
.pip.hidden { display: none; }
.pip-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 7px 9px;
  border-bottom: 1px solid var(--edge-dim);
}
.pip-title { display: flex; align-items: center; gap: 7px; font-size: 10px; letter-spacing: .2em; text-transform: uppercase; color: var(--text); }
.pip-rec-dot { width: 5px; height: 5px; background: var(--bad); transform: rotate(45deg); animation: rec 1.6s steps(2, jump-none) infinite; }
@keyframes rec { 50% { opacity: .25; } }
.pip-actions { display: flex; gap: 4px; }
.pip-tool-btn {
  padding: 3px 7px;
  font-size: 9px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim);
  border: 1px solid transparent;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.pip-tool-btn:hover { color: var(--accent); border-color: var(--edge); }
.pip-tool-btn.active { color: var(--accent); border-color: var(--edge); }
.pip-view-wrap { position: relative; line-height: 0; }
#pipCanvas { width: 100%; height: auto; display: block; }
.pip-telemetry { display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1px solid var(--edge-dim); }
.pip-stat { padding: 6px 4px; text-align: center; border-right: 1px solid rgba(30,58,77,.4); }
.pip-stat:last-child { border-right: 0; }
.pip-stat span { display: block; font-size: 8px; letter-spacing: .2em; color: var(--dimmer); text-transform: uppercase; }
.pip-stat strong { display: block; margin-top: 2px; font-family: var(--mono); font-size: 10.5px; font-weight: 400; color: var(--text-hi); }

/* ------------------------------------------------------------ TIER 3 menu */

/* Two stages, and the first one is barely a menu at all.
 *
 * Stage one puts a rail down each edge and leaves the middle of the screen
 * completely alone - no blur, no dim, no scrim across the warehouse. That is the
 * whole point of a pause overlay: you are still looking at the world, and the
 * categories are laid beside it rather than over it. Only the rails carry a
 * gradient, and only wide enough to put ground under their own text.
 *
 * Stage two opens the chosen category as a sheet over about three quarters of
 * the screen, with the rails still visible past its edges - so you can always see
 * where you came from and how to get back.
 */

/* The rails sit BELOW the HUD (z-index 8 against its 10). In the reference the
   health bars stay lit on top of the menu's gradient, and they cannot do that if
   the gradient is painted over them - the vitals just go grey. The sheet is a
   sibling rather than a child for exactly this reason: it needs to be above the
   HUD, and a child cannot escape its parent's stacking context. */
#menu {
  position: fixed;
  inset: 0;
  z-index: 8;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t) var(--ease), visibility 0s linear var(--t);
}
/* The sheet's backdrop, and the click target that dismisses it. */
#menu::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(5,11,18,.55);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t) var(--ease);
}
body.sheet-open #menu::after { opacity: 1; pointer-events: auto; }
body.menu-open #menu {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--t) var(--ease), visibility 0s;
}

/* --------------------------------------------------------------- the rails */

.rail {
  position: absolute;
  top: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  /* Same reasoning as .sheet above: #menu goes `pointer-events: none` when it
     closes, but a child that says `auto` is still hit-testable inside it, so the
     rails kept catching clicks meant for the world after the menu was dismissed. */
  pointer-events: none;
  transition: opacity var(--t) var(--ease);
}
body.menu-open .rail { pointer-events: auto; }
.rail-left {
  left: 0;
  width: 330px;
  padding: 26px 26px 22px;
  justify-content: center;
  gap: 26px;
  background: linear-gradient(90deg, rgba(5,11,18,.94) 0%, rgba(5,11,18,.82) 55%, transparent 100%);
}
.rail-right {
  right: 0;
  width: 244px;
  padding: 26px 22px 22px;
  justify-content: center;
  gap: 22px;
  background: linear-gradient(270deg, rgba(5,11,18,.94) 0%, rgba(5,11,18,.8) 55%, transparent 100%);
}
body.menu-open .rail-left  { animation: rail-in-left var(--t) var(--ease) both; }
body.menu-open .rail-right { animation: rail-in-right var(--t) var(--ease) both; }
@keyframes rail-in-left  { from { transform: translateX(-18px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes rail-in-right { from { transform: translateX(18px);  opacity: 0; } to { transform: none; opacity: 1; } }

.rail-mark { display: grid; gap: 3px; padding-left: 4px; }
.rail-mark b {
  font-family: var(--serif);
  font-size: 16px;
  font-weight: 400;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--rim-lit);
}
.rail-mark b em { font-style: normal; color: var(--accent); text-shadow: 0 0 16px rgba(53,198,244,.5); }
.rail-mark small { font-size: 8.5px; letter-spacing: .24em; text-transform: uppercase; color: var(--dimmer); }

/* The category list. An icon in its own bordered square, the label beside it, and
   a cursor that is a band sliding behind the row and fading out to the right -
   not a block of colour filling it. */
.rail-list { display: grid; gap: 2px; }
.rail-item {
  position: relative;
  display: grid;
  grid-template-columns: 34px 1fr auto;
  align-items: center;
  gap: 14px;
  padding: 7px 12px 7px 6px;
  text-align: left;
  color: var(--dim);
  transition: color var(--t-fast) var(--ease);
}
.rail-item::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(53,198,244,.16), rgba(53,198,244,.05) 55%, transparent);
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease);
}
.rail-item > * { position: relative; }
.rail-item i {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  font-style: normal;
  font-size: 14px;
  border: 1px solid var(--edge-dim);
  background: linear-gradient(145deg, #1c2a3a, #0a121c);
  color: var(--rim);
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.rail-item span {
  font-family: var(--serif);
  font-size: 15px;
  letter-spacing: .06em;
}
.rail-item kbd {
  font-family: var(--mono);
  font-size: 9px;
  color: var(--dimmer);
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease);
}
.rail-item:hover, .rail-item.active { color: var(--text-hi); }
.rail-item:hover kbd, .rail-item.active kbd { opacity: 1; }
.rail-item:hover i { border-color: var(--edge); color: var(--rim-lit); }
.rail-item.active { color: var(--text-max); }
.rail-item.active::before { opacity: 1; }
.rail-item.active i {
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 0 16px rgba(53,198,244,.25), inset 0 0 12px rgba(53,198,244,.12);
}
.rail-item.active span { text-shadow: 0 0 16px rgba(53,198,244,.35); }

/* Dimmed, not hidden. A category with nothing in it yet still says it exists and
   what will be there, which is why the reference greys its entries rather than
   removing them. */
.rail-item.is-empty { color: var(--dimmer); cursor: not-allowed; }
.rail-item.is-empty i { opacity: .4; border-color: rgba(30,58,77,.5); }
.rail-item.is-empty:hover { color: var(--dimmer); }
.rail-item.is-empty:hover i { border-color: rgba(30,58,77,.5); color: var(--rim); }

.rail-prompt { display: flex; gap: 18px; padding-left: 6px; }
.rail-prompt button {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 9.5px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--dim);
  transition: color var(--t-fast) var(--ease);
}
.rail-prompt button:hover { color: var(--accent); }
.rail-prompt kbd {
  font-family: var(--mono);
  font-size: 10px;
  min-width: 22px;
  text-align: center;
  padding: 3px 6px;
  border: 1px solid var(--edge-dim);
  background: #0a121c;
  color: var(--rim);
}

/* ------------------------------------------------------ the right-hand rail */

.quick { display: grid; gap: 9px; }
.quick h4 {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-family: var(--serif);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--dim);
}
.quick h4 i { font-style: normal; font-size: 11px; color: var(--rim); }
.quick-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 5px; }

/* Slots, the way a pouch is slots: fixed squares whose positions you learn. */
.slot {
  position: relative;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 3px;
  padding: 4px;
  border: 1px solid var(--edge-dim);
  background: linear-gradient(145deg, #1c2a3a, #0a121c);
  color: var(--dim);
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.slot:hover { border-color: var(--edge); color: var(--text); background: linear-gradient(145deg, #23384e, #0c1725); }
.slot.active {
  border-color: var(--accent);
  color: var(--text-max);
  box-shadow: 0 0 14px rgba(53,198,244,.2), inset 0 0 12px rgba(53,198,244,.1);
}
.slot b { font-family: var(--mono); font-size: 10.5px; font-weight: 400; letter-spacing: .02em; }
.slot small { font-size: 8px; letter-spacing: .12em; text-transform: uppercase; color: var(--dimmer); }
.slot > i { font-style: normal; font-size: 15px; line-height: 1; color: var(--rim); }
.slot.active > i { color: var(--accent); }
.slot-swatch { width: 7px; height: 7px; transform: rotate(45deg); }
/* A battery read at slot size has room for a bar and nothing else. */
.slot-batt { position: absolute; left: 4px; right: 4px; bottom: 4px; height: 2px; background: rgba(30,58,77,.6); }
.slot-batt i { display: block; height: 100%; background: var(--ok); }
.slot-batt i.low { background: var(--warn); }
.slot-batt i.crit { background: var(--bad); }
.quick-empty { grid-column: 1 / -1; font-size: 11px; font-style: italic; color: var(--dimmer); }

.rail-counter {
  display: grid;
  gap: 3px;
  justify-items: center;
  padding: 9px 12px;
  border: 1px solid var(--edge-dim);
  background: #0a121c;
}
.rail-counter small { font-size: 8.5px; letter-spacing: .24em; text-transform: uppercase; color: var(--dimmer); }
.rail-counter b { font-family: var(--mono); font-size: 17px; font-weight: 400; color: var(--text-hi); }
.rail-counter b.good { color: var(--ok); }
.rail-counter b.bad { color: var(--bad); }

/* ---------------------------------------------------------------- the sheet */

.sheet {
  position: fixed;
  z-index: 45;
  top: 50%;
  left: 50%;
  width: 76vw;
  height: 78vh;
  display: grid;
  grid-template-rows: auto 1fr;
  /* Closed panels must not be clickable, and this must NOT be left to the
     `visibility` transition below. That transition is delayed (`0s linear var(--t)`)
     so the panel stays hit-testable through the fade - and if it fails to land, the
     panel stays hit-testable forever: invisible, `opacity: 0`, and still swallowing
     every click aimed at the rails underneath it. That is exactly the bug where
     opening a category and pressing Esc left the mouse dead and only the arrow keys
     working. `pointer-events` is not transitioned, so it flips with the class and
     cannot get stuck. Drive hit-testing from the state, never from the animation. */
  pointer-events: none;
  border: 1px solid var(--edge);
  background: linear-gradient(180deg, rgba(12,21,32,.97), rgba(5,11,18,.98));
  box-shadow: 0 30px 90px rgba(0,0,0,.72);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -50%) scale(.985);
  transition: opacity var(--t) var(--ease), transform var(--t) var(--ease), visibility 0s linear var(--t);
}
body.sheet-open .sheet {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
  transition: opacity var(--t) var(--ease), transform var(--t) var(--ease), visibility 0s;
}

/* The world dims only once a sheet is open, and only then. Stage one leaves it
   alone entirely, which is the difference the reference is built on. */
body.sheet-open .vignette { opacity: .5; }
body.sheet-open .rail { opacity: .3; }

.sheet-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  padding: 20px 28px 16px;
  border-bottom: 1px solid var(--edge-dim);
}
.sheet-head h2 {
  margin: 4px 0 0;
  font-family: var(--serif);
  font-size: 23px;
  font-weight: 400;
  letter-spacing: .12em;
  color: var(--text-hi);
}
.sheet-close {
  display: flex;
  align-items: center;
  white-space: nowrap;
  gap: 9px;
  font-size: 10px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--dim);
  transition: color var(--t-fast) var(--ease);
}
.sheet-close:hover { color: var(--accent); }
.sheet-close kbd {
  font-family: var(--mono);
  font-size: 9px;
  padding: 3px 7px;
  border: 1px solid var(--edge-dim);
  color: var(--rim);
}

.sheet-body { min-height: 0; padding: 18px 28px 24px; }
.panel { display: none; height: 100%; min-height: 0; }
.panel.active { display: grid; animation: panel-in var(--t) var(--ease) both; }
@keyframes panel-in { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }

/* Every category is the same shape: a list you move through on the left, the
   detail of whatever is selected on the right. Learn it once, use it five times. */
.split {
  grid-template-columns: minmax(300px, 380px) 1fr;
  gap: 30px;
  min-height: 0;
}
.split.wide { grid-template-columns: minmax(340px, 440px) 1fr; }
.col { min-height: 0; display: grid; grid-template-rows: auto 1fr; gap: 12px; }
.col-head { display: grid; gap: 5px; }
.col-head h2 {
  margin: 0;
  font-family: var(--serif);
  font-size: 19px;
  font-weight: 400;
  letter-spacing: .1em;
  color: var(--text-hi);
}
.col-head p { margin: 0; font-size: 12px; line-height: 1.5; color: var(--dim); max-width: 62ch; }
.scroller { min-height: 0; overflow-y: auto; overflow-x: hidden; padding-right: 8px; }

/* A bronze thread, not a scrollbar. */
.scroller::-webkit-scrollbar { width: 3px; }
.scroller::-webkit-scrollbar-track { background: rgba(30,58,77,.18); }
.scroller::-webkit-scrollbar-thumb { background: var(--edge); }
.scroller { scrollbar-width: thin; scrollbar-color: var(--edge) rgba(30,58,77,.18); }

/* The framed detail card. One border style for every right-hand pane. */
.frame {
  border: 1px solid var(--edge-dim);
  background: linear-gradient(180deg, rgba(19,30,43,.62), rgba(5,11,18,.72));
  padding: 20px 22px;
}
.frame + .frame { margin-top: 14px; }
.frame > h3 {
  margin: 0 0 4px;
  font-family: var(--serif);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: .32em;
  text-transform: uppercase;
  color: var(--dim);
}
.frame-head-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.frame-head-row > h3 { margin: 0; }

/* A physical rocker rather than a bare checkbox - it reads as a control you flip,
   not a box you tick, which matches the toggle-heavy panel it sits in. */
.switch-toggle { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; }
.switch-toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  position: relative;
  width: 30px; height: 16px;
  border: 1px solid var(--edge-dim);
  background: linear-gradient(180deg, rgba(5,11,18,.9), rgba(19,30,43,.7));
  box-shadow: inset 0 1px 3px rgba(0,0,0,.5);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.switch-thumb {
  position: absolute;
  top: 1px; left: 1px;
  width: 12px; height: 12px;
  background: var(--dim);
  box-shadow: 0 1px 2px rgba(0,0,0,.6);
  transition: transform var(--t-fast) var(--ease), background var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.switch-toggle input:checked ~ .switch-track { border-color: var(--accent); background: linear-gradient(180deg, rgba(14,48,68,.7), rgba(8,15,24,.9)); }
.switch-toggle input:checked ~ .switch-track .switch-thumb {
  transform: translateX(14px);
  background: var(--accent);
  box-shadow: 0 0 8px rgba(53,198,244,.75), 0 1px 2px rgba(0,0,0,.6);
}
.switch-toggle input:focus-visible ~ .switch-track { border-color: var(--rim); }
.switch-label { font-size: 9.5px; letter-spacing: .2em; text-transform: uppercase; color: var(--dim); }
.switch-toggle input:checked ~ .switch-label { color: var(--accent); }
.switch-toggle.is-disabled { cursor: not-allowed; opacity: .4; }

/* ------------------------------------------------------ 1 · DEPLOYMENT tab */

.scenario-gallery { display: grid; gap: 7px; }
.scenario-card {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 14px;
  align-items: center;
  padding: 13px 16px;
  text-align: left;
  color: var(--text);
  border: 1px solid transparent;
  border-left: 2px solid var(--edge-dim);
  background: linear-gradient(90deg, rgba(19,30,43,.5), rgba(8,15,24,.2));
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.scenario-card:hover { border-left-color: var(--rim); background: linear-gradient(90deg, rgba(24,42,60,.7), rgba(8,15,24,.28)); transform: translateX(3px); }
.scenario-card.active {
  border-color: var(--edge);
  border-left: 2px solid var(--card-accent, var(--accent));
  background: linear-gradient(90deg, rgba(17,48,68,.55), rgba(8,15,24,.3));
}
/* The lozenge that marks the selection. In a souls menu the cursor is a shape,
   not a highlight; the row does not change colour, the marker moves to it. */
.scenario-card.active::after {
  content: "\25C6";
  position: absolute;
  right: 13px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 8px;
  color: var(--accent);
  text-shadow: 0 0 10px rgba(53,198,244,.7);
}
.scenario-index { font-family: var(--mono); font-size: 15px; color: var(--dimmer); }
.scenario-card.active .scenario-index { color: var(--card-accent, var(--accent)); }
.scenario-copy { display: grid; gap: 3px; min-width: 0; padding-right: 20px; }
.scenario-copy b { font-weight: 400; font-size: 15.5px; letter-spacing: .04em; color: var(--text-hi); }
.scenario-copy small { font-size: 9px; letter-spacing: .18em; text-transform: uppercase; color: var(--dimmer); }
.scenario-meta { font-family: var(--mono); font-size: 9.5px; color: var(--dim); }
.scenario-card.active .scenario-meta { color: var(--text); }

.field-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px 16px; margin-top: 14px; }
.field { display: grid; gap: 5px; }
.field[hidden] { display: none; }
.field > span { font-size: 9.5px; letter-spacing: .24em; text-transform: uppercase; color: var(--dim); }
.field select, .field input { width: 100%; padding: 8px 9px; font-size: 12px; }
.field.span-2 { grid-column: 1 / -1; }
.field.span-3 { grid-column: 1 / -1; }
.window-mode { display: flex; align-items: center; gap: 8px; margin-top: 16px; font-size: 11px; color: var(--dim); }
.window-mode input { accent-color: var(--accent); margin: 0; }
.window-hint { margin: 7px 0 0; font-size: 11px; line-height: 1.5; color: var(--dim); }

/* The launch button. This is the one control on the whole screen that gets to
   look ceremonial, because pressing it is the moment the demo actually starts. */
.launch {
  position: relative;
  width: 100%;
  margin-top: 18px;
  padding: 16px 20px;
  display: grid;
  gap: 4px;
  justify-items: center;
  overflow: hidden;
  border: 1px solid var(--edge);
  background: linear-gradient(180deg, rgba(14,48,68,.5), rgba(8,15,24,.94));
  color: var(--text-hi);
  transition: border-color var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
.launch:hover:not(:disabled) { border-color: var(--accent); box-shadow: 0 0 30px rgba(53,198,244,.2), inset 0 0 24px rgba(53,198,244,.09); }
.launch:disabled { opacity: .42; cursor: not-allowed; }
.launch b {
  font-family: var(--serif);
  font-size: 15px;
  font-weight: 400;
  letter-spacing: .3em;
  text-indent: .3em;
  text-transform: uppercase;
  color: var(--accent);
  text-shadow: 0 0 20px rgba(53,198,244,.4);
}
.launch small { font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em; color: var(--dim); }
/* A slow sweep, the way a grace site pulses. It is the only looping animation in
   the menu, so it reads as an invitation instead of as noise. */
.launch-beam {
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 30%, rgba(53,198,244,.09) 50%, transparent 70%);
  transform: translateX(-100%);
  animation: sweep 4.2s var(--ease) infinite;
  pointer-events: none;
}
.launch:disabled .launch-beam { animation: none; opacity: 0; }
@keyframes sweep { 0%, 55% { transform: translateX(-100%); } 100% { transform: translateX(100%); } }

.status { margin: 12px 0 0; font-size: 11.5px; line-height: 1.5; color: var(--dim); font-style: italic; }
.status.ok { color: var(--ok); font-style: normal; }
.status.err { color: #ff5f57; font-style: normal; }
.status.busy { color: var(--accent); font-style: normal; }

/* -------------------------------------------------------- 2 · FLEET tab */

.fleet { display: grid; gap: 5px; }
.robot {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 11px;
  align-items: center;
  padding: 10px 13px;
  text-align: left;
  cursor: pointer;
  border: 1px solid transparent;
  border-left: 2px solid var(--edge-dim);
  background: linear-gradient(90deg, rgba(19,30,43,.45), rgba(8,15,24,.15));
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.robot:hover { background: linear-gradient(90deg, rgba(24,42,60,.68), rgba(8,15,24,.24)); transform: translateX(3px); }
.robot.active-focus { border-color: var(--edge); background: linear-gradient(90deg, rgba(17,48,68,.55), rgba(8,15,24,.28)); }
.robot .swatch { width: 7px; height: 7px; transform: rotate(45deg); flex: none; }
.robot .rid { display: flex; align-items: center; gap: 8px; font-family: var(--mono); font-size: 12.5px; color: var(--text-hi); }
.robot .cam-indicator {
  font-family: var(--serif);
  font-size: 8px;
  letter-spacing: .18em;
  padding: 1px 5px;
  color: var(--accent);
  border: 1px solid var(--edge);
}
.robot .meta { font-size: 10px; color: var(--dimmer); margin-top: 3px; font-family: var(--mono); }
.robot .batt { position: relative; height: 3px; margin-top: 6px; background: rgba(30,58,77,.4); overflow: hidden; }
.robot .batt i { position: absolute; inset: 0 auto 0 0; background: var(--ok); transition: width var(--t) var(--ease); }
.robot .batt i.low { background: var(--warn); }
.robot .batt i.crit { background: var(--bad); }
.robot .right { text-align: right; }
.robot .state {
  font-size: 8.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--dim);
  padding: 2px 6px;
  border: 1px solid var(--edge-dim);
}
.robot .state.charging { color: var(--ok); border-color: rgba(70,211,154,.6); }
.robot .state.blocked, .robot .state.failed { color: #ff5f57; border-color: rgba(255,95,87,.6); }
.robot .state.retreat, .robot .state.yield { color: var(--warn); border-color: rgba(245,184,67,.6); }

.inspector-empty { display: grid; justify-items: center; gap: 7px; padding: 48px 20px; text-align: center; }
.inspector-empty .target-glyph { font-size: 26px; color: var(--edge); }
.inspector-empty strong { font-weight: 400; font-size: 15px; letter-spacing: .16em; text-transform: uppercase; color: var(--text); }
.inspector-empty small { font-size: 11.5px; color: var(--dimmer); max-width: 34ch; line-height: 1.5; }

.inspector-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 14px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--edge-dim);
}
.inspector-head small { display: block; font-size: 9px; letter-spacing: .3em; text-transform: uppercase; color: var(--dimmer); }
.inspector-head strong {
  display: block;
  margin-top: 4px;
  font-family: var(--serif);
  font-size: 25px;
  font-weight: 400;
  letter-spacing: .12em;
  color: var(--text-hi);
}
.inspector-state {
  font-size: 9.5px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--accent);
  padding: 4px 10px;
  border: 1px solid var(--edge);
  white-space: nowrap;
}
/* Elden Ring's stat block: label left in dust, value right in bone, one row per
   line, no boxes. It works here for exactly the reason it works there — these
   are attributes of a thing you selected, and the eye wants a column of values. */
.inspector-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px 30px; margin-top: 6px; }
.inspector-grid > div { display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: 12px; padding: 9px 0; border-bottom: 1px solid rgba(30,58,77,.32); }
.inspector-grid span { font-size: 10px; letter-spacing: .18em; text-transform: uppercase; color: var(--dim); }
.inspector-grid b { font-family: var(--mono); font-size: 13px; font-weight: 400; color: var(--text-hi); text-align: right; }
.inspector-grid .battery-track { grid-column: 1 / -1; height: 3px; background: rgba(30,58,77,.4); margin-top: 3px; }
.inspector-grid .battery-track i { display: block; height: 100%; background: linear-gradient(90deg, var(--edge), var(--rim-lit)); }
.reserve-note {
  margin-top: 16px;
  padding: 12px 14px;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--dim);
  border-left: 2px solid var(--edge);
  background: rgba(19,30,43,.5);
}
.reserve-note b { font-weight: 400; color: var(--rim-lit); }

/* ------------------------------------------------- 3 · COORDINATION tab */

.bios6-mode { display: flex; align-items: center; gap: 12px; padding-bottom: 14px; border-bottom: 1px solid var(--edge-dim); margin-bottom: 14px; }
.collective-pulse {
  width: 9px;
  height: 9px;
  flex: none;
  transform: rotate(45deg);
  background: var(--dimmer);
  transition: background var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
.is-v6 .collective-pulse { background: var(--accent); box-shadow: 0 0 14px rgba(53,198,244,.8); animation: pulse 2.4s var(--ease) infinite; }
@keyframes pulse { 50% { opacity: .35; } }
.bios6-mode b { display: block; font-family: var(--serif); font-size: 14px; font-weight: 400; letter-spacing: .24em; text-transform: uppercase; color: var(--text); }
.is-v6 .bios6-mode b { color: var(--accent); }
.bios6-mode small { display: block; margin-top: 2px; font-size: 10px; letter-spacing: .12em; color: var(--dimmer); }

.collective-metrics { display: grid; gap: 2px; }
.metric { display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: 14px; padding: 10px 0; border-bottom: 1px solid rgba(30,58,77,.3); }
.metric span { font-size: 10.5px; letter-spacing: .18em; text-transform: uppercase; color: var(--dim); }
.metric b { font-family: var(--mono); font-size: 17px; font-weight: 400; color: var(--text-hi); }
.metric b.good { color: var(--ok); }

.thought-heading { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin: 4px 0 10px; }
.thought-heading span { font-size: 9.5px; letter-spacing: .3em; text-transform: uppercase; color: var(--dim); }
.thought-heading small { font-size: 9.5px; font-style: italic; color: var(--dimmer); }
.thought-stream { display: grid; gap: 8px; }
/* Decision rows are the item-description panel: a heading, a sentence of plain
   prose, and a smaller line of exact numbers underneath. */
.thought-row { padding: 11px 14px; border-left: 2px solid var(--edge); background: rgba(19,30,43,.5); }
.thought-row header { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.thought-row header b { font-family: var(--mono); font-size: 11px; font-weight: 400; letter-spacing: .06em; color: var(--rim-lit); }
.thought-row time { font-family: var(--mono); font-size: 9.5px; color: var(--dimmer); }
.thought-row p { margin: 6px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--text); }
.thought-row small { display: block; margin-top: 5px; font-family: var(--mono); font-size: 10px; color: var(--dim); }

.auction-summary { display: grid; gap: 6px; padding-bottom: 13px; margin-bottom: 13px; border-bottom: 1px solid var(--edge-dim); font-size: 11.5px; color: var(--dim); }
.auction-proof { font-size: 9.5px; letter-spacing: .24em; text-transform: uppercase; color: var(--accent); }
.auction-log { display: grid; gap: 1px; }
/* Two rows, always. The single-line version fits until an award arrives - awards
   carry a winner, a cost and a lease, and cramming that into the same line makes
   the identifier column wrap mid-name on every one of them. Putting the detail on
   its own line costs a few pixels of height and never wraps. */
.auction-row {
  display: grid;
  grid-template-columns: 44px 26px 1fr;
  gap: 2px 10px;
  align-items: baseline;
  padding: 7px 10px;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--dim);
  border-left: 2px solid var(--edge-dim);
  background: rgba(19,30,43,.32);
}
.auction-row b { font-weight: 400; color: var(--text); }
.auction-row .auction-time { color: var(--dimmer); }
.auction-row > span:not(.auction-time) { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.auction-row small { grid-column: 3; color: var(--dimmer); font-size: 9.5px; }
.auction-row.type-TN { border-left-color: var(--edge); }
.auction-row.type-BD { border-left-color: var(--violet); }
.auction-row.type-BD b { color: var(--violet); }
.auction-row.type-AW { border-left-color: var(--accent); }
.auction-row.type-AW b { color: var(--accent); }
.auction-row.type-TD { border-left-color: var(--ok); }
.auction-row.type-TD b { color: #7ee8bd; }

/* ---------------------------------------------------- 4 · EVIDENCE tab */

.summary-live { display: grid; gap: 3px; padding-bottom: 14px; margin-bottom: 14px; border-bottom: 1px solid var(--edge-dim); }
.summary-live span { font-size: 9.5px; letter-spacing: .28em; text-transform: uppercase; color: var(--dim); }
.summary-live strong { font-family: var(--mono); font-size: 30px; font-weight: 400; color: var(--text-hi); }
.summary-live small { font-family: var(--mono); font-size: 10.5px; color: var(--dimmer); }

.outcome-banner { display: grid; grid-template-columns: auto 1fr; gap: 16px; align-items: center; padding: 14px 16px; margin-bottom: 18px; border-left: 2px solid var(--edge); background: rgba(19,30,43,.55); }
.outcome-banner > span { font-size: 9.5px; letter-spacing: .26em; text-transform: uppercase; color: var(--dim); white-space: nowrap; }
.outcome-banner strong { display: block; font-family: var(--serif); font-size: 16px; font-weight: 400; letter-spacing: .06em; color: var(--text-hi); }
.outcome-banner small { display: block; margin-top: 3px; font-size: 11.5px; color: var(--dim); line-height: 1.5; }
.outcome-banner.complete { border-left-color: var(--ok); }
.outcome-banner.complete > span { color: var(--ok); }
.outcome-banner.window { border-left-color: var(--warn); }
.outcome-banner.window > span { color: var(--warn); }

/* No column gap. dt and dt each carry their own bottom rule, so a gap between
   them cuts every rule in half and the table reads as a ladder with a seam down
   the middle of it. */
.summary dl { display: grid; grid-template-columns: 1fr auto; gap: 0; margin: 0; }
.summary dt { padding: 10px 20px 10px 0; font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--dim); border-bottom: 1px solid rgba(30,58,77,.3); }
.summary dd { margin: 0; padding: 10px 0; font-family: var(--mono); font-size: 13px; color: var(--text-hi); text-align: right; border-bottom: 1px solid rgba(30,58,77,.3); }
/* renderSummary marks contact counts good or bad; without this the more specific
   `.summary dd` colour wins and every safety number renders the same neutral
   cream, which is the one place on the screen where the colour is the message. */
.summary dd.good, .metric b.good { color: var(--ok); }
.summary dd.bad { color: #ff8a83; }
.run-diagnostics { margin-top: 18px; border-top: 1px solid var(--edge-dim); padding-top: 6px; }
.run-diagnostics > summary { cursor: pointer; padding: 10px 0; font-size: 9.5px; letter-spacing: .28em; text-transform: uppercase; color: var(--dim); list-style: none; }
.run-diagnostics > summary::-webkit-details-marker { display: none; }
.run-diagnostics > summary:hover { color: var(--accent); }
.run-diagnostics[open] > summary { color: var(--rim-lit); }
.evidence-scope { margin: 18px 0 0; padding-top: 14px; border-top: 1px solid rgba(30,58,77,.3); font-size: 10.5px; font-style: italic; line-height: 1.6; color: var(--dimmer); }

/* ------------------------------------------------------ 5 · SYSTEM tab */

.sys-group { display: grid; gap: 9px; }
.sys-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 11px 0; border-bottom: 1px solid rgba(30,58,77,.3); }
.sys-row > div { display: grid; gap: 2px; min-width: 0; }
.sys-row b { font-weight: 400; font-size: 13px; color: var(--text-hi); letter-spacing: .06em; }
.sys-row small { font-size: 11px; color: var(--dimmer); line-height: 1.45; }
/* `.sys-row > div` also matches this one, and a type-plus-class selector outranks
   a bare class - so without the child combinator here the segmented control
   inherits display:grid and stacks its buttons into a column. */
.sys-row > .seg { display: flex; border: 1px solid var(--edge-dim); flex: none; gap: 0; }
.seg button {
  padding: 6px 14px;
  font-size: 9.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--dim);
  transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.seg button + button { border-left: 1px solid var(--edge-dim); }
.seg button:hover { color: var(--text); }
.seg button.active { color: var(--accent); background: linear-gradient(180deg, rgba(18,58,80,.6), rgba(8,15,24,.4)); }
.ghost-btn {
  padding: 7px 16px;
  flex: none;
  font-size: 9.5px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--text);
  border: 1px solid var(--edge-dim);
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.ghost-btn:hover { border-color: var(--rim); color: var(--accent); background: rgba(18,58,80,.35); }
.ghost-btn.active { border-color: var(--accent); color: var(--accent); }

.keys { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px 26px; }
.keys div { display: grid; grid-template-columns: auto 1fr; gap: 12px; align-items: baseline; padding: 7px 0; border-bottom: 1px solid rgba(30,58,77,.24); }
.keys kbd {
  font-family: var(--mono);
  font-size: 10px;
  min-width: 42px;
  text-align: center;
  padding: 3px 6px;
  color: var(--rim-lit);
  border: 1px solid var(--edge-dim);
  background: rgba(8,15,24,.7);
}
.keys span { font-size: 11.5px; color: var(--dim); }

.bios4 { display: grid; gap: 12px; }
.bios4-row { display: flex; gap: 8px; flex-wrap: wrap; }
.bios4-row button { padding: 8px 14px; font-size: 10px; letter-spacing: .18em; text-transform: uppercase; color: var(--text); border: 1px solid var(--edge-dim); transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease); }
.bios4-row button:hover:not(:disabled) { border-color: var(--rim); color: var(--accent); }
.bios4-row button:disabled { opacity: .4; cursor: not-allowed; }
.bios4-params { gap: 14px; }
.bios4-params label { display: grid; gap: 4px; font-size: 9.5px; letter-spacing: .2em; text-transform: uppercase; color: var(--dim); }
.bios4-params input { width: 90px; padding: 6px 8px; background: rgba(8,15,24,.86); border: 1px solid var(--edge-dim); color: var(--text); font-family: var(--mono); font-size: 11px; }
.bios4-bar { height: 3px; background: rgba(30,58,77,.4); }
.bios4-bar > div { height: 100%; width: 0; background: linear-gradient(90deg, var(--edge), var(--accent)); transition: width var(--t) var(--ease); }
#trainChart { display: block; width: 100%; height: 56px; margin-top: 8px; border: 1px solid rgba(30,58,77,.4); }

/* ------------------------------------------------------------- overlays */

/* The ceremonial layer. A souls game reserves full-screen serif for exactly two
   events — you won, you died — and that scarcity is what gives them weight, so
   this layer fires on run completion and on nothing else. */
.verdict {
  position: fixed;
  inset: 0;
  z-index: 55;
  display: grid;
  place-items: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--t-slow) var(--ease);
  /* Dark enough in the middle that the line has ground under it. The event rail
     and the warehouse both run straight through this band, and a ceremonial
     sentence competing with a robot for the same pixels is not ceremonial. */
  background: radial-gradient(ellipse 62% 30% at 50% 50%, rgba(5,11,18,.94), rgba(5,11,18,.55) 62%, transparent 82%);
}
.verdict.show { opacity: 1; }
.verdict-copy { display: grid; justify-items: center; gap: 13px; max-width: min(880px, 76vw); text-align: center; }
.verdict-copy b {
  font-family: var(--serif);
  font-size: clamp(26px, 3.6vw, 46px);
  font-weight: 400;
  letter-spacing: .22em;
  text-indent: .22em;
  text-transform: uppercase;
  color: var(--accent);
  text-shadow: 0 0 40px rgba(53,198,244,.5), 0 4px 30px rgba(0,0,0,1);
}
.verdict.window .verdict-copy b { color: var(--warn); text-shadow: 0 0 40px rgba(245,184,67,.45), 0 4px 30px rgba(0,0,0,1); }
/* Not uppercased: the detail line carries a unit, and "1.24 m" becomes "1.24 M"
   the moment it is, which is a different unit. */
.verdict-copy small {
  font-size: 13px;
  letter-spacing: .16em;
  color: var(--text);
  text-shadow: 0 2px 14px rgba(0,0,0,1);
}
.verdict-rule { width: min(520px, 56vw); height: 1px; background: var(--rule); }

.jury-overlay {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 30;
  display: grid;
  justify-items: center;
  gap: 14px;
  padding: 34px 40px 42px;
  background: linear-gradient(180deg, transparent, rgba(5,11,18,.92) 45%);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t-slow) var(--ease), visibility 0s linear var(--t-slow);
}
body.jury-mode .jury-overlay { opacity: 1; visibility: visible; transition: opacity var(--t-slow) var(--ease), visibility 0s; }
.jury-copy { display: grid; justify-items: center; gap: 8px; text-align: center; max-width: 900px; }
.jury-copy small { font-size: 9.5px; letter-spacing: .34em; text-transform: uppercase; color: var(--dim); }
.jury-copy strong {
  font-family: var(--serif);
  font-size: clamp(20px, 2.6vw, 33px);
  font-weight: 400;
  letter-spacing: .1em;
  color: var(--text-hi);
  text-shadow: 0 3px 22px rgba(0,0,0,1);
}
.jury-copy p { margin: 0; font-size: 14px; line-height: 1.6; color: var(--text); max-width: 68ch; }
#exitJuryBtn {
  padding: 8px 20px;
  font-size: 9.5px;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--dim);
  border: 1px solid var(--edge-dim);
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
#exitJuryBtn:hover { border-color: var(--rim); color: var(--accent); }

/* In jury mode the operator controls leave and only the narrative stays: the
   mission bar, the vitals, and the sentence being argued. */
body.jury-mode .perspective,
body.jury-mode .transport,
body.jury-mode .messages,
body.jury-mode .pip { opacity: 0; pointer-events: none; }
body.jury-mode .mission { bottom: auto; top: 76px; }

/* -------------------------------------------------------------- prompts */

/* One persistent hint, bottom right, in the place a souls game puts its context
   action. It is the only thing that tells a first-time viewer the menu exists. */
.menu-prompt {
  position: absolute;
  right: 26px;
  bottom: 26px;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 9.5px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--dim);
  transition: color var(--t-fast) var(--ease), opacity var(--t) var(--ease);
}
.menu-prompt:hover { color: var(--accent); }
.menu-prompt kbd {
  font-family: var(--mono);
  font-size: 10px;
  padding: 4px 9px;
  color: var(--rim-lit);
  border: 1px solid var(--edge-dim);
  background: rgba(8,15,24,.7);
}
body.pip-open .menu-prompt { bottom: 250px; }
body.jury-mode .menu-prompt { opacity: 0; pointer-events: none; }


/* ------------------------------------------------------- the scenario builder
 *
 * Merged from feat/scenario-builder, restyled. The branch drew it in the old
 * shell's idiom - rounded corners, its own palette literals, a stylesheet that
 * no longer exists - so what came across is the behaviour, not the surface.
 *
 * It is an overlay rather than a category: painting a grid wants the whole
 * screen, and the builder is a detail inside Deployment (it is how you make a
 * floor to deploy onto), not a sixth thing in the rail.
 */

.build-button {
  display: grid;
  grid-template-columns: 34px 1fr;
  grid-template-rows: auto auto;
  align-items: center;
  gap: 1px 14px;
  width: 100%;
  margin-top: 10px;
  padding: 13px 16px;
  text-align: left;
  border: 1px dashed var(--edge-dim);
  background: rgba(19,30,43,.35);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.build-button:hover { border-color: var(--accent); background: rgba(20,62,86,.45); }
.build-button i {
  grid-row: 1 / 3;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  font-style: normal;
  font-size: 15px;
  color: var(--rim);
  border: 1px solid var(--edge-dim);
}
.build-button:hover i { color: var(--accent); border-color: var(--edge); }
.build-button span { font-family: var(--serif); font-size: 15px; color: var(--text-hi); }
.build-button small { font-size: 10.5px; color: var(--dimmer); }

.builder {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  grid-template-rows: auto 1fr;
  background: linear-gradient(180deg, #0c1520, #050b12);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t) var(--ease), visibility 0s linear var(--t);
}
body.builder-open .builder {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--t) var(--ease), visibility 0s;
}

.builder-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  padding: 20px 30px 16px;
  border-bottom: 1px solid var(--edge-dim);
}
.builder-head h2 {
  margin: 4px 0 0;
  font-family: var(--serif);
  font-size: 23px;
  font-weight: 400;
  letter-spacing: .12em;
  color: var(--text-hi);
}
.builder-head-actions { display: flex; align-items: center; gap: 16px; }
.ghost-btn.primary { border-color: var(--edge); color: var(--accent); }
.ghost-btn.primary:hover { border-color: var(--accent); background: rgba(20,62,86,.5); }
.ghost-btn:disabled { opacity: .4; cursor: not-allowed; border-color: var(--edge-dim); color: var(--dimmer); }

.builder-body {
  display: grid;
  grid-template-columns: 268px 1fr;
  gap: 26px;
  min-height: 0;
  padding: 22px 30px 26px;
}

.builder-pool { display: grid; grid-template-rows: auto auto auto auto 1fr; gap: 14px; align-content: start; min-height: 0; }
.builder-pool h4 {
  margin: 0;
  font-family: var(--serif);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--dim);
}
.pool-list { display: grid; gap: 4px; }
.pool-tile {
  display: grid;
  grid-template-columns: 34px 1fr;
  grid-template-rows: auto auto;
  align-items: center;
  gap: 0 12px;
  padding: 8px 11px;
  text-align: left;
  cursor: grab;
  border: 1px solid transparent;
  border-left: 2px solid var(--edge-dim);
  background: rgba(19,30,43,.45);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.pool-tile:active { cursor: grabbing; }
.pool-tile:hover { background: rgba(32,52,72,.6); }
.pool-tile.active { border-color: var(--edge); border-left-color: var(--accent); background: rgba(20,62,86,.45); }
.pool-tile b { font-family: var(--serif); font-size: 13.5px; font-weight: 400; color: var(--text-hi); }
.pool-tile small { font-size: 9.5px; color: var(--dimmer); }
.pool-swatch { grid-row: 1 / 3; width: 34px; height: 34px; border: 1px solid var(--edge-dim); }
/* The swatches are the tiles the grid actually paints, baked from the same
   sources the 3D twin renders from - so the palette, the canvas and the
   warehouse you end up with cannot drift apart. Flat colours here were a legend
   you had to learn; these are a preview. */
.pool-swatch { background-size: cover; background-position: center; background-color: #0d1a26; }
.pool-swatch.free    { background-image: url('/assets/twin/builder/tile_floor.png'); }
.pool-swatch.rack    { background-image: url('/assets/twin/builder/tile_rack.png'); }
.pool-swatch.station { background-image: url('/assets/twin/builder/tile_station.png'); }
.pool-swatch.dock    { background-image: url('/assets/twin/builder/tile_amr_dock.png'); }
.pool-swatch.amr     { background-image: url('/assets/twin/builder/tile_amr.png'); }
.pool-swatch.human   { background-image: url('/assets/twin/builder/tile_human.png'); }

.builder-hint { margin: 0; font-size: 11.5px; line-height: 1.5; color: var(--dim); }
.builder-tally { display: grid; grid-template-columns: 1fr auto; gap: 0; margin: 0; }
.builder-tally dt { padding: 7px 0; font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--dim); border-bottom: 1px solid rgba(30,58,77,.5); }
.builder-tally dd { margin: 0; padding: 7px 0; font-family: var(--mono); font-size: 13px; color: var(--text-hi); text-align: right; border-bottom: 1px solid rgba(30,58,77,.5); }

.builder-stage {
  display: grid;
  place-items: center;
  min-height: 0;
  overflow: auto;
  padding: 14px;
  border: 1px solid var(--edge-dim);
  background:
    linear-gradient(rgba(30,58,77,.16) 1px, transparent 1px) 0 0 / 28px 28px,
    linear-gradient(90deg, rgba(30,58,77,.16) 1px, transparent 1px) 0 0 / 28px 28px,
    #070f18;
}
#builderGrid { display: block; border: 1px solid var(--edge); cursor: crosshair; }

/* Follows the pointer during a drag so you can see which cell you are about to
   land on before you let go. */
.builder-preview {
  position: fixed;
  pointer-events: none;
  border: 1px solid var(--accent);
  background-color: rgba(53,198,244,.2);
  background-size: cover;
  background-position: center;
  opacity: .9;
  transform: translate(-50%, -50%);
  display: none;
  z-index: 60;
}

/* ------------------------------------------------------------ responsive */

@media (max-width: 1180px) {
  .vitals, .perspective { width: 232px; }
  .messages { width: 260px; }
  .mission { width: min(560px, 56vw); }
  .split, .split.wide { grid-template-columns: minmax(260px, 320px) 1fr; gap: 22px; }
  .rail-left { width: 292px; }
  .rail-right { width: 216px; }
  .sheet { width: 82vw; }
  .sheet-body { padding-left: 22px; padding-right: 22px; }
}

@media (max-width: 900px) {
  .messages { display: none; }
  .transport { width: min(320px, 44vw); }
  .split, .split.wide { grid-template-columns: 1fr; grid-template-rows: minmax(0, .9fr) minmax(0, 1.1fr); }
  /* Below this the two rails would meet in the middle and there would be no
     world left between them, which is the one thing this layout exists to
     protect. The right-hand rail is the one to drop: its slots are shortcuts to
     things the categories also reach. */
  .rail-right { display: none; }
  .rail-left { width: 258px; }
  .rail-mark small { display: none; }
  .rail-item span { font-size: 13.5px; }
  .sheet { width: 92vw; height: 84vh; }
  .field-grid, .inspector-grid, .keys { grid-template-columns: 1fr; }
  .pip { width: 220px; }
}

@media (max-width: 620px) {
  .vitals, .perspective { width: 168px; left: 14px; }
  .vitals { top: 14px; }
  .perspective { bottom: 14px; }
  .link-state { right: 14px; top: 14px; }
  .proof-pips { display: none; }
  .transport { position: absolute; top: auto; bottom: 92px; width: min(300px, 82vw); }
  .mission { width: 82vw; }
  .pip { display: none; }
  .menu-prompt { right: 14px; bottom: 14px; }
}

/* ============================================================== TIER 0 · BOOT
   The power-on self-test. It covers the gap between the page painting and the
   first run answering - about six seconds of POST /api/run, during which the
   old page sat there fully drawn and completely empty.

   It is the one screen here that is allowed to be ceremonial, and the reason is
   that the team is called BIOS: a power-on self-test is not a theme borrowed
   from somewhere, it is the literal thing. Every line in the readout reports a
   real stage with its real elapsed time, so the ceremony is also the honest
   status display it looks like.

   The house rules still hold. No border-radius. The rules above and below the
   mark fade at both ends and never close a box - a frame around the logo would
   be the one place on this screen contradicting every other.  Labels are
   letterspaced small caps in the serif, values are mono.
   ========================================================================== */

.boot {
  position: fixed;
  inset: 0;
  z-index: 90;                      /* over the builder (50) and the toast (55) */
  display: grid;
  place-items: center;
  background: var(--void);
  overflow: hidden;
  cursor: pointer;                  /* the whole screen is the dismiss target */
  transition: opacity 640ms var(--ease);
}

.boot.is-leaving { opacity: 0; pointer-events: none; }
.boot.is-leaving .boot-stack { transform: scale(1.035); filter: blur(3px); }

/* -------------------------------------------------------------- the field */

.boot-field { position: absolute; inset: 0; pointer-events: none; }

/* The lit ground. Two stops rather than one so the falloff has a shoulder - a
   single radial-gradient reads as a spotlight, which is a lighting effect;
   this reads as a room. */
.boot-field::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 78% at 50% 34%, rgba(53,198,244,.13), transparent 62%),
    radial-gradient(90% 60% at 50% 96%, rgba(70,211,154,.07), transparent 70%);
}

/* Scanlines, at the threshold of visibility. Enough to say "this is a display"
   and not enough to be a texture anyone notices. */
.boot-field::after {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(to bottom,
    rgba(255,255,255,.018) 0 1px, transparent 1px 3px);
}

/* The warehouse floor, receding. The same grid the twin lays its racks on -
   which is what this screen is waiting for, so the boot screen is standing on
   the thing it is building. */
.boot-grid {
  position: absolute;
  left: 50%;
  bottom: -12%;
  width: 260%;
  height: 66%;
  transform: translateX(-50%) perspective(340px) rotateX(69deg);
  transform-origin: 50% 100%;
  background-image:
    linear-gradient(to right, rgba(53,198,244,.20) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(53,198,244,.20) 1px, transparent 1px);
  background-size: 58px 58px;
  -webkit-mask-image: linear-gradient(to top, #000 4%, transparent 74%);
          mask-image: linear-gradient(to top, #000 4%, transparent 74%);
  animation: bootGridDrift 5.2s linear infinite;
}

@keyframes bootGridDrift { to { background-position: 0 58px, 0 58px; } }

/* --------------------------------------------------------------- the stack */

.boot-stack {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 30px;
  width: min(620px, 88vw);
  padding: 0 20px;
  transition: transform 640ms var(--ease), filter 640ms var(--ease);
}

/* ---------------------------------------------------------------- the mark */

.boot-mark {
  position: relative;
  display: grid;
  justify-items: center;
  width: 100%;
  padding: 26px 0;
}

/* The two hairlines. They fade at both ends, like every other rule on this
   interface, so the logo is bracketed rather than boxed. */
.boot-mark::before,
.boot-mark::after {
  content: '';
  position: absolute;
  left: 0;
  width: 100%;
  height: 1px;
  background: var(--rule);
  opacity: 0;
  animation: bootRule 900ms var(--ease) 260ms forwards;
}
.boot-mark::before { top: 0; }
.boot-mark::after  { bottom: 0; }

@keyframes bootRule {
  from { opacity: 0; transform: scaleX(.35); }
  to   { opacity: 1; transform: scaleX(1); }
}

.boot-logo-wrap { position: relative; display: block; line-height: 0; }

/* The bloom is its own layer behind the logo rather than a filter on it. An
   animated drop-shadow repaints the whole 556px raster every frame; a scaling
   radial-gradient is a composite. */
.boot-logo-wrap::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 116%;
  height: 132%;
  transform: translate(-50%, -50%);
  background: radial-gradient(closest-side, rgba(53,198,244,.20), transparent 72%);
  animation: bootBloom 4.6s ease-in-out 1.6s infinite;
  pointer-events: none;
}

@keyframes bootBloom {
  0%, 100% { opacity: .55; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 1;   transform: translate(-50%, -50%) scale(1.06); }
}

/* Never drawn above its native 556px - it is a 556px asset, and upscaling it is
   the one thing that would make it look cheap on a projector. The third term is
   a height budget written as a width: 556/376 is the aspect ratio, so this caps
   the logo at 44vh without letterboxing it inside a box that stayed 376px tall.
   Without it the mark alone is taller than a 1280x720 projector has room for
   once the readout and the prompt are under it, and the prompt falls off. */
.boot-logo {
  position: relative;
  display: block;
  width: min(556px, 100%, 44vh * 1.4787);
  height: auto;

  /* Scanned in, left to right. The fleet senses by casting rays, so the logo
     arriving under a sweep is the project's own mechanism used as an entrance.

     The geometry matters and it is easy to get backwards. The mask is 4x the
     width of the logo, so the logo only ever sees a quarter of it: at 100% that
     quarter is the transparent tail (75-100%, against a ramp that finishes by
     50%) and at 0% it is the opaque head (0-25%, against a ramp that starts at
     38%). Both ends therefore sit well clear of the ramp, which is what stops a
     rounding error - or the 100deg tilt, which moves the stops by ~2% at the
     corners - from leaving a visible seam across the logo.

     Do NOT animate past 0%. Outside a no-repeat mask there is no mask at all,
     which composites as fully transparent, so overshooting does not "reveal
     harder" - it hides the logo completely, and it does it in the FINAL state
     where `forwards` then pins it. That was the first version of this. */
  -webkit-mask-image: linear-gradient(100deg, #000 0 38%, rgba(0,0,0,.4) 44%, transparent 50%);
          mask-image: linear-gradient(100deg, #000 0 38%, rgba(0,0,0,.4) 44%, transparent 50%);
  -webkit-mask-size: 400% 100%;
          mask-size: 400% 100%;
  -webkit-mask-position: 100% 0;
          mask-position: 100% 0;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  animation: bootScan 1500ms var(--ease) 180ms forwards,
             bootRise 1100ms var(--ease) 180ms backwards;
}

/* The mask is dropped outright once the scan has played, so the logo's resting
   state does not depend on the arithmetic above being right. boot-screen.js sets
   this on animationend. Belt and braces, because a logo that fails to appear is
   the worst thing this screen could do. */
.boot-logo.is-scanned {
  -webkit-mask-image: none;
          mask-image: none;
}

@keyframes bootScan {
  to { -webkit-mask-position: 0 0; mask-position: 0 0; }
}

@keyframes bootRise {
  from { opacity: 0; transform: scale(1.035); }
  to   { opacity: 1; transform: scale(1); }
}

/* The sweep bar rides the same timing as the mask edge, so the logo looks lit
   into existence by it rather than merely revealed underneath it. */
.boot-sweep {
  position: absolute;
  top: -4%;
  bottom: -4%;
  width: 130px;
  pointer-events: none;
  background: linear-gradient(90deg,
    transparent, rgba(127,212,245,.10) 32%, rgba(200,240,255,.55) 50%,
    rgba(127,212,245,.10) 68%, transparent);
  opacity: 0;
  animation: bootSweep 1500ms var(--ease) 180ms forwards;
}

@keyframes bootSweep {
  0%   { left: -14%; opacity: 0; }
  12%  { opacity: 1; }
  82%  { opacity: 1; }
  100% { left: 102%; opacity: 0; }
}

/* ------------------------------------------------------------- the readout */

.boot-readout {
  width: 100%;
  max-width: 460px;
  opacity: 0;
  animation: bootFadeUp 700ms var(--ease) 900ms forwards;
  transition: box-shadow 220ms var(--ease);
}

/* Input before the world is ready. A flash on the readout, not a shake on the
   screen - the answer to "is this frozen?" is to point at the thing that is
   still working, and the working line is already shimmering. */
.boot-readout.is-nudged { box-shadow: 0 0 0 1px rgba(53,198,244,.45); }

@keyframes bootFadeUp {
  from { opacity: 0; transform: translateY(7px); }
  to   { opacity: 1; transform: translateY(0); }
}

.boot-readout-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 9px;
  border-bottom: 1px solid var(--edge-dim);
  margin-bottom: 11px;
}

.boot-readout-head b {
  font: 400 12.5px/1 var(--serif);
  font-variant: small-caps;
  letter-spacing: .17em;
  color: var(--text-hi);
}

.boot-readout-head span {
  font: 400 10.5px/1 var(--mono);
  letter-spacing: .13em;
  color: var(--dimmer);
}

.boot-stages { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }

.boot-stage {
  display: flex;
  align-items: center;
  gap: 9px;
  font: 400 12px/1 var(--serif);
  font-variant: small-caps;
  letter-spacing: .11em;
  color: var(--dimmer);
  transition: color var(--t) var(--ease);
}

.boot-stage b { font-weight: 400; }

/* Square, because nothing on this interface is round. Outline while pending,
   filled once the stage has actually reported. */
.boot-stage-pip {
  flex: none;
  width: 7px;
  height: 7px;
  border: 1px solid var(--edge);
  transition: background var(--t) var(--ease), border-color var(--t) var(--ease);
}

/* The leader is what makes a readout a readout: it ties a name to a number
   across whatever gap the two happen to leave. */
.boot-stage-leader {
  flex: 1 1 auto;
  height: 1px;
  min-width: 12px;
  background: repeating-linear-gradient(90deg,
    var(--edge-dim) 0 1px, transparent 1px 5px);
  opacity: .75;
}

.boot-stage-value {
  flex: none;
  font: 400 11px/1 var(--mono);
  letter-spacing: .05em;
  color: var(--dimmer);
  font-variant-numeric: tabular-nums;
}

.boot-stage.is-working { color: var(--text); }
.boot-stage.is-working .boot-stage-pip {
  border-color: var(--accent);
  animation: bootPip 1.15s ease-in-out infinite;
}
.boot-stage.is-working .boot-stage-value { color: var(--rim); }

@keyframes bootPip {
  0%, 100% { background: transparent; }
  50%      { background: var(--accent); }
}

.boot-stage.is-done { color: var(--text); }
.boot-stage.is-done .boot-stage-pip { background: var(--ok); border-color: var(--ok); }
.boot-stage.is-done .boot-stage-value { color: var(--dim); }

.boot-stage.is-bad { color: var(--text-hi); }
.boot-stage.is-bad .boot-stage-pip { background: var(--bad); border-color: var(--bad); }
.boot-stage.is-bad .boot-stage-value { color: var(--bad); }

/* The bar tracks how far the boot got, weighted by what each stage costs.
   Unweighted it would sit at 28% through the entire simulation and then jump,
   which is the behaviour that makes people distrust progress bars. */
.boot-progress {
  position: relative;
  height: 1px;
  margin-top: 15px;
  background: var(--edge-dim);
  overflow: hidden;
}

/* --fill is deliberately not defined in this stylesheet - boot-screen.js sets it
   on .boot-progress as each stage reports, exactly as main.js sets --card-accent
   per scenario card. Both carry a fallback here, and both are the expected
   answers to the "used but not defined" token sweep in the interface note. */
.boot-progress i {
  display: block;
  height: 100%;
  width: var(--fill, 0%);
  background: linear-gradient(90deg, var(--ok), var(--accent));
  transition: width 620ms var(--ease);
}

/* -------------------------------------------------------------- the prompt */

.boot-prompt {
  margin: 0;
  min-height: 1em;
  font: 400 11.5px/1 var(--serif);
  font-variant: small-caps;
  letter-spacing: .26em;
  color: var(--dimmer);
  text-align: center;
  transition: color var(--t) var(--ease);
}

.boot.is-armed .boot-prompt {
  color: var(--rim-lit);
  animation: bootPrompt 2.4s ease-in-out infinite;
}

/* Qualified with .boot.is-armed, not left as .boot-prompt.is-bad, because arming
   is exactly when this state is reachable - a failed stage arms the screen - and
   the unqualified selector loses the specificity tie to the armed rule above.
   The symptom is a failure message rendered in the healthy colour. */
.boot-prompt.is-bad,
.boot.is-armed .boot-prompt.is-bad { color: var(--warn); animation: none; opacity: 1; }

@keyframes bootPrompt {
  0%, 100% { opacity: .48; }
  50%      { opacity: 1; }
}

@media (max-width: 620px) {
  .boot-stack { gap: 22px; }
  .boot-readout { max-width: 100%; }
  .boot-prompt { letter-spacing: .18em; }
}

/* 1280x720 is the actual demo target, and a projector is the one screen where
   nobody can scroll to find the prompt. The logo already has a 44vh cap; this
   buys the rest of the room back out of the gaps. */
@media (max-height: 780px) {
  .boot-stack { gap: 20px; }
  .boot-mark { padding: 16px 0; }
  .boot-stages { gap: 5px; }
}

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