/* =============================================================================
   MarsX — mission-control HUD
   Layer model (the renderer should stay inside its band):
     0   #game-container  — full-bleed Phaser mount, fixed inset:0
     1   .fx              — decorative cockpit glass, pointer-events:none
     10  .hud             — overlay chrome, click-through except on controls
     50+ reserved for modals / future overlays

   HUD geometry contract with the renderer (game.js)
   -------------------------------------------------
   All HUD chrome is confined to at most two rectangles of the viewport:

     desktop  →  a full-width top strip (.hud-top) + a right column (.hud-dock)
     ≤820px   →  a full-width top strip (.hud-top) + a bottom sheet (.hud-dock)

   The size of those rectangles is published on :root as --game-safe-top /
   -right / -bottom / -left, in px of viewport edge covered by chrome. The
   renderer reads them with getComputedStyle at boot and on resize, offsets its
   follow-camera so the local ship stays centred in the VISIBLE region, and
   insets its bottom-left sector map. They are registered with @property so the
   computed value is always a resolved <length> (e.g. "94px"), never a raw
   calc()/env() token stream that parseFloat() would choke on.

   If you move HUD chrome, re-measure and update these numbers. A value that is
   too SMALL hides the player's own ship; too large only nudges the camera.
   ============================================================================= */

@property --game-safe-top    { syntax: "<length>"; inherits: true; initial-value: 0px; }
@property --game-safe-right  { syntax: "<length>"; inherits: true; initial-value: 0px; }
@property --game-safe-bottom { syntax: "<length>"; inherits: true; initial-value: 0px; }
@property --game-safe-left   { syntax: "<length>"; inherits: true; initial-value: 0px; }

:root {
  /* --- palette ---------------------------------------------------------- */
  --space-0: #04060c;
  --space-1: #070c16;
  --space-2: #0d1424;

  --ink:       #e9f1ff;
  --ink-dim:   #a2b3cd;
  --ink-faint: #7286a4;

  --rust:      #ff6b2c;
  --rust-soft: #ff9d63;
  --amber:     #ffb648;
  --cyan:      #4fd6ff;
  --cyan-soft: #a6e9ff;
  --teal:      #35e0a1;

  /* --- BNB Chain accent --------------------------------------------------
     #F0B90B is BNB's gold. It is used here as a COLOUR and paired with the
     words "BNB Chain" — never with Binance's logo or wordmark, and never in a
     way that implies Binance operates or endorses MarsX (see .splash-foot and
     the footer, which say so explicitly). Within the UI the gold has one
     consistent meaning: "this touches BNB Chain" — the chain chip, the score
     that becomes SPCXB, the Rewards Vault, the badge, and every on-chain
     action button. Rust stays the game's own identity; cyan stays the system
     /telemetry colour.                                                      */
  --bnb:       #F0B90B;
  --bnb-soft:  #ffd94f;
  --bnb-pale:  #ffe9a3;
  --bnb-deep:  #a87d04;
  --bnb-ink:   #251a00;

  --ok:    #35e0a1;
  --warn:  #ffc23d;
  --err:   #ff5a63;

  --ok-ink:   #93f2ce;
  --warn-ink: #ffdc92;
  --err-ink:  #ffa9ae;

  --edge:        rgba(126,181,236, 0.22);
  --edge-bright: rgba(150,210,255, 0.42);
  --edge-rust:   rgba(255,140, 80, 0.34);
  --edge-gold:   rgba(240,185, 11, 0.34);
  --edge-gold-hi:rgba(255,215, 90, 0.62);

  --glass:        linear-gradient(178deg, rgba(20,33,55,0.62) 0%, rgba(5,9,17,0.74) 100%);
  --glass-strong: linear-gradient(178deg, rgba(22,36,60,0.74) 0%, rgba(4,8,15,0.86) 100%);
  --blur: blur(16px) saturate(150%);

  /* --- type ------------------------------------------------------------- */
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* --- metrics ---------------------------------------------------------- */
  --pad: 16px;
  --rail-w: 348px;
  --focus: #7fe4ff;
  --fade-h: 40px;

  /* --- viewport occluded by HUD chrome (see contract above) --------------
     Measured, not guessed. --game-safe-top = .hud padding-top (16) +
     .hud-top height (66, pinned below); --game-safe-right = padding-right (16)
     + --rail-w (348). Nothing touches the bottom or left edge on desktop.
     The top strip grew from 55 to 66 when the flight deck absorbed the cargo
     hold and the region/navigation cell — see the measurements in .deck.      */
  --game-safe-top:    82px;
  --game-safe-right: 364px;
  --game-safe-bottom:  0px;
  --game-safe-left:    0px;

  --top-h: 66px;              /* .hud-top is height-pinned so the numbers above stay true */

  font-size: 16px;
  color-scheme: dark;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
  background: var(--space-0);
  color: var(--ink);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--cyan);
  text-decoration: none;
  border-bottom: 1px solid rgba(79, 214, 255, 0.35);
}
a:hover { color: var(--cyan-soft); border-bottom-color: var(--cyan-soft); }

code {
  font-family: var(--mono);
  font-size: 0.92em;
  color: var(--cyan-soft);
}

::selection { background: rgba(255, 107, 44, 0.4); color: #fff; }

/* =============================================================================
   0 — the game
   ============================================================================= */

#game-container {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 50% -10%, #101a2e 0%, #070c16 45%, #04060c 100%);
}
/* No !important — the renderer sets inline sizing on the canvas and must win. */
#game-container canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* =============================================================================
   1 — cockpit glass (decoration only)
   ============================================================================= */

.fx {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}
.fx > * { position: absolute; inset: 0; }

/* vignette + edge scrims: keeps HUD text legible over a busy starfield */
.fx-vignette {
  background:
    linear-gradient(180deg, rgba(3,6,12,0.80) 0%, rgba(3,6,12,0.28) 12%, rgba(3,6,12,0) 26%),
    linear-gradient(0deg,   rgba(3,6,12,0.78) 0%, rgba(3,6,12,0.24) 12%, rgba(3,6,12,0) 28%),
    linear-gradient(270deg, rgba(3,6,12,0.62) 0%, rgba(3,6,12,0.14) 20%, rgba(3,6,12,0) 38%),
    radial-gradient(130% 100% at 50% 50%, rgba(0,0,0,0) 42%, rgba(0,0,0,0.55) 100%);
}

/* scanlines */
.fx-scan {
  background-image: repeating-linear-gradient(
    180deg,
    rgba(180, 220, 255, 0.055) 0px,
    rgba(180, 220, 255, 0.055) 1px,
    rgba(0, 0, 0, 0) 1px,
    rgba(0, 0, 0, 0) 3px
  );
  opacity: 0.55;
  animation: scan-drift 9s linear infinite;
}

/* viewport corner brackets */
.fx-frame {
  --c: 30px;
  inset: 10px;
  border: 1px solid rgba(126, 181, 236, 0.30);
  -webkit-mask-image:
    linear-gradient(to right,  #000 var(--c), transparent var(--c), transparent calc(100% - var(--c)), #000 calc(100% - var(--c))),
    linear-gradient(to bottom, #000 var(--c), transparent var(--c), transparent calc(100% - var(--c)), #000 calc(100% - var(--c)));
  mask-image:
    linear-gradient(to right,  #000 var(--c), transparent var(--c), transparent calc(100% - var(--c)), #000 calc(100% - var(--c))),
    linear-gradient(to bottom, #000 var(--c), transparent var(--c), transparent calc(100% - var(--c)), #000 calc(100% - var(--c)));
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

@keyframes scan-drift {
  from { background-position: 0 0; }
  to   { background-position: 0 60px; }
}

/* =============================================================================
   1.5 — world overlays: event banner, containment warning, banking burst, notices
   -----------------------------------------------------------------------------
   Transient, non-interactive, and laid out INSIDE the --game-safe-* band so they
   can never sit under HUD chrome or over a control. Because everything in here is
   pointer-events:none, sitting above .hud in z-order costs no hit-testing: a click
   in open space still lands on the canvas.

   Band ownership, agreed with the renderer: the page owns
   safe.top → safe.top + 96px (event band + containment warning) and
   safe.bottom → safe.bottom + 120px (notices). In-world banners drawn by game.js
   should stay clear of those two strips.
   ============================================================================= */

.overlays {
  position: fixed;
  inset: 0;
  z-index: 11;
  pointer-events: none;
}
.overlays * { pointer-events: none; }

/* --- containment field: screen-edge pressure, one value per side ------------ */
.fence-glow {
  --fence-t: 0;
  --fence-r: 0;
  --fence-b: 0;
  --fence-l: 0;
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(255, 60, 72, calc(0.55 * var(--fence-t))) 0%, rgba(255, 60, 72, 0) 16%),
    linear-gradient(270deg, rgba(255, 60, 72, calc(0.55 * var(--fence-r))) 0%, rgba(255, 60, 72, 0) 14%),
    linear-gradient(0deg,   rgba(255, 60, 72, calc(0.55 * var(--fence-b))) 0%, rgba(255, 60, 72, 0) 16%),
    linear-gradient(90deg,  rgba(255, 60, 72, calc(0.55 * var(--fence-l))) 0%, rgba(255, 60, 72, 0) 14%);
  transition: background 180ms linear;
}

/* --- banner column ---------------------------------------------------------- */
.banners {
  position: absolute;
  top: calc(var(--game-safe-top) + 10px);
  left: calc(var(--game-safe-left) + 14px);
  right: calc(var(--game-safe-right) + 14px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
}

/* Ore surge is a GAME event, so it wears the game's rust/amber — never the BNB gold, which in
   this UI means "this touches the chain". */
.event-band {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  max-width: 100%;
  padding: 8px 14px 10px;
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
  background: linear-gradient(180deg, rgba(52, 26, 10, 0.94), rgba(14, 8, 4, 0.95));
  box-shadow: inset 0 0 0 1px var(--edge-rust), 0 10px 34px rgba(0, 0, 0, 0.55);
  opacity: 0;
  transform: translateY(-10px);
  transition: opacity 300ms ease, transform 300ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
.event-band[data-on="1"] { opacity: 1; transform: none; }
.event-band[data-here="1"] {
  box-shadow:
    inset 0 0 0 1px rgba(255, 157, 99, 0.75),
    0 0 30px rgba(255, 107, 44, 0.35),
    0 10px 34px rgba(0, 0, 0, 0.55);
}
.event-pip {
  width: 9px;
  height: 9px;
  flex: none;
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  background: var(--rust);
  box-shadow: 0 0 12px var(--rust);
  animation: dot-pulse 1.6s ease-in-out infinite;
}
.event-name {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #ffd2b6;
  white-space: nowrap;
}
.event-where {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.event-mult {
  padding: 2px 6px;
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--rust-soft);
  box-shadow: inset 0 0 0 1px var(--edge-rust);
  white-space: nowrap;
}
.event-clock {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: #fff;
  white-space: nowrap;
}
.event-track {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: rgba(255, 107, 44, 0.18);
}
.event-track i {
  display: block;
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, var(--amber), var(--rust));
  box-shadow: 0 0 10px rgba(255, 107, 44, 0.8);
  transition: width 300ms linear;
}

/* --- containment warning ---------------------------------------------------- */
.fence-warn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 6px 12px;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  background: linear-gradient(180deg, rgba(62, 10, 18, 0.94), rgba(20, 4, 8, 0.95));
  box-shadow: inset 0 0 0 1px rgba(255, 90, 99, 0.55), 0 8px 26px rgba(0, 0, 0, 0.5);
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--err-ink);
  animation: reveal 240ms ease both;
}
.fence-ico {
  width: 9px;
  height: 9px;
  flex: none;
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
  background: var(--err);
  box-shadow: 0 0 10px var(--err);
}
.fence-depth { font-variant-numeric: tabular-nums; color: #fff; }
.fence-warn[data-hard="1"] { animation: fence-alarm 0.9s steps(1, end) infinite; }
@keyframes fence-alarm {
  0%, 60%  { box-shadow: inset 0 0 0 1px rgba(255, 90, 99, 0.85), 0 0 26px rgba(255, 60, 72, 0.5); }
  61%,100% { box-shadow: inset 0 0 0 1px rgba(255, 90, 99, 0.35), 0 8px 26px rgba(0, 0, 0, 0.5); }
}

/* --- the banking payoff ------------------------------------------------------ */
.bank-burst {
  position: absolute;
  top: var(--game-safe-top);
  right: var(--game-safe-right);
  bottom: var(--game-safe-bottom);
  left: var(--game-safe-left);
  display: grid;
  place-items: center;
  opacity: 0;
  transition: opacity 380ms ease;
}
.bank-burst[data-on="1"] { opacity: 1; }
.bank-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding: 20px 34px 18px;
  clip-path: polygon(16px 0, 100% 0, 100% calc(100% - 16px), calc(100% - 16px) 100%, 0 100%, 0 16px);
  background: linear-gradient(178deg, rgba(10, 42, 38, 0.92) 0%, rgba(4, 10, 16, 0.95) 100%);
  box-shadow:
    inset 0 0 0 1px rgba(53, 224, 161, 0.5),
    inset 0 -70px 80px -70px rgba(240, 185, 11, 0.8),
    0 24px 70px rgba(0, 0, 0, 0.6);
  transform: scale(0.94);
}
.bank-burst[data-on="1"] .bank-card { animation: bank-pop 520ms cubic-bezier(0.2, 0.9, 0.3, 1) both; }
/* the shock ring */
.bank-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 120px;
  height: 120px;
  margin: -60px 0 0 -60px;
  border-radius: 50%;
  border: 1px solid rgba(53, 224, 161, 0.7);
}
.bank-burst[data-on="1"] .bank-ring { animation: bank-ring 900ms cubic-bezier(0.15, 0.7, 0.3, 1) both; }
.bank-kicker {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--ok-ink);
}
.bank-gain {
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-family: var(--mono);
  font-weight: 700;
  line-height: 1;
}
/* The gain is rendered in GOLD: at this instant the cargo stopped being at-risk rust and
   became banked, redeemable score. That colour change IS the reward. */
.bank-gain em {
  font-style: normal;
  font-size: 40px;
  font-variant-numeric: tabular-nums;
  color: var(--bnb-soft);
  text-shadow: 0 0 30px rgba(240, 185, 11, 0.6);
}
.bank-gain-unit {
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--bnb-pale);
  opacity: 0.75;
}
.bank-facts {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 3px;
  padding-top: 9px;
  border-top: 1px solid rgba(53, 224, 161, 0.22);
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  white-space: nowrap;
}
.bank-facts b { color: var(--ink); font-variant-numeric: tabular-nums; }

@keyframes bank-pop {
  0%   { opacity: 0; transform: scale(0.86); }
  60%  { opacity: 1; transform: scale(1.03); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes bank-ring {
  0%   { opacity: 0.9; transform: scale(0.4); }
  100% { opacity: 0;   transform: scale(2.6); }
}

/* --- notices ----------------------------------------------------------------- */
.notices {
  position: absolute;
  left: calc(var(--game-safe-left) + 14px);
  right: calc(var(--game-safe-right) + 14px);
  bottom: calc(var(--game-safe-bottom) + 16px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.note-toast {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  max-width: min(100%, 460px);
  padding: 8px 13px;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  background: linear-gradient(180deg, rgba(18, 29, 49, 0.95), rgba(4, 8, 15, 0.96));
  box-shadow: inset 0 0 0 1px var(--edge), 0 8px 26px rgba(0, 0, 0, 0.5);
  font-family: var(--mono);
  font-size: 10.5px;
  line-height: 1.45;
  letter-spacing: 0.02em;
  color: var(--ink);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 260ms ease, transform 260ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
.note-toast[data-on="1"] { opacity: 1; transform: none; }
.note-text { overflow-wrap: anywhere; }
.note-count {
  flex: none;
  padding: 1px 5px;
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
  box-shadow: inset 0 0 0 1px rgba(126, 181, 236, 0.25);
}
.note-toast[data-kind="good"]  { box-shadow: inset 0 0 0 1px rgba(53, 224, 161, 0.55), 0 8px 26px rgba(0,0,0,0.5); color: var(--ok-ink); }
.note-toast[data-kind="warn"]  { box-shadow: inset 0 0 0 1px rgba(255, 194, 61, 0.55), 0 8px 26px rgba(0,0,0,0.5); color: var(--warn-ink); }
.note-toast[data-kind="error"] { box-shadow: inset 0 0 0 1px rgba(255, 90, 99, 0.6),  0 8px 26px rgba(0,0,0,0.5); color: var(--err-ink); }
.note-toast[data-kind="info"]  { color: var(--cyan-soft); }

/* =============================================================================
   2 — HUD shell
   ============================================================================= */

.hud {
  position: fixed;
  inset: 0;
  z-index: 10;
  pointer-events: none;
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--rail-w);
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-areas:
    "top  top"
    "void dock";
  column-gap: var(--pad);
  row-gap: 10px;
  padding: var(--pad);
}

/* Height-pinned on purpose: --game-safe-top is a constant the renderer trusts, and
   .status-pill:hover expands to reveal a long failure string. With a fixed height the
   expanded pill overflows (visibly, over the game) instead of growing the row and
   silently invalidating --game-safe-top. */
.hud-top {
  grid-area: top;
  display: flex;
  align-items: stretch;
  align-content: flex-start;
  flex-wrap: nowrap;
  gap: 12px;
  min-width: 0;
  height: var(--top-h);
}
.hud-top > .brand { flex: 0 0 auto; }
/* flex: 0 0 auto on purpose. The deck's numbers are nowrap and the module is clip-path'd, so a
   shrunk deck would CLIP its own readouts rather than reflow them. The status pill is the item
   designed to absorb the shrink (it ellipsises and expands on hover); below 1180px the strip
   wraps to two rows instead. */
.hud-top > .deck  { flex: 0 0 auto; min-width: 0; }

/* Ops column. Holds EVERYTHING that is not the top strip, so the play area keeps a single
   clean rectangle. Flex order here is the desktop order; the ≤820px block re-orders it. */
.hud-dock {
  grid-area: dock;
  min-height: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  pointer-events: none;
}
/* Column, not row: the tab bar sits above the scrollport. --tabs-h is what the drawn scrollbar
   offsets itself by so its track lines up with the scrolling area, not the tabs. */
.rail-shell  {
  order: 1;
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* Measured: 26px tab bar + 9px margin. The drawn scrollbar offsets itself by this so its
     track lines up with the scrollport rather than starting behind the tabs. */
  --tabs-h: 35px;
}
.hud-bottom  { order: 2; flex: 0 0 auto; display: flex; flex-direction: column; }
.foot        { order: 3; flex: 0 0 auto; }

.hud-rail {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow-y: auto;
  overflow-x: hidden;
  padding-right: 12px;   /* clears .rail-bar */
  padding-bottom: 8px;
  /* The UA scrollbar is suppressed on purpose. Measured in Chromium/macOS: a ::-webkit-scrollbar
     thumb paints NOTHING when the platform uses overlay scrollbars, and `scrollbar-width: thin`
     makes it worse (0px overlay, invisible until you already know to scroll). Rather than ship an
     affordance that silently isn't there, the rail draws its own — see .rail-bar. */
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
.hud-rail::-webkit-scrollbar { width: 0; height: 0; }
/* panels must overflow the rail, not squash inside it */
.hud-rail > * { flex: 0 0 auto; }

/* Drawn scroll bar. Base state is invisible; a scroll-driven animation turns it on. An
   INACTIVE scroll timeline (i.e. the rail does not overflow) leaves the base style in place,
   so the bar appears if and only if there is genuinely more content. */
.rail-bar {
  --thumb-h: 26%;
  position: absolute;
  top: calc(var(--tabs-h) + 2px);
  bottom: 2px;
  right: 0;
  width: 6px;
  z-index: 2;
  pointer-events: none;
  border-radius: 6px;
  background: rgba(126, 181, 236, 0.18);
  box-shadow: inset 0 0 0 1px rgba(126, 181, 236, 0.14);
  opacity: 0;
}
.rail-thumb {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: var(--thumb-h);
  border-radius: 5px;
  background: linear-gradient(180deg, rgba(166, 233, 255, 0.95), rgba(79, 214, 255, 0.6));
  box-shadow: 0 0 8px rgba(79, 214, 255, 0.55);
}
@supports (animation-timeline: scroll()) {
  /* the timeline is declared on the scroller and hoisted so its following siblings can see it */
  .rail-shell { timeline-scope: --rail; }
  .hud-rail   { scroll-timeline: --rail block; }
  .rail-bar   { animation: rail-bar-on linear both; animation-timeline: --rail; }
  .rail-thumb { animation: rail-thumb-travel linear both; animation-timeline: --rail; }
}
@keyframes rail-bar-on { from, to { opacity: 1; } }
@keyframes rail-thumb-travel {
  from { top: 0; }
  to   { top: calc(100% - var(--thumb-h)); }
}

/* Permanent bottom fade. Deliberately a sibling overlay and NOT a mask on .hud-rail: a mask
   forms a backdrop root and would kill backdrop-filter on every panel inside it. */
.rail-fade {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--fade-h);
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(4, 7, 14, 0) 0%,
    rgba(4, 7, 14, 0.45) 46%,
    rgba(4, 7, 14, 0.86) 100%);
}
/* a hairline "there is a floor here" cue under the fade */
.rail-fade::after {
  content: "";
  position: absolute;
  left: 2px;
  right: 14px;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(126, 181, 236, 0.35), transparent);
}

/* =============================================================================
   Glass surface — beveled corners + luminous edge
   ============================================================================= */

.mod,
.panel {
  --cut: 14px;
  --len: calc(var(--cut) * 1.4143);
  position: relative;
  pointer-events: auto;
  clip-path: polygon(
    var(--cut) 0, 100% 0,
    100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%,
    0 100%, 0 var(--cut)
  );
  background: var(--glass);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  box-shadow:
    inset 0 0 0 1px var(--edge),
    inset 0 1px 0 rgba(200, 230, 255, 0.10),
    inset 0 -40px 60px -50px rgba(79, 214, 255, 0.65);
}
.mod[data-cut="sm"] { --cut: 10px; }

/* the two bevel edges, drawn as hairlines */
.mod::before, .panel::before,
.mod::after,  .panel::after {
  content: "";
  position: absolute;
  width: var(--len);
  height: 1px;
  background: var(--edge-bright);
  pointer-events: none;
}
.mod::before, .panel::before {
  top: calc(var(--cut) / 2);
  left: calc(var(--cut) / 2 - var(--len) / 2);
  transform: rotate(-45deg);
}
.mod::after, .panel::after {
  bottom: calc(var(--cut) / 2);
  right: calc(var(--cut) / 2 - var(--len) / 2);
  transform: rotate(-45deg);
}

/* =============================================================================
   Top row — identity, telemetry, uplink
   ============================================================================= */

.brand {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 16px 10px 13px;
}
.brand-mark {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 33% 30%, #ffb07a 0%, #e0602a 42%, #8d3312 78%, #571a06 100%);
  box-shadow: 0 0 14px rgba(255, 107, 44, 0.55), inset -3px -3px 6px rgba(0,0,0,0.45);
  flex: none;
}
.brand-text { display: flex; flex-direction: column; gap: 3px; }
.brand-name {
  font-family: var(--mono);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.22em;
  line-height: 1;
  color: var(--ink);
  white-space: nowrap;
}
.brand-name em { font-style: normal; color: var(--rust); text-shadow: 0 0 12px rgba(255,107,44,.6); }
.brand-sub {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ink-faint);
  line-height: 1;
  white-space: nowrap;
}

/* --- BNB Chain chip -------------------------------------------------------
   Wording + gold. The pip is a plain coin-edge ring: deliberately NOT a
   diamond or a four-square arrangement, so it cannot be mistaken for the
   Binance mark.                                                             */
.chain-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  margin-left: 3px;
  padding: 5px 9px 5px 7px;
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
  background: linear-gradient(180deg, rgba(240,185,11,0.16), rgba(240,185,11,0.05));
  box-shadow: inset 0 0 0 1px var(--edge-gold);
  font-family: var(--mono);
  font-size: 8.5px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bnb-pale);
  white-space: nowrap;
}
.chain-pip {
  width: 9px;
  height: 9px;
  flex: none;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #fff0b8 0%, var(--bnb) 52%, var(--bnb-deep) 100%);
  box-shadow: 0 0 9px rgba(240,185,11,0.7), inset 0 0 0 1px rgba(255,240,190,0.55);
}
.chain-chip-lg {
  padding: 7px 12px 7px 9px;
  font-size: 9.5px;
  letter-spacing: 0.2em;
}
.chain-chip-lg .chain-pip { width: 11px; height: 11px; }

/* =============================================================================
   FLIGHT DECK — banked score + credits | cargo hold | region & navigation
   -----------------------------------------------------------------------------
   One glass module, three cells, read left to right in loop order: what you keep,
   what you can lose, where you are.

   Colour is load-bearing and must not drift:
     GOLD  — banked score. Redeemable for SPCXB on BNB Chain. Never spent, never
             decremented (see the invariant in app.js and the server spec).
     CYAN  — credits. The spendable in-game currency, tracked separately.
     AMBER→RUST→RED — unbanked cargo, escalating with how full the hold is.
     TEAL  — banking in progress / safe.
   ============================================================================= */

.deck {
  display: flex;
  align-items: stretch;
  gap: 11px;
  padding: 7px 12px;
  min-width: 0;
}
.deck-cell {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  min-width: 0;
}
.deck-div {
  flex: none;
  width: 1px;
  align-self: stretch;
  background: linear-gradient(180deg, transparent, var(--edge) 22%, var(--edge) 78%, transparent);
}
/* Reserved width, not content width: the score is tabular and grows a character at a time, and
   an elastic scores cell would push the uplink until the pilot's own callsign ellipsised. */
.deck-scores { flex-direction: row; align-items: center; gap: 14px; min-width: 186px; }

.readout { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.readout-label {
  font-family: var(--mono);
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  line-height: 1;
  white-space: nowrap;
}
.readout-value {
  display: flex;
  align-items: baseline;
  gap: 4px;
  font-family: var(--mono);
  font-weight: 700;
  font-size: 25px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: 0.01em;
  /* Score is the number that becomes SPCXB on BNB Chain — so it carries the gold. */
  color: var(--bnb-soft);
  text-shadow: 0 0 18px rgba(240, 185, 11, 0.45);
  white-space: nowrap;
}
.readout-unit {
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-shadow: none;
}
/* Credits are secondary to score by a full step of the type scale — spendable, not redeemable. */
.readout-alt .readout-value {
  font-size: 17px;
  color: var(--cyan);
  text-shadow: 0 0 16px rgba(79, 214, 255, 0.40);
}
.readout-div {
  width: 1px;
  align-self: stretch;
  background: linear-gradient(180deg, transparent, var(--edge) 25%, var(--edge) 75%, transparent);
}

/* --- the cargo hold: the tense one ---------------------------------------- */

.hold {
  --hold-c: var(--ink-faint);
  --hold-c2: #2b3a52;
  --hold-glow: rgba(114, 134, 164, 0.35);
  width: 210px;
  flex: 0 1 210px;
  gap: 6px;
}
.hold-head { display: flex; align-items: baseline; gap: 7px; min-width: 0; }
.hold-label {
  font-family: var(--mono);
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  white-space: nowrap;
}
.hold-flag {
  padding: 2px 5px;
  font-family: var(--mono);
  font-size: 7.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--hold-c);
  box-shadow: inset 0 0 0 1px currentColor;
  opacity: 0.85;
  white-space: nowrap;
}
.hold-units {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--hold-c);
  white-space: nowrap;
}
.hold-cap { font-size: 9.5px; font-weight: 600; color: var(--ink-faint); }

.hold-bar {
  position: relative;
  height: 9px;
  overflow: hidden;
  clip-path: polygon(3px 0, 100% 0, 100% calc(100% - 3px), calc(100% - 3px) 100%, 0 100%, 0 3px);
  background: rgba(2, 6, 13, 0.85);
  box-shadow: inset 0 0 0 1px rgba(126, 181, 236, 0.22);
}
.hold-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0%;
  background: linear-gradient(90deg, var(--hold-c2), var(--hold-c));
  box-shadow: 0 0 12px var(--hold-glow);
  transition: width 220ms cubic-bezier(0.2, 0.8, 0.3, 1), background 300ms ease;
}
/* Docking progress rides over the cargo fill in teal — the bar literally converts from
   "at risk" to "banked" as the dock completes. */
.hold-dock {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0%;
  background: repeating-linear-gradient(
    -45deg,
    rgba(53, 224, 161, 0.95) 0 5px,
    rgba(23, 198, 138, 0.7) 5px 10px
  );
  box-shadow: 0 0 14px rgba(53, 224, 161, 0.8);
  transition: width 140ms linear;
}
.hold-ticks {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    90deg,
    rgba(0, 0, 0, 0) 0 calc(25% - 1px),
    rgba(3, 6, 12, 0.75) calc(25% - 1px) 25%
  );
}

.hold-foot { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.hold-value {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font-family: var(--mono);
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--hold-c);
  text-shadow: 0 0 16px var(--hold-glow);
}
.hold-value-unit {
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-shadow: none;
}
.hold-state {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Escalation. An empty hold is grey and uninteresting; a full hold deep in the field is red,
   glowing and moving. Nothing here touches score — only cargo can be lost. */
.hold[data-risk="empty"]   { --hold-c: #7286a4; --hold-c2: #2b3a52; --hold-glow: rgba(114,134,164,0.25); }
.hold[data-risk="low"]     { --hold-c: var(--amber); --hold-c2: #7a5410; --hold-glow: rgba(255,182,72,0.45); }
.hold[data-risk="mid"]     { --hold-c: var(--rust-soft); --hold-c2: #8a4218; --hold-glow: rgba(255,157,99,0.5); }
.hold[data-risk="high"]    { --hold-c: var(--rust); --hold-c2: #93330f; --hold-glow: rgba(255,107,44,0.6); }
.hold[data-risk="full"]    { --hold-c: var(--err); --hold-c2: #8c1c24; --hold-glow: rgba(255,90,99,0.65); }
.hold[data-risk="docking"] { --hold-c: var(--teal); --hold-c2: #0c6b4c; --hold-glow: rgba(53,224,161,0.6); }

.hold[data-risk="high"] .hold-state,
.hold[data-risk="full"] .hold-state { color: var(--hold-c); }
.hold[data-risk="full"] .hold-flag,
.hold[data-risk="high"] .hold-flag { opacity: 1; }
.hold[data-risk="full"] .hold-bar { box-shadow: inset 0 0 0 1px rgba(255, 90, 99, 0.5); }

/* Carrying, and outside the safe core: the one state where the module actively nags. */
.hold[data-exposed="1"] .hold-units,
.hold[data-exposed="1"] .hold-value { animation: hold-breathe 2.4s ease-in-out infinite; }
.hold[data-risk="full"][data-exposed="1"] .hold-bar::after,
.hold[data-risk="high"][data-exposed="1"] .hold-bar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 30%, rgba(255, 255, 255, 0.28) 50%, transparent 70%);
  background-size: 240% 100%;
  animation: hold-sweep 1.9s linear infinite;
}

@keyframes hold-breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.62; }
}
@keyframes hold-sweep {
  from { background-position: 120% 0; }
  to   { background-position: -120% 0; }
}

/* --- region + navigation --------------------------------------------------- */

.nav { width: 164px; flex: 0 1 164px; gap: 6px; }
.nav-top { display: flex; align-items: center; gap: 8px; min-width: 0; }
.region-chip {
  padding: 3px 7px;
  clip-path: polygon(5px 0, 100% 0, 100% calc(100% - 5px), calc(100% - 5px) 100%, 0 100%, 0 5px);
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
  color: var(--ink-faint);
  background: rgba(126, 181, 236, 0.06);
  box-shadow: inset 0 0 0 1px rgba(126, 181, 236, 0.22);
  transition: color 240ms ease, box-shadow 240ms ease, background 240ms ease;
}
.region-chip[data-region="0"] {
  color: var(--ok-ink);
  background: rgba(53, 224, 161, 0.10);
  box-shadow: inset 0 0 0 1px rgba(53, 224, 161, 0.42);
}
.region-chip[data-region="1"] {
  color: var(--warn-ink);
  background: rgba(255, 194, 61, 0.10);
  box-shadow: inset 0 0 0 1px rgba(255, 194, 61, 0.42);
}
.region-chip[data-region="2"] {
  color: #ffb9a2;
  background: rgba(255, 107, 44, 0.14);
  box-shadow: inset 0 0 0 1px rgba(255, 107, 44, 0.55);
  text-shadow: 0 0 12px rgba(255, 107, 44, 0.5);
}

.nav-bearing { display: inline-flex; align-items: center; gap: 5px; margin-left: auto; }
.nav-arrow {
  width: 13px;
  height: 13px;
  flex: none;
  fill: var(--teal);
  filter: drop-shadow(0 0 6px rgba(53, 224, 161, 0.7));
  transition: transform 200ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
.nav-dist {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--ok-ink);
}
.nav-note {
  font-family: var(--mono);
  font-size: 9px;
  line-height: 1.4;
  letter-spacing: 0.02em;
  color: var(--ink-faint);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.nav[data-mode="run"] .nav-note { color: var(--ok-ink); opacity: 0.85; }

.uplink {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: nowrap;
  justify-content: flex-end;
  pointer-events: none;
}
.uplink > * { pointer-events: auto; }

/* --- connection status light --------------------------------------------
   app.js rewrites className to exactly `status-pill status-<ok|error|pending>`,
   so every state must be reachable from these two classes alone.            */
.status-pill {
  --dot: var(--ink-dim);
  display: block;
  position: relative;
  flex: 0 1 auto;
  min-width: 0;
  max-width: 44ch;
  padding: 8px 13px 8px 30px;
  /* one clean line by default; long failure strings (which carry a URL) expand on hover
     and stay fully readable to assistive tech via role="status". */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  background: linear-gradient(180deg, rgba(16,26,44,0.72), rgba(4,8,15,0.82));
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  backdrop-filter: blur(12px) saturate(140%);
  box-shadow: inset 0 0 0 1px var(--edge);
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.status-pill:hover {
  white-space: normal;
  overflow-wrap: anywhere;
  text-overflow: clip;
}
.status-pill::before {
  content: "";
  position: absolute;
  left: 12px;
  top: 50%;
  margin-top: -4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--dot);
  box-shadow: 0 0 10px var(--dot);
  animation: dot-pulse 2.4s ease-in-out infinite;
}
.status-pill:hover::before { top: 13px; margin-top: 0; }
.status-pill.status-pending { --dot: var(--warn); color: var(--warn-ink); box-shadow: inset 0 0 0 1px rgba(255,194,61,0.35); }
.status-pill.status-ok      { --dot: var(--ok);   color: var(--ok-ink);   box-shadow: inset 0 0 0 1px rgba(53,224,161,0.35); }
.status-pill.status-error   { --dot: var(--err);  color: var(--err-ink);  box-shadow: inset 0 0 0 1px rgba(255,90,99,0.42); }
.status-pill.status-ok::before      { animation-duration: 3.6s; }
.status-pill.status-error::before   { animation: dot-alarm 1.1s steps(1, end) infinite; }

@keyframes dot-pulse {
  0%, 100% { opacity: 1;    box-shadow: 0 0 10px var(--dot); }
  50%      { opacity: 0.45; box-shadow: 0 0 3px var(--dot); }
}
@keyframes dot-alarm {
  0%, 49%   { opacity: 1;   box-shadow: 0 0 12px var(--dot); }
  50%, 100% { opacity: 0.2; box-shadow: 0 0 0 var(--dot); }
}

/* =============================================================================
   Buttons + fields
   ============================================================================= */

.btn {
  pointer-events: auto;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 16px;
  border: 0;
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
  background: linear-gradient(180deg, rgba(24,38,62,0.78), rgba(8,14,25,0.86));
  box-shadow: inset 0 0 0 1px var(--edge), inset 0 1px 0 rgba(200,230,255,0.10);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  /* explicit line-height so button heights are font-metric independent — .hud-top is
     height-pinned and must not depend on which system font the browser picked */
  line-height: 1.1;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: background 160ms ease, box-shadow 160ms ease, color 160ms ease, filter 160ms ease, transform 90ms ease;
}
.btn:hover {
  background: linear-gradient(180deg, rgba(34,54,86,0.86), rgba(12,20,34,0.9));
  box-shadow: inset 0 0 0 1px var(--edge-bright), inset 0 1px 0 rgba(200,230,255,0.16);
  color: #fff;
}
.btn:active { transform: translateY(1px); }
.btn-wide { display: flex; width: 100%; margin-top: 12px; }

.btn-accent {
  background: linear-gradient(180deg, #ff9250 0%, #ef5f1f 55%, #d34c12 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 200, 160, 0.55), inset 0 1px 0 rgba(255,255,255,0.35);
  color: #260d02;
  filter: drop-shadow(0 4px 16px rgba(255, 107, 44, 0.35));
}
.btn-accent:hover {
  background: linear-gradient(180deg, #ffa668 0%, #ff6f28 55%, #e4551a 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 220, 190, 0.7), inset 0 1px 0 rgba(255,255,255,0.45);
  color: #1c0900;
  filter: drop-shadow(0 4px 22px rgba(255, 107, 44, 0.6));
}

/* --- BNB gold: every button that talks to BNB Chain ----------------------- */
.btn-bnb {
  background: linear-gradient(180deg, #ffdc63 0%, var(--bnb) 52%, #c9970a 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 240, 190, 0.6), inset 0 1px 0 rgba(255,255,255,0.42);
  color: var(--bnb-ink);
  filter: drop-shadow(0 4px 16px rgba(240, 185, 11, 0.32));
}
.btn-bnb:hover {
  background: linear-gradient(180deg, #ffe98c 0%, #ffc61f 52%, #dda80c 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 248, 220, 0.75), inset 0 1px 0 rgba(255,255,255,0.5);
  color: #170f00;
  filter: drop-shadow(0 4px 22px rgba(240, 185, 11, 0.58));
}

/* quiet + ghost: secondary actions that must not compete with the gold ones */
.btn-quiet {
  background: linear-gradient(180deg, rgba(18,29,48,0.7), rgba(6,11,20,0.8));
  box-shadow: inset 0 0 0 1px rgba(126,181,236,0.18);
  color: var(--ink-dim);
}
.btn-quiet:hover { color: var(--cyan-soft); box-shadow: inset 0 0 0 1px var(--edge-bright); }
.btn-tiny { padding: 6px 10px; font-size: 9px; letter-spacing: 0.14em; }

.btn-ghost {
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--edge-bright);
  color: var(--cyan-soft);
}
.btn-ghost:hover { background: rgba(79,214,255,0.08); color: #fff; }

.btn:disabled,
.btn[disabled] {
  cursor: not-allowed;
  filter: grayscale(0.7) brightness(0.72);
  opacity: 0.62;
}
.btn:disabled:hover,
.btn[disabled]:hover { transform: none; }
.btn:disabled:active,
.btn[disabled]:active { transform: none; }

/* --- callsign chip: the way back into the briefing ------------------------ */
.btn-callsign {
  /* flex: 0 0 auto — the pilot's own identity is the one thing in this row that must never
     ellipsise, and 0 1 auto let it truncate to "Briefi…" once the flight deck widened the strip.
     The status pill absorbs every squeeze instead: it keeps its colour-coded dot and expands on
     hover, and the same sentence is repeated in the Pilot ID panel and as a toast. */
  flex: 0 0 auto;
  min-width: 0;
  max-width: 22ch;
  padding: 9px 12px;
  gap: 7px;
  background: linear-gradient(180deg, rgba(20,32,52,0.78), rgba(6,11,20,0.86));
  box-shadow: inset 0 0 0 1px var(--edge);
  color: var(--ink-dim);
  text-transform: none;
  letter-spacing: 0.06em;
  font-size: 11px;
}
.btn-callsign #callsign-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.btn-callsign:hover { color: var(--bnb-pale); box-shadow: inset 0 0 0 1px var(--edge-gold); }
.btn-callsign.has-callsign { color: var(--bnb-pale); box-shadow: inset 0 0 0 1px var(--edge-gold); }
.callsign-glyph {
  width: 7px;
  height: 7px;
  flex: none;
  clip-path: polygon(50% 0, 100% 38%, 82% 100%, 18% 100%, 0 38%);
  background: currentColor;
  box-shadow: 0 0 8px currentColor;
}

.btn-confirm {
  background: linear-gradient(180deg, #4defb9 0%, #17c68a 55%, #0da072 100%);
  box-shadow: inset 0 0 0 1px rgba(180, 255, 230, 0.55), inset 0 1px 0 rgba(255,255,255,0.35);
  color: #002317;
  filter: drop-shadow(0 4px 16px rgba(53, 224, 161, 0.32));
}
.btn-confirm:hover {
  background: linear-gradient(180deg, #6df5c8 0%, #23dc9b 55%, #10b881 100%);
  filter: drop-shadow(0 4px 22px rgba(53, 224, 161, 0.55));
}

/* connected wallet — app.js adds .btn-connected on top of .btn.btn-accent */
.btn.btn-connected {
  background: linear-gradient(180deg, rgba(14,34,48,0.85), rgba(5,12,20,0.9));
  box-shadow: inset 0 0 0 1px rgba(79, 214, 255, 0.55);
  color: var(--cyan-soft);
  cursor: default;
  filter: none;
  /* the label becomes a checksummed address — never case-fold it */
  text-transform: none;
  letter-spacing: 0.06em;
}
.btn.btn-connected::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--teal);
  box-shadow: 0 0 9px var(--teal);
}
.btn.btn-connected:hover { color: #fff; transform: none; }

.btn-mine {
  flex: 1 1 auto;
  padding: 13px 22px;
  font-size: 12px;
  letter-spacing: 0.2em;
  background: linear-gradient(180deg, rgba(58,32,18,0.88), rgba(20,10,5,0.9));
  box-shadow: inset 0 0 0 1px var(--edge-rust), inset 0 1px 0 rgba(255, 190, 150, 0.18);
  color: var(--rust-soft);
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  white-space: nowrap;
}
.btn-mine:hover { color: #ffd0b2; box-shadow: inset 0 0 0 1px rgba(255,140,80,0.65); }
.btn-mine:active {
  background: linear-gradient(180deg, #ff8340, #d9500f);
  color: #240b01;
  box-shadow: inset 0 0 0 1px rgba(255,220,190,0.7);
}
/* Full hold: mining is refused by the server, so the control says why instead of looking live. */
.btn-mine[data-full="1"] {
  background: linear-gradient(180deg, rgba(62, 16, 22, 0.9), rgba(22, 6, 9, 0.92));
  box-shadow: inset 0 0 0 1px rgba(255, 90, 99, 0.55), inset 0 1px 0 rgba(255, 190, 190, 0.14);
  color: var(--err-ink);
}
.btn-mine[data-full="1"]:hover { color: #ffd0d3; box-shadow: inset 0 0 0 1px rgba(255, 90, 99, 0.8); }
.btn-mine[data-full="1"] .mine-dot { animation: dot-pulse 1.2s ease-in-out infinite; }

.mine-dot {
  width: 8px;
  height: 8px;
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  background: currentColor;
  box-shadow: 0 0 10px currentColor;
}

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

/* =============================================================================
   Rail panels
   ============================================================================= */

.panel { padding: 13px 15px 14px; }

.panel-head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding-bottom: 8px;
  margin-bottom: 10px;
  border-bottom: 1px solid rgba(126, 181, 236, 0.16);
}
.panel-head h2 {
  margin: 0;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink);
}
.panel-glyph {
  flex: none;
  width: 8px;
  height: 8px;
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  background: var(--cyan);
  box-shadow: 0 0 10px rgba(79, 214, 255, 0.9);
}
.panel-tag {
  margin-left: auto;
  padding: 3px 7px;
  font-family: var(--mono);
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink-faint);
  box-shadow: inset 0 0 0 1px rgba(126, 181, 236, 0.20);
}
.panel-tag-bnb { color: var(--bnb-pale); box-shadow: inset 0 0 0 1px var(--edge-gold); }
.panel-tag[data-kind="ok"]   { color: var(--ok-ink);   box-shadow: inset 0 0 0 1px rgba(53,224,161,0.32); }
.panel-tag[data-kind="warn"] { color: var(--warn-ink); box-shadow: inset 0 0 0 1px rgba(255,194,61,0.32); }
.panel-tag[data-kind="idle"] { color: var(--ink-faint); box-shadow: inset 0 0 0 1px rgba(126,181,236,0.16); }

.panel-copy {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-dim);
}

/* status/notes — app.js resets className to `panel-note[ note-<kind>]` */
.panel-note {
  margin: 11px 0 0;
  padding-left: 10px;
  border-left: 2px solid rgba(126, 181, 236, 0.28);
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink-dim);
  overflow-wrap: anywhere;
}
.panel-note:empty { display: none; }
.panel-note.note-ok      { color: var(--ok-ink);   border-left-color: var(--ok); }
.panel-note.note-error   { color: var(--err-ink);  border-left-color: var(--err); }
.panel-note.note-pending { color: var(--warn-ink); border-left-color: var(--warn); }

/* --- cash out: the money path -------------------------------------------- */

/* The vault is the on-chain module, so it carries the BNB gold edge rather than the
   game's rust. Rust stays on the game-side chrome (mine button, brand, hazards). */
.panel-primary {
  background: var(--glass-strong);
  box-shadow:
    inset 0 0 0 1px var(--edge-gold),
    inset 0 1px 0 rgba(255, 236, 180, 0.16),
    inset 0 -60px 70px -60px rgba(240, 185, 11, 0.7);
}
.panel-primary::before, .panel-primary::after { background: var(--edge-gold-hi); }
.panel-primary .panel-glyph { background: var(--bnb); box-shadow: 0 0 10px rgba(240, 185, 11, 0.95); }
.panel-primary .panel-head { border-bottom-color: rgba(240, 185, 11, 0.24); }

.cashout-details {
  margin-top: 13px;
  animation: reveal 320ms cubic-bezier(0.2, 0.8, 0.3, 1) both;
}

/* THE BLOCKER FIX. The moment a voucher exists, the claim card becomes the first thing in the
   rail, so Confirm sits ~330px from the top of the scrollport at every breakpoint we ship —
   it can never be pushed under the fold by the panels above it. The rail still scrolls (and
   still shows the fade + thumb), but the money path never depends on scrolling. */
#cashout-section:has(#cashout-details:not([hidden])) { order: -1; }
/* and the least useful panel steps out of the way while a claim is pending */
.hud-rail:has(#cashout-details:not([hidden])) #lookup-panel { order: 1; }

/* Once a voucher preview exists the panel becomes a CLAIM CARD: the three vault readouts, the
   withdrawal meter and the explainer are all superseded by the voucher's own numbers, and
   dropping them is what keeps "Confirm Claim On-Chain" above the fold on a 720px-tall laptop —
   the money path must never depend on noticing that the rail scrolls. */
#cashout-section:has(#cashout-details:not([hidden])) .vault,
#cashout-section:has(#cashout-details:not([hidden])) .meter,
#cashout-section:has(#cashout-details:not([hidden])) .meter-note,
#cashout-section:has(#cashout-details:not([hidden])) .panel-copy { display: none; }

/* once a preview is on screen, "confirm" becomes the primary action and "fetch" steps back */
#cashout-section:has(#cashout-details:not([hidden])) #cashout-start-btn {
  background: linear-gradient(180deg, rgba(40,32,10,0.8), rgba(14,11,3,0.88));
  box-shadow: inset 0 0 0 1px var(--edge-gold);
  color: var(--bnb-pale);
  filter: none;
}
#cashout-section:has(#cashout-details:not([hidden])) #cashout-start-btn:hover {
  color: #fff2c4;
  box-shadow: inset 0 0 0 1px var(--edge-gold-hi);
}

.voucher { margin: 0; }
.voucher-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  align-items: baseline;
  padding: 6px 0;
  border-bottom: 1px dashed rgba(126, 181, 236, 0.16);
}
.voucher-row dt {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.voucher-row dd {
  margin: 0;
  text-align: right;
  font-family: var(--mono);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.voucher-payout {
  margin-top: 11px;
  padding: 11px 13px;
  position: relative;
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
  background:
    linear-gradient(180deg, rgba(12, 48, 44, 0.85), rgba(5, 20, 22, 0.9));
  box-shadow:
    inset 0 0 0 1px rgba(53, 224, 161, 0.4),
    inset 0 -30px 40px -34px rgba(53, 224, 161, 0.9);
}
.voucher-payout dt {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(147, 242, 206, 0.85);
}
.voucher-payout dd {
  margin: 5px 0 0;
  font-family: var(--mono);
  font-size: 21px;
  font-weight: 700;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  color: #8ff7d3;
  text-shadow: 0 0 22px rgba(53, 224, 161, 0.5);
  overflow-wrap: anywhere;
}

/* --- lookup --------------------------------------------------------------- */

.field-label {
  display: block;
  margin-bottom: 7px;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.lookup-row { display: flex; gap: 8px; }
.lookup-row input {
  flex: 1;
  min-width: 0;
  padding: 10px 11px;
  border: 0;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  background: rgba(2, 6, 13, 0.72);
  box-shadow: inset 0 0 0 1px var(--edge);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.02em;
  transition: box-shadow 160ms ease, background 160ms ease;
}
.lookup-row input::placeholder { color: rgba(114, 134, 164, 0.75); }
.lookup-row input:hover { box-shadow: inset 0 0 0 1px var(--edge-bright); }
.lookup-row input:focus { outline: none; background: rgba(2, 6, 13, 0.9); box-shadow: inset 0 0 0 1px var(--cyan); }
/* keyboard focus gets a thicker ring that still follows the bevel (an outline would be clipped) */
.lookup-row input:focus-visible { box-shadow: inset 0 0 0 2px var(--focus); }
.lookup-row .btn { flex: none; }

/* =============================================================================
   Controls block + system line (both live inside .hud-dock)
   ============================================================================= */

.controls {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 10px 12px;
}
.control-row { display: flex; align-items: stretch; gap: 8px; }
.legend { display: flex; flex-direction: column; gap: 6px; }
.legend-item { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; row-gap: 4px; }
.legend-key {
  min-width: 42px;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.legend-hint {
  margin-left: 4px;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
}
kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 19px;
  padding: 0 5px;
  border-radius: 3px;
  background: linear-gradient(180deg, rgba(30,46,72,0.9), rgba(9,15,26,0.92));
  box-shadow: inset 0 0 0 1px rgba(126,181,236,0.28), 0 1px 0 rgba(0,0,0,0.5);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  color: var(--ink-dim);
}
kbd.kbd-wide { min-width: 48px; letter-spacing: 0.08em; }

/* --- ops drawer toggle (phones only; hidden at desktop widths) ------------- */

.ops-toggle {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
}
.ops-tab {
  display: none; /* desktop: the rail is always open, so there is nothing to toggle */
  flex: 0 0 auto;
  gap: 7px;
  padding: 13px 15px;
  background: linear-gradient(180deg, rgba(22,40,64,0.9), rgba(7,13,24,0.92));
  box-shadow: inset 0 0 0 1px var(--edge-bright);
  color: var(--cyan-soft);
}
.ops-tab:hover { color: #fff; }
.ops-caret {
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-bottom: 6px solid currentColor;
  transition: transform 200ms ease;
}
.ops-toggle:focus-visible + .ops-tab { outline: 2px solid var(--focus); outline-offset: -3px; }

.foot {
  pointer-events: none;
  display: flex;
  align-items: baseline;
  gap: 6px 12px;
  flex-wrap: wrap;
  justify-content: flex-start;
  padding: 0 4px 2px;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  min-width: 0;
}
.foot-line { display: inline-flex; align-items: baseline; gap: 8px; min-width: 0; flex-wrap: wrap; row-gap: 4px; }
.foot-key { text-transform: uppercase; }
.foot code {
  pointer-events: auto;
  user-select: text;
  padding: 3px 7px;
  clip-path: polygon(5px 0, 100% 0, 100% calc(100% - 5px), calc(100% - 5px) 100%, 0 100%, 0 5px);
  background: rgba(2, 6, 13, 0.7);
  box-shadow: inset 0 0 0 1px rgba(126, 181, 236, 0.18);
  font-size: 9px;
  letter-spacing: 0.04em;
  color: var(--ink-dim);
  overflow-wrap: anywhere;
}
.foot a { pointer-events: auto; text-transform: uppercase; letter-spacing: 0.12em; }
.foot-chain code { color: var(--bnb-pale); box-shadow: inset 0 0 0 1px var(--edge-gold); }

/* =============================================================================
   Live sector strip — the "telemetry" module
   -----------------------------------------------------------------------------
   Four cells, four real sources (see telemetry.js). No market data, no modelled
   figures: a cell with no source shows an em dash, never a number.
   ============================================================================= */

.strip {
  pointer-events: auto;
  display: grid;
  /* The network cell carries a word, not a number, so it gets the extra track width —
     "BNB Chain" must never ellipsis to "BNB Cha…". */
  grid-template-columns: minmax(0, 0.86fr) minmax(0, 0.94fr) minmax(0, 0.92fr) minmax(0, 1.28fr);
  gap: 1px;
  padding: 1px;
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
  background: rgba(126, 181, 236, 0.16);
  box-shadow: inset 0 0 0 1px var(--edge);
  position: relative;
  overflow: hidden;
}
/* slow gold sweep — the module reads as "live" without animating any number */
.strip::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(100deg, transparent 32%, rgba(240,185,11,0.13) 50%, transparent 68%);
  background-size: 260% 100%;
  animation: strip-sweep 7.5s linear infinite;
}
@keyframes strip-sweep {
  from { background-position: 130% 0; }
  to   { background-position: -130% 0; }
}

.strip-cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: 7px 9px 8px;
  background: linear-gradient(180deg, rgba(14,23,40,0.92), rgba(5,9,17,0.94));
}
.strip-label {
  font-family: var(--mono);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.strip-value {
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--cyan-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.strip-cell-chain .strip-value {
  font-size: 10.5px;
  letter-spacing: 0;
  color: var(--bnb-pale);
  text-shadow: 0 0 14px rgba(240,185,11,0.35);
  padding-top: 3px;
}
#stat-pilots[data-live="1"] { color: var(--ok-ink); }

/* value-changed flash, shared by every live readout */
.tick { animation: tick-flash 620ms ease-out 1; }
@keyframes tick-flash {
  0%   { filter: brightness(2.1); text-shadow: 0 0 16px currentColor; }
  100% { filter: none; }
}

/* =============================================================================
   Rail tabs — two groups instead of eight stacked boxes
   -----------------------------------------------------------------------------
   SHIP  : live sector strip, Pilot ID, Ship Upgrades   (what you fly with)
   CHAIN : Rewards Vault, Leaderboard, Pilot Badge, Lookup   (what touches BNB Chain)

   This is also the money path's structural guarantee: the vault is the FIRST
   module in its group, so "Fetch Cash-Out Voucher" sits ~300px from the top of
   the scrollport at every size we ship and can never be pushed under the fold by
   the panels above it. The old single column could not promise that once the
   upgrades panel joined the rail.

   CSS-only, driven by two radios (strict CSP forbids inline script). The last
   rule in this block is the backstop: a revealed #cashout-details force-shows the
   vault whatever the selected tab is, so a claim in progress can never be hidden
   behind a tab — the same guarantee the mobile ops drawer already makes.
   ============================================================================= */

.rail-tabs {
  flex: none;
  display: flex;
  gap: 1px;
  margin-bottom: 9px;
  padding: 1px;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  background: rgba(126, 181, 236, 0.14);
  box-shadow: inset 0 0 0 1px var(--edge);
  pointer-events: auto;
}
.rail-tab-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
}
.rail-tab {
  flex: 1 1 0;
  padding: 7px 8px 8px;
  background: linear-gradient(180deg, rgba(10, 17, 30, 0.9), rgba(4, 8, 15, 0.94));
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-align: center;
  line-height: 1;
  color: var(--ink-faint);
  cursor: pointer;
  transition: color 180ms ease, background 180ms ease, box-shadow 180ms ease;
}
.rail-tab:hover { color: var(--ink-dim); }
.rail-tab-input:checked + .rail-tab {
  background: linear-gradient(180deg, rgba(26, 44, 70, 0.95), rgba(8, 15, 27, 0.95));
  box-shadow: inset 0 -2px 0 var(--cyan);
  color: var(--cyan-soft);
}
.rail-tab-input:checked + .rail-tab-chain {
  box-shadow: inset 0 -2px 0 var(--bnb);
  color: var(--bnb-pale);
}
.rail-tab-input:focus-visible + .rail-tab { outline: 2px solid var(--focus); outline-offset: -3px; }

/* SECTOR: live strip, Pilot ID, Ship Upgrades, Leaderboard (standings are a sector thing).
   BNB CHAIN: Rewards Vault, Pilot Badge, Ledger Lookup. */
.rail-shell:has(#tab-chain:checked) #sector-strip,
.rail-shell:has(#tab-chain:checked) #login-panel,
.rail-shell:has(#tab-chain:checked) #upgrades-panel,
.rail-shell:has(#tab-chain:checked) #leaderboard-panel,
.rail-shell:has(#tab-ship:checked) #cashout-section,
.rail-shell:has(#tab-ship:checked) #badge-panel,
.rail-shell:has(#tab-ship:checked) #lookup-panel { display: none; }

/* The money path outranks the tab selection, always. */
.rail-shell:has(#cashout-details:not([hidden])) #cashout-section { display: block; }

/* =============================================================================
   Ship upgrades
   -----------------------------------------------------------------------------
   Four tracks, five levels each. Cyan throughout: credits are the currency here,
   and none of this touches the chain. The server is authoritative — a row only
   moves when the server says so (see upgrades.js).
   ============================================================================= */

.panel-ship .panel-glyph { background: var(--teal); box-shadow: 0 0 10px rgba(53, 224, 161, 0.9); }
#up-credits { font-variant-numeric: tabular-nums; }
#up-credits[data-kind="ok"] { color: var(--cyan-soft); box-shadow: inset 0 0 0 1px rgba(79, 214, 255, 0.34); }

.ups {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
/* One line per track — mark, name, what the next level buys, level pips, price. Four of these
   plus a head is 140px of rail, which is what keeps the Rewards Vault's entry point above the
   fold on a 900px-tall viewport. */
.up {
  display: grid;
  grid-template-columns: 15px auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 8px;
  padding: 5px 4px 5px 3px;
  background: linear-gradient(90deg, rgba(126, 181, 236, 0.05), rgba(126, 181, 236, 0));
  transition: background 180ms ease;
}
.up[data-state="ready"] { background: linear-gradient(90deg, rgba(79, 214, 255, 0.11), rgba(79, 214, 255, 0)); }
.up[data-state="max"]   { opacity: 0.62; }

.up-ico {
  width: 14px;
  height: 14px;
  justify-self: center;
  background: var(--ink-faint);
  transition: background 200ms ease, box-shadow 200ms ease;
}
.up[data-state="ready"] .up-ico { background: var(--cyan); box-shadow: 0 0 10px rgba(79, 214, 255, 0.7); }
/* Procedural marks — no icon font, no image assets. */
.up[data-kind="cargo"]  .up-ico { clip-path: polygon(3px 0, 100% 0, 100% calc(100% - 3px), calc(100% - 3px) 100%, 0 100%, 0 3px); }
.up[data-kind="thrust"] .up-ico { clip-path: polygon(50% 0, 100% 100%, 50% 78%, 0 100%); }
.up[data-kind="mining"] .up-ico { clip-path: polygon(50% 0, 100% 38%, 82% 100%, 18% 100%, 0 38%); }
.up[data-kind="shield"] .up-ico { clip-path: polygon(50% 0, 100% 22%, 100% 62%, 50% 100%, 0 62%, 0 22%); }

.up-name {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.up-pips { display: flex; gap: 3px; flex: none; }
.up-pips i {
  width: 9px;
  height: 3px;
  background: rgba(126, 181, 236, 0.22);
  transition: background 220ms ease, box-shadow 220ms ease;
}
.up-pips i[data-on="1"] { background: var(--cyan); box-shadow: 0 0 7px rgba(79, 214, 255, 0.75); }
.up-effect {
  min-width: 0;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.02em;
  color: var(--ink-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.btn-up {
  flex: none;
  min-width: 56px;
  padding: 5px 8px;
  gap: 3px;
  font-size: 10px;
  letter-spacing: 0.1em;
  background: linear-gradient(180deg, rgba(18, 34, 54, 0.85), rgba(6, 12, 22, 0.9));
  box-shadow: inset 0 0 0 1px rgba(126, 181, 236, 0.22);
  color: var(--ink-dim);
}
.up[data-state="ready"] .btn-up {
  background: linear-gradient(180deg, rgba(24, 62, 84, 0.9), rgba(7, 20, 30, 0.92));
  box-shadow: inset 0 0 0 1px rgba(79, 214, 255, 0.55);
  color: var(--cyan-soft);
}
.up[data-state="ready"] .btn-up:hover { color: #fff; box-shadow: inset 0 0 0 1px rgba(166, 233, 255, 0.85); }
.up-cost { font-variant-numeric: tabular-nums; }
.up-unit { font-size: 8px; opacity: 0.7; }
.up[data-state="pending"] .btn-up { color: var(--warn-ink); box-shadow: inset 0 0 0 1px rgba(255, 194, 61, 0.45); }

.up-won { animation: up-won 900ms ease-out 1; }
@keyframes up-won {
  0%   { background: linear-gradient(90deg, rgba(53, 224, 161, 0.42), rgba(53, 224, 161, 0.04)); }
  100% { background: linear-gradient(90deg, rgba(126, 181, 236, 0.05), rgba(126, 181, 236, 0)); }
}

/* =============================================================================
   Pilot ID card
   ============================================================================= */

.panel-pilot .panel-glyph { background: var(--rust); box-shadow: 0 0 10px rgba(255,107,44,0.9); }

.pilot {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) auto;
  align-items: center;
  gap: 11px;
}
.pilot-sigil {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: conic-gradient(from 200deg, #24344f, #101a2b 55%, #24344f);
  box-shadow: inset 0 0 0 1px rgba(200,230,255,0.25), 0 0 14px rgba(0,0,0,0.6);
  position: relative;
  filter: saturate(0.25) brightness(0.7);
  transition: filter 320ms ease;
}
.pilot-sigil[data-linked="1"] { filter: none; }
.pilot-sigil::after {
  content: "";
  position: absolute;
  inset: 7px;
  border-radius: 50%;
  background: radial-gradient(circle at 40% 35%, rgba(6,10,18,0.15), rgba(4,7,14,0.92));
}
.pilot-id { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.pilot-name {
  pointer-events: auto;
  align-self: flex-start;
  max-width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1.15;
  color: var(--ink-dim);
  cursor: pointer;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  border-bottom: 1px dashed rgba(126,181,236,0.35);
  transition: color 160ms ease, border-color 160ms ease;
}
.pilot-name:hover { color: var(--cyan-soft); border-bottom-color: var(--cyan); }
.pilot-name.has-callsign { color: var(--bnb-pale); border-bottom-color: var(--edge-gold); }
.pilot-name.has-callsign:hover { color: #fff3cb; border-bottom-color: var(--bnb); }
.pilot-addr {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pilot-rank { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.pilot-rank-label {
  font-family: var(--mono);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  line-height: 1;
}
.pilot-rank-value {
  font-family: var(--mono);
  font-size: 18px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--ink-dim);
}
.pilot-rank-value[data-top="1"] { color: var(--bnb-soft); text-shadow: 0 0 16px rgba(240,185,11,0.45); }

/* =============================================================================
   Rewards Vault readouts + claim meter
   ============================================================================= */

.vault { margin: 0 0 11px; }
.vault-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  align-items: baseline;
  padding: 5px 0;
  border-bottom: 1px solid rgba(240, 185, 11, 0.10);
}
.vault-row:last-child { border-bottom: 0; }
.vault-row dt {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.vault-row dd {
  margin: 0;
  text-align: right;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--bnb-pale);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.meter {
  position: relative;
  height: 5px;
  overflow: hidden;
  clip-path: polygon(3px 0, 100% 0, 100% calc(100% - 3px), calc(100% - 3px) 100%, 0 100%, 0 3px);
  background: rgba(2, 6, 13, 0.8);
  box-shadow: inset 0 0 0 1px rgba(240,185,11,0.18);
}
.meter-fill {
  display: block;
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--bnb-deep), var(--bnb) 60%, var(--bnb-soft));
  box-shadow: 0 0 12px rgba(240,185,11,0.6);
  transition: width 520ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
/* dormant = the contracts do not exist yet. Hatched, unfilled, obviously inert. */
.meter[data-state="dormant"] {
  background-image: repeating-linear-gradient(
    -45deg,
    rgba(240,185,11,0.14) 0 4px,
    rgba(0,0,0,0) 4px 9px
  );
}
.meter-note {
  margin: 7px 0 0;
  font-family: var(--mono);
  font-size: 9.5px;
  line-height: 1.5;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
}
#cashout-section .panel-copy { margin-top: 11px; }

/* =============================================================================
   Leaderboard
   ============================================================================= */

.panel-board .panel-glyph { background: var(--bnb); box-shadow: 0 0 10px rgba(240,185,11,0.9); }

.board {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.board-row {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr) auto;
  align-items: center;
  gap: 9px;
  padding: 6px 8px 6px 4px;
  position: relative;
  background: linear-gradient(90deg, rgba(126,181,236,0.06), rgba(126,181,236,0));
  transition: background 160ms ease, box-shadow 160ms ease;
}
.board-row:hover { background: linear-gradient(90deg, rgba(126,181,236,0.14), rgba(126,181,236,0.02)); }
.board-rank {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--ink-faint);
}
.board-name {
  font-family: var(--mono);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.board-name[data-anon="1"] { color: var(--ink-faint); font-weight: 500; }
.board-score {
  font-family: var(--mono);
  font-size: 11.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--cyan-soft);
}

/* podium — gold, then two cooler metals so #1 is unmistakable */
.board-row[data-medal="1"] .board-rank { color: var(--bnb); text-shadow: 0 0 10px rgba(240,185,11,0.7); }
.board-row[data-medal="2"] .board-rank { color: #cfe0f2; }
.board-row[data-medal="3"] .board-rank { color: #d8a273; }
.board-row[data-medal="1"] { background: linear-gradient(90deg, rgba(240,185,11,0.14), rgba(240,185,11,0)); }

/* you */
.board-row[data-you="1"] {
  background: linear-gradient(90deg, rgba(240,185,11,0.20), rgba(240,185,11,0.02));
  box-shadow: inset 0 0 0 1px var(--edge-gold);
}
.board-row[data-you="1"] .board-name { color: var(--bnb-pale); font-weight: 700; }
/* The marker lives INSIDE the row's own padding. An earlier version floated it above the row,
   where it landed on the score of the pilot ranked above you. */
.board-row[data-you="1"] { padding-top: 13px; }
.board-row[data-you="1"]::after {
  content: "YOU";
  position: absolute;
  right: 4px;
  top: 1px;
  padding: 1px 4px;
  font-family: var(--mono);
  font-size: 7px;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: var(--bnb-ink);
  background: var(--bnb);
}
.board-row[data-pinned="1"]::after { content: none; }

.board-empty {
  margin: 4px 0 0;
  font-size: 11px;
  line-height: 1.5;
  color: var(--ink-faint);
}
.board-empty[hidden] { display: none; }

.board-you {
  margin-top: 9px;
  padding-top: 8px;
  border-top: 1px dashed rgba(240,185,11,0.24);
}
.board-you-label {
  display: block;
  margin-bottom: 5px;
  font-family: var(--mono);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--bnb-pale);
}
.board-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 10px;
}
.board-stamp {
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-left: auto;
}

/* =============================================================================
   Pilot Badge
   ============================================================================= */

.panel-badge .panel-glyph { background: var(--bnb); box-shadow: 0 0 10px rgba(240,185,11,0.9); }

.badge-lay { display: grid; grid-template-columns: 84px minmax(0, 1fr); gap: 13px; align-items: start; }
.badge-art {
  width: 84px;
  height: 84px;
  display: block;
  filter: drop-shadow(0 4px 18px rgba(240,185,11,0.28));
  transition: filter 320ms ease, opacity 320ms ease;
}
#badge-panel[data-state="locked"] .badge-art { filter: grayscale(0.85) brightness(0.62); opacity: 0.72; }
#badge-panel[data-state="minted"] .badge-art { filter: drop-shadow(0 4px 24px rgba(240,185,11,0.6)); }
.badge-copy { min-width: 0; }
.badge-copy .panel-copy { font-size: 11px; }
.badge-facts { margin: 9px 0 0; }
.badge-fact {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 8px;
  align-items: baseline;
  padding: 4px 0;
  border-bottom: 1px solid rgba(240,185,11,0.10);
}
.badge-fact:last-child { border-bottom: 0; }
.badge-fact dt {
  font-family: var(--mono);
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.badge-fact dd {
  margin: 0;
  text-align: right;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--ink-dim);
}
#badge-rank { font-size: 13px; font-weight: 700; color: var(--ink-dim); }
#badge-rank[data-top="1"] { color: var(--bnb-soft); text-shadow: 0 0 14px rgba(240,185,11,0.45); }

/* =============================================================================
   Toast — replaces alert() on the wallet path
   ============================================================================= */

.toast {
  position: fixed;
  left: 50%;
  bottom: 26px;
  z-index: 70;
  max-width: min(92vw, 480px);
  padding: 12px 16px;
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
  background: linear-gradient(180deg, rgba(20,32,54,0.96), rgba(5,9,17,0.97));
  box-shadow: inset 0 0 0 1px var(--edge-bright), 0 12px 40px rgba(0,0,0,0.6);
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.5;
  letter-spacing: 0.03em;
  color: var(--ink);
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, 14px);
  transition: opacity 240ms ease, transform 240ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
.toast[data-on="1"] { opacity: 1; transform: translate(-50%, 0); }
.toast[data-kind="error"] { box-shadow: inset 0 0 0 1px rgba(255,90,99,0.5), 0 12px 40px rgba(0,0,0,0.6); color: var(--err-ink); }
.toast[data-kind="ok"]    { box-shadow: inset 0 0 0 1px rgba(53,224,161,0.5), 0 12px 40px rgba(0,0,0,0.6); color: var(--ok-ink); }

/* =============================================================================
   Splash / first-run briefing
   ============================================================================= */

.splash {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 24px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.splash-scrim {
  position: fixed;
  inset: 0;
  background:
    radial-gradient(70% 55% at 82% 8%,  rgba(240,185,11,0.16) 0%, rgba(240,185,11,0) 62%),
    radial-gradient(70% 60% at 12% 92%, rgba(255,107,44,0.16) 0%, rgba(255,107,44,0) 62%),
    linear-gradient(180deg, rgba(3,6,12,0.88), rgba(2,4,9,0.95));
  -webkit-backdrop-filter: blur(14px) saturate(130%);
  backdrop-filter: blur(14px) saturate(130%);
  animation: fade-in 340ms ease both;
}

.splash-card {
  --cut: 20px;
  --len: calc(var(--cut) * 1.4143);
  position: relative;
  width: min(560px, 100%);
  margin: auto;
  padding: 24px 26px 20px;
  clip-path: polygon(
    var(--cut) 0, 100% 0,
    100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%,
    0 100%, 0 var(--cut)
  );
  background: linear-gradient(176deg, rgba(20,32,54,0.86) 0%, rgba(5,9,17,0.94) 100%);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  box-shadow:
    inset 0 0 0 1px var(--edge-gold),
    inset 0 1px 0 rgba(255,236,180,0.16),
    inset 0 -90px 110px -90px rgba(240,185,11,0.55),
    0 30px 80px rgba(0,0,0,0.7);
  animation: splash-in 460ms cubic-bezier(0.2, 0.85, 0.3, 1) both;
}
.splash-card:focus { outline: none; }
.splash-card::before,
.splash-card::after {
  content: "";
  position: absolute;
  width: var(--len);
  height: 1px;
  background: var(--edge-gold-hi);
  pointer-events: none;
}
.splash-card::before { top: calc(var(--cut) / 2); left: calc(var(--cut) / 2 - var(--len) / 2); transform: rotate(-45deg); }
.splash-card::after  { bottom: calc(var(--cut) / 2); right: calc(var(--cut) / 2 - var(--len) / 2); transform: rotate(-45deg); }

.splash-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 15px;
  margin-bottom: 17px;
  border-bottom: 1px solid rgba(240,185,11,0.20);
}
.splash-mark { width: 26px; height: 26px; }
.splash-wordmark {
  font-family: var(--mono);
  font-weight: 700;
  font-size: 17px;
  letter-spacing: 0.24em;
  line-height: 1;
  color: var(--ink);
}
.splash-wordmark em { font-style: normal; color: var(--rust); text-shadow: 0 0 14px rgba(255,107,44,0.65); }
.splash-skip {
  margin-left: auto;
  padding: 6px 10px;
  border: 0;
  background: none;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
  cursor: pointer;
  transition: color 160ms ease;
}
.splash-skip:hover { color: var(--cyan-soft); }

.splash-title {
  margin: 0 0 9px;
  font-family: var(--mono);
  font-size: 25px;
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.005em;
  color: var(--ink);
}
.splash-lede {
  margin: 0 0 20px;
  max-width: 52ch;
  font-size: 13px;
  line-height: 1.62;
  color: var(--ink-dim);
}

/* The loop, said once, before the three onboarding steps. Four lines beat a paragraph:
   this is the part a new pilot must actually understand before flying. */
.brief-loop {
  list-style: none;
  margin: 0 0 18px;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
  background: rgba(126, 181, 236, 0.05);
  box-shadow: inset 0 0 0 1px var(--edge);
}
.brief-loop li {
  display: grid;
  grid-template-columns: 66px minmax(0, 1fr);
  gap: 10px;
  align-items: baseline;
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-dim);
}
.brief-loop em { font-style: normal; color: var(--ink); }
.brief-step {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--rust-soft);
}
.brief-loop li:nth-child(2) .brief-step { color: var(--warn-ink); }
.brief-loop li:nth-child(3) .brief-step { color: var(--ok-ink); }
.brief-loop li:nth-child(4) .brief-step { color: var(--cyan-soft); }

.steps { list-style: none; margin: 0 0 18px; padding: 0; display: flex; flex-direction: column; gap: 1px; }

.step {
  position: relative;
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) 20px;
  gap: 12px;
  align-items: start;
  padding: 14px 12px 14px 10px;
  background: linear-gradient(90deg, rgba(126,181,236,0.05), rgba(126,181,236,0));
  transition: background 260ms ease, box-shadow 260ms ease, opacity 260ms ease;
}
.step-num {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  line-height: 1.4;
  color: var(--ink-faint);
  text-align: right;
  transition: color 260ms ease;
}
.step-body { min-width: 0; }
.step-title {
  margin: 0 0 5px;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
}
.step-note {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--ink-faint);
}
.step-note kbd { margin: 0 1px; }
.step-actions { margin-top: 11px; display: flex; gap: 8px; align-items: stretch; flex-wrap: wrap; }
.step-tick {
  width: 18px;
  height: 18px;
  margin-top: 1px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(126,181,236,0.25);
  position: relative;
  transition: box-shadow 260ms ease, background 260ms ease;
}

/* --- step states --------------------------------------------------------- */
.step[data-state="todo"] { opacity: 0.5; }
.step[data-state="todo"] .step-actions { display: none; }

.step[data-state="active"] {
  background: linear-gradient(90deg, rgba(240,185,11,0.13), rgba(240,185,11,0));
  box-shadow: inset 2px 0 0 var(--bnb);
}
.step[data-state="active"] .step-num { color: var(--bnb); }
.step[data-state="active"] .step-tick { box-shadow: inset 0 0 0 1px var(--edge-gold); }
.step[data-state="active"] .step-tick::after {
  content: "";
  position: absolute;
  inset: 5px;
  border-radius: 50%;
  background: var(--bnb);
  box-shadow: 0 0 10px var(--bnb);
  animation: dot-pulse 2s ease-in-out infinite;
}

.step[data-state="done"] { background: linear-gradient(90deg, rgba(53,224,161,0.08), rgba(53,224,161,0)); }
.step[data-state="done"] .step-num { color: var(--ok); }
.step[data-state="done"] .step-tick { box-shadow: inset 0 0 0 1px rgba(53,224,161,0.55); }
.step[data-state="done"] .step-tick::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 3px;
  width: 5px;
  height: 9px;
  border: solid var(--ok);
  border-width: 0 2px 2px 0;
  transform: rotate(42deg);
}
.step[data-state="done"] .step-actions { display: none; }

.step[data-state="skipped"] { opacity: 0.62; }
.step[data-state="skipped"] .step-tick::after {
  content: "";
  position: absolute;
  left: 4px;
  right: 4px;
  top: 8px;
  height: 1px;
  background: var(--ink-faint);
}
.step[data-state="skipped"] .step-actions { display: none; }

.callsign-row { align-items: stretch; }
.callsign-row input {
  flex: 1 1 200px;
  min-width: 0;
  padding: 11px 12px;
  border: 0;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  background: rgba(2, 6, 13, 0.78);
  box-shadow: inset 0 0 0 1px var(--edge);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.04em;
  transition: box-shadow 160ms ease, background 160ms ease;
}
.callsign-row input::placeholder { color: rgba(114, 134, 164, 0.7); }
.callsign-row input:hover:not(:disabled) { box-shadow: inset 0 0 0 1px var(--edge-gold); }
.callsign-row input:focus { outline: none; background: rgba(2,6,13,0.92); box-shadow: inset 0 0 0 1px var(--bnb); }
.callsign-row input:focus-visible { box-shadow: inset 0 0 0 2px var(--focus); }
.callsign-row input:disabled { opacity: 0.75; cursor: default; }
.callsign-row .btn { flex: 0 0 auto; }

.step-feedback {
  margin: 9px 0 0;
  min-height: 1em;
  font-family: var(--mono);
  font-size: 10.5px;
  line-height: 1.5;
  letter-spacing: 0.03em;
  color: var(--ink-faint);
}
.step-feedback.fb-ok      { color: var(--ok-ink); }
.step-feedback.fb-warn    { color: var(--warn-ink); }
.step-feedback.fb-pending { color: var(--bnb-pale); }
.step-feedback:empty { display: none; }

.suggests { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 9px; }
.suggests[hidden] { display: none; }
.suggests-label {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.suggest {
  padding: 5px 9px;
  border: 0;
  clip-path: polygon(5px 0, 100% 0, 100% calc(100% - 5px), calc(100% - 5px) 100%, 0 100%, 0 5px);
  background: rgba(240,185,11,0.10);
  box-shadow: inset 0 0 0 1px var(--edge-gold);
  color: var(--bnb-pale);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.03em;
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}
.suggest:hover { background: rgba(240,185,11,0.22); color: #fff3cb; }

.splash-foot {
  margin: 0;
  padding-top: 14px;
  border-top: 1px solid rgba(126,181,236,0.14);
  font-family: var(--mono);
  font-size: 9.5px;
  line-height: 1.6;
  letter-spacing: 0.02em;
  color: var(--ink-faint);
}

body.splash-open { overflow: hidden; }

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes splash-in {
  from { opacity: 0; transform: translateY(16px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

/* =============================================================================
   Entrance motion
   ============================================================================= */

@keyframes reveal {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
@keyframes drop-in {
  from { opacity: 0; transform: translateY(-14px); }
  to   { opacity: 1; transform: none; }
}
@keyframes slide-in {
  from { opacity: 0; transform: translateX(22px); }
  to   { opacity: 1; transform: none; }
}
@keyframes rise-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

.hud-top > *      { animation: drop-in 520ms cubic-bezier(0.2, 0.8, 0.3, 1) both; }
.hud-top > *:nth-child(2) { animation-delay: 80ms; }
.hud-top > *:nth-child(3) { animation-delay: 160ms; }
/* Staggered by DOM position rather than by class, so the telemetry strip and every future
   module joins the cascade automatically instead of popping in un-animated. */
.hud-rail > * { animation: slide-in 520ms cubic-bezier(0.2, 0.8, 0.3, 1) both; }
.hud-rail > *:nth-child(1) { animation-delay:  90ms; }
.hud-rail > *:nth-child(2) { animation-delay: 150ms; }
.hud-rail > *:nth-child(3) { animation-delay: 210ms; }
.hud-rail > *:nth-child(4) { animation-delay: 270ms; }
.hud-rail > *:nth-child(5) { animation-delay: 330ms; }
.hud-rail > *:nth-child(6) { animation-delay: 390ms; }
.hud-bottom > *,
.foot             { animation: rise-in 520ms cubic-bezier(0.2, 0.8, 0.3, 1) both; animation-delay: 240ms; }

/* =============================================================================
   Responsive — every breakpoint restates --game-safe-*, measured not guessed
   ============================================================================= */

/* Wide displays: give the two cells that carry sentences (the hold's state line and the nav
   note) room to say them in full. Nothing else changes — the deck never shrinks the uplink. */
@media (min-width: 1600px) {
  .hold { width: 232px; flex-basis: 232px; }
  .nav  { width: 196px; flex-basis: 196px; }
}

/* ---------------------------------------------------------------------------
   1180–1420: the deck keeps all three cells but drops a type step, and the
   status pill (which ellipsises by design) absorbs what is left.
   --------------------------------------------------------------------------- */
@media (max-width: 1420px) {
  .status-pill { max-width: 30ch; }
  .readout-value { font-size: 22px; }
  .readout-alt .readout-value { font-size: 16px; }
  .deck { gap: 11px; padding: 7px 12px; }
  .hold { width: 200px; flex-basis: 200px; }
  .nav  { width: 158px; flex-basis: 158px; }
  .hold-value { font-size: 15px; }
  .hold-units { font-size: 14px; }
}

/* 1180–1340: one more step down, so the connection pill keeps enough width to read as a
   sentence rather than "CONNECT…". */
@media (max-width: 1340px) {
  .readout-value { font-size: 20px; }
  .readout-alt .readout-value { font-size: 15px; }
  .deck-scores { min-width: 158px; gap: 12px; }
  .deck { gap: 10px; padding: 7px 11px; }
  .hold { width: 184px; flex-basis: 184px; }
  .nav  { width: 148px; flex-basis: 148px; }
  .hold-units { font-size: 13px; }
  .hold-value { font-size: 14px; }
  .status-pill { max-width: 24ch; }
}

/* ---------------------------------------------------------------------------
   ≤1180: the top strip becomes two rows — [brand | deck] then the uplink —
   rather than squeezing the deck until its numbers clip. Same pattern the phone
   layout has always used, just triggered earlier now that the deck carries the
   cargo hold. --game-safe-top is re-measured for it below.
   --------------------------------------------------------------------------- */
@media (max-width: 1180px) {
  :root {
    --rail-w: 306px;
    --pad: 12px;
    /* Two DETERMINISTIC rows, measured: a 38px identity/uplink row, then the deck on its own
       full-width row. Row 1 is height-pinned and the deck is flex-basis 100%, so the strip
       cannot silently become three rows the way a content-sized wrap did at 840px — which
       would leave --game-safe-top 50px short and hide the player's own ship. */
    --top-h: 110px;           /* 38 row 1 + 8 gap + 64 deck row */
    --game-safe-top: 122px;   /* 12 pad + 110 top strip */
    --game-safe-right: 318px; /* 12 pad + 306 rail      */
  }
  .hud-top { flex-wrap: wrap; align-content: flex-start; gap: 8px; }
  .hud-top > .brand { height: 38px; padding: 0 12px 0 10px; }
  .uplink { order: 2; flex: 1 1 90px; width: auto; height: 38px; justify-content: flex-end; }
  .hud-top > .deck { order: 3; flex: 1 0 100%; width: 100%; }
  .deck { padding: 6px 12px; }
  /* The deck now fills the row, so give the slack to the cell that carries sentences rather
     than leaving a dead strip of glass after it. */
  .nav { flex: 1 1 158px; width: auto; }
  .status-pill { flex: 1 1 40px; max-width: none; min-width: 0; }

  .strip-value { font-size: 13.5px; }
  .strip-cell { padding: 8px 8px 9px; }
  .strip-cell-chain .strip-value { font-size: 10px; }
  .badge-lay { grid-template-columns: 72px minmax(0, 1fr); gap: 11px; }
  .badge-art { width: 72px; height: 72px; }
  .btn-callsign { max-width: 22ch; }
}

@media (max-width: 980px) {
  .legend { display: none; }
  .brand-sub { display: none; }
  .chain-chip { font-size: 8px; letter-spacing: 0.1em; padding: 5px 7px 5px 6px; }
}

/* ---------------------------------------------------------------------------
   Phones: the dock becomes a bottom sheet that is CLOSED by default, so the
   game owns everything between the top strip and the control bar. It opens on
   the Ops toggle — and is force-opened whenever a cash-out voucher is on
   screen, so the Confirm button can never be behind a closed drawer.
   --------------------------------------------------------------------------- */
@media (max-width: 820px) {
  :root {
    /* Two deterministic rows, measured in a real browser at 390x844:
         row 1   32px  [brand | status pill | callsign | connect]  (height-pinned below)
         row 2  100px  the flight deck, wrapping internally into two lines
       32 + 8 gap + 100 = 140.                                                    */
    --top-h: 140px;
    --game-safe-top: 152px;   /* 12 pad + 140 top strip */
    --game-safe-right: 0px;
    /* closed sheet = 64px control bar + 12px dock padding, plus the home-indicator inset */
    --game-safe-bottom: calc(76px + env(safe-area-inset-bottom, 0px));
    --fade-h: 34px;
  }

  .hud {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas:
      "top"
      "void";
    padding-bottom: 0;
  }

  /* Row 1 is identity + uplink; the deck takes the whole of row 2 (order:3 puts it after the
     uplink even though it comes first in the DOM — the DOM order is the desktop reading order,
     where the deck sits between the brand and the uplink). Nothing here may wrap into a third
     row or --game-safe-top goes stale: the pill is the only flexible item and it shrinks. */
  .hud-top { flex-wrap: wrap; gap: 8px; align-content: flex-start; }
  .hud-top > .deck { order: 3; flex: 1 0 100%; width: 100%; }
  /* flex-basis 90px, not auto: the uplink's CONTENT is ~360px wide, and a content-sized basis
     makes flexbox break the line before it ever considers shrinking the pill — which is what
     pushed the strip to three rows and silently invalidated --game-safe-top. With a small basis
     the row never wraps and the pill (the one item designed to ellipsise) takes the squeeze. */
  .uplink { flex: 1 1 90px; width: auto; min-width: 0; height: 32px; justify-content: flex-end; gap: 7px; }
  .hud-top > .brand { height: 32px; }
  .status-pill { flex: 1 1 40px; max-width: none; min-width: 0; padding: 7px 9px 7px 25px; font-size: 9.5px; }
  #connect-btn { flex: 0 0 auto; padding: 8px 10px; font-size: 9.5px; letter-spacing: 0.06em; }
  /* The Mars mark and the BNB Chain chip are the identity that has to survive at 390px; the
     wordmark is already in the tab title, the briefing and the footer. Trading ~50px of it back
     is what keeps the status pill readable next to the connect button. */
  .brand { padding: 7px 9px; gap: 7px; }
  .brand-mark { width: 17px; height: 17px; }
  .brand-text { display: none; }
  /* Icon-only, but still a real focusable control: splash.js returns focus here after the
     briefing closes, so it must not become display:none. */
  .btn-callsign { flex: 0 0 auto; max-width: none; padding: 8px 9px; gap: 0; }
  .btn-callsign #callsign-label { display: none; }

  /* --- the deck on a phone: line A = numbers + region, line B = the hold ----- */
  .deck { flex-wrap: wrap; align-items: center; gap: 7px 10px; padding: 7px 11px 8px; }
  .deck-div { display: none; }
  .deck-scores { order: 1; flex: 0 0 auto; gap: 12px; }
  .nav {
    order: 2;
    flex: 1 1 auto;
    width: auto;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
  }
  .nav-note { display: none; }
  .hold { order: 3; flex: 1 0 100%; width: auto; gap: 5px; }
  .readout { gap: 4px; }
  .readout-value { font-size: 19px; }
  .readout-alt .readout-value { font-size: 15px; }
  .hold-units { font-size: 13px; }
  .hold-value { font-size: 14px; }
  .hold-bar { height: 8px; }
  .hold-label { font-size: 8px; letter-spacing: 0.16em; }
  .hold-state { font-size: 8.5px; }

  /* The chain chip stays visible on phones — the BNB Chain relationship is part of the
     identity, not a desktop-only flourish — but drops to the short form so the brand mod
     cannot push the uplink into a third row and invalidate --game-safe-top. */
  .chain-chip { margin-left: 1px; padding: 4px 6px 4px 5px; font-size: 7.5px; letter-spacing: 0.08em; gap: 4px; }
  .chain-pip { width: 7px; height: 7px; }

  .strip { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .strip-value { font-size: 14px; }
  .badge-lay { grid-template-columns: 66px minmax(0, 1fr); }
  .badge-art { width: 66px; height: 66px; }

  /* Overlays: narrower band, smaller banner, and the notice stack clears the control bar
     because it is anchored to --game-safe-bottom (which the open ops sheet also updates). */
  .banners { top: calc(var(--game-safe-top) + 8px); left: 10px; right: 10px; }
  .event-band { gap: 7px; padding: 6px 10px 8px; }
  .event-name { font-size: 9.5px; letter-spacing: 0.14em; }
  .event-where { font-size: 8.5px; letter-spacing: 0.08em; }
  .event-clock { font-size: 11px; }
  .fence-warn { font-size: 8.5px; letter-spacing: 0.12em; padding: 5px 9px; }
  .notices { left: 10px; right: 10px; bottom: calc(var(--game-safe-bottom) + 8px); }
  .note-toast { font-size: 10px; padding: 7px 11px; }
  /* A phone's free band is short. Two toasts at once, and only one while the ops sheet is open —
     otherwise the stack grows upward straight over the flight deck, which is the one thing on
     screen that must always be readable. (body:has, not .hud:has: .overlays is a preceding
     sibling of .hud, so it cannot be reached with a combinator from inside the HUD.) */
  .note-toast:nth-last-child(n + 3) { display: none; }
  body:has(#ops-toggle:checked) .note-toast:nth-last-child(n + 2),
  body:has(#cashout-details:not([hidden])) .note-toast:nth-last-child(n + 2) { display: none; }
  /* With the ops sheet open the visible band is ~50px tall, and a burst centred in it spills
     over the flight deck. Clamp the anchor so the card always centres somewhere it fits; the
     sheet is a menu, the deck is instrumentation. */
  .bank-burst { bottom: min(var(--game-safe-bottom), 46vh); }
  .bank-card { padding: 16px 22px 14px; }
  .bank-gain em { font-size: 32px; }
  .bank-facts { gap: 10px; font-size: 8.5px; }

  .splash { padding: 14px; align-items: start; }
  .splash-card { --cut: 16px; padding: 18px 18px 16px; width: 100%; }
  .splash-title { font-size: 21px; }
  .splash-lede { font-size: 12.5px; margin-bottom: 16px; }
  .brief-loop { padding: 10px 12px; margin-bottom: 14px; gap: 6px; }
  .brief-loop li { grid-template-columns: 58px minmax(0, 1fr); gap: 8px; font-size: 11.5px; }
  .splash-head { gap: 8px; flex-wrap: wrap; padding-bottom: 12px; margin-bottom: 14px; }
  .splash-wordmark { font-size: 15px; }
  .step { grid-template-columns: 26px minmax(0, 1fr) 18px; gap: 9px; padding: 12px 9px 12px 8px; }
  .step-actions .btn { flex: 1 1 auto; }
  .callsign-row input { flex: 1 1 100%; }

  .hud-dock {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    max-height: 76vh;
    gap: 8px;
    padding: 0 var(--pad) calc(var(--pad) + env(safe-area-inset-bottom, 0px));
  }
  .rail-shell { order: 1; display: none; }
  .foot       { order: 2; display: none; padding-bottom: 0; }
  .hud-bottom { order: 3; }

  .btn-mine { padding: 15px 18px; }
  .ops-tab { display: inline-flex; }

  /* --- sheet open ---------------------------------------------------------- */
  .hud:has(#ops-toggle:checked) .rail-shell,
  .hud:has(#cashout-details:not([hidden])) .rail-shell { display: flex; }
  .hud:has(#ops-toggle:checked) .foot,
  .hud:has(#cashout-details:not([hidden])) .foot { display: flex; }
  .hud:has(#ops-toggle:checked) .hud-dock,
  .hud:has(#cashout-details:not([hidden])) .hud-dock {
    pointer-events: auto;
    padding-top: 10px;
    background: linear-gradient(180deg,
      rgba(4, 7, 14, 0) 0%,
      rgba(4, 7, 14, 0.66) 9%,
      rgba(4, 7, 14, 0.9) 26%,
      rgba(4, 7, 14, 0.94) 100%);
  }
  .hud:has(#ops-toggle:checked) .ops-caret { transform: rotate(180deg); }

  /* The occluded band is honest about the open sheet too, for a renderer that
     re-reads (e.g. on a ResizeObserver). Closed is the value it sees at boot. */
  :root:has(#ops-toggle:checked),
  :root:has(#cashout-details:not([hidden])) { --game-safe-bottom: 76vh; }
}

/* ---------------------------------------------------------------------------
   Laptop-height compaction (1280x720 and friends).
   The rail now carries six modules. On a 720px-tall viewport the un-compacted
   stack pushed "Fetch Cash-Out Voucher" — the entry point to the money path —
   below the fold, which is the exact failure the rail's scroll affordances were
   added to paper over. Rather than rely on the affordance, tighten the rhythm
   so the whole vault module fits above the fold at 720px.
   Nothing in here changes the height of .hud-top or the control bar, so the
   --game-safe-* numbers stay true.
   --------------------------------------------------------------------------- */
@media (min-width: 821px) and (max-height: 810px) {
  .hud-rail { gap: 9px; }
  .panel { padding: 11px 13px 12px; }
  .panel-head { padding-bottom: 6px; margin-bottom: 8px; }
  .strip-cell { padding: 7px 9px 8px; gap: 4px; }
  .strip-value { font-size: 14px; }
  .pilot { grid-template-columns: 30px minmax(0, 1fr) auto; gap: 10px; }
  .pilot-sigil { width: 30px; height: 30px; }
  .pilot-name { font-size: 12.5px; }
  .vault-row { padding: 4px 0; }
  .vault-row dd { font-size: 11.5px; }
  .meter-note { margin-top: 6px; font-size: 9px; }
  .panel-copy-tight { font-size: 11px; margin-top: 9px; }
  .btn-wide { margin-top: 10px; }
  .panel-note { margin-top: 9px; font-size: 11px; }
  .board-row { padding: 5px 8px 5px 4px; }
  /* The upgrades panel is the new module above the vault, so it carries its share of the
     compaction: the effect line is a nicety, the level pips and the price are not. */
  .up { padding: 4px 4px 4px 3px; gap: 8px; }
  .up-effect { display: none; }
  .btn-up { padding: 6px 8px; min-width: 54px; }
  .rail-tabs { margin-bottom: 7px; }
  .rail-tab { padding: 6px 8px 7px; }
  .rail-shell { --tabs-h: 31px; }  /* 24 + 7, measured at 1280x720 */
}

/* Short viewports: compact the panels only. Nothing here may change the height of
   .hud-top or .hud-dock's control bar, or --game-safe-* would go stale. */
@media (max-height: 640px) {
  .panel { padding: 11px 13px 12px; }
  .hud-rail { gap: 9px; }
  .panel-copy { font-size: 11.5px; }
  .strip-cell { padding: 7px 8px 8px; }
  .up { padding: 4px 4px 4px 3px; }
  .up-effect { display: none; }
  .splash { align-items: start; padding: 12px; }
  .splash-card { padding: 16px 18px 14px; }
  .splash-title { font-size: 20px; margin-bottom: 6px; }
  .splash-lede { font-size: 12px; margin-bottom: 13px; line-height: 1.5; }
  .brief-loop { padding: 9px 11px; margin-bottom: 12px; gap: 5px; }
  .brief-loop li { font-size: 11px; line-height: 1.35; }
  .step { padding: 10px 10px 10px 8px; }
  .steps { margin-bottom: 13px; }
}

/* =============================================================================
   Reduced motion
   ============================================================================= */

@media (prefers-reduced-motion: reduce) {
  .fx-scan { animation: none; }
  .status-pill::before { animation: none !important; }
  .strip::after { animation: none !important; background: none; }
  .step[data-state="active"] .step-tick::after { animation: none !important; }
  .tick { animation: none !important; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    /* animation-fill-mode:both + a surviving delay would hold panels at opacity 0 for the
       length of the delay. Zero the delays too. */
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  /* The scroll indicator is position-driven, not time-driven: it is not "motion" a user asked
     to suppress, and a 0.001ms duration would collapse its whole range onto scroll offset 0 —
     turning the affordance into a stuck bar. Hand it back its progress-based duration. */
  .rail-bar, .rail-thumb { animation-duration: auto !important; }
}

/* ── ARI, the flight advisor ─────────────────────────────────────────────────────────────────
   Lives in the ops rail alongside the other panels, so it inherits .panel's glass treatment.
   Only the chat-specific bits are defined here. */
.npc-head { display: flex; gap: 10px; align-items: flex-start; margin-bottom: 10px; }
.npc-portrait {
  flex: 0 0 56px; width: 56px; height: 56px; border-radius: 6px; overflow: hidden;
  border: 1px solid rgba(255, 107, 44, 0.35);
  box-shadow: inset 0 0 18px rgba(255, 107, 44, 0.18);
  background: #04060c;
}
.npc-portrait-img { width: 100%; height: 100%; display: block; object-fit: cover; }
.npc-blurb { margin: 0; font-size: 11.5px; line-height: 1.45; color: var(--ink-dim, #9aa3b2); }

.npc-log {
  max-height: 190px; overflow-y: auto; display: flex; flex-direction: column; gap: 8px;
  padding: 8px; margin-bottom: 8px; font-size: 12.5px; line-height: 1.5;
  background: rgba(4, 6, 12, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.06); border-radius: 4px;
  scrollbar-width: thin;
}
.npc-log:empty { display: none; }
.npc-line { display: grid; grid-template-columns: 34px 1fr; gap: 8px; align-items: baseline; }
.npc-who {
  font-size: 9.5px; letter-spacing: 0.09em; opacity: 0.75;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.npc-user .npc-who { color: var(--cyan, #4fd6ff); }
.npc-ari .npc-who { color: var(--rust, #ff6b2c); }
.npc-text { white-space: pre-wrap; word-break: break-word; }
/* A caret while the reply streams, so a slow answer never looks like a dead panel. */
.npc-text.is-streaming::after {
  content: "▌"; margin-left: 2px; opacity: 0.8;
  animation: npc-caret 1s steps(2, start) infinite;
}
@keyframes npc-caret { 50% { opacity: 0; } }

.npc-row { display: flex; gap: 6px; }
.npc-row input {
  flex: 1 1 auto; min-width: 0; padding: 7px 9px; font: inherit; font-size: 12.5px;
  color: var(--ink, #e8edf5); background: rgba(4, 6, 12, 0.6);
  border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 3px;
}
.npc-row input:focus-visible { outline: 2px solid var(--cyan, #4fd6ff); outline-offset: 1px; }
.npc-row input:disabled { opacity: 0.5; }
.npc-row .btn { flex: 0 0 auto; }

@media (prefers-reduced-motion: reduce) {
  .npc-text.is-streaming::after { animation: none; }
}
