:root {
  --bg0: #0a1018;
  --bg1: #121a22;
  --fg: #e8eef2;
  --muted: #8aa0ad;
  --accent: #f5c518; /* FR24-class yellow */
  --accent-dim: #c9a012;
  --ok: #3ecf8e;
  --warn: #e0a35c;
  --line: #243240;
  --panel: rgba(18, 26, 34, 0.92);
  --ocean: #0a1628;
  --land: #1a2420;
  --mono: "IBM Plex Mono", "SF Mono", ui-monospace, monospace;
  --sans: "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;
}

* { box-sizing: border-box; }
html, body {
  margin: 0;
  height: 100%;
  background: #0a1018;
  color: var(--fg);
  font-family: var(--sans);
}

.shell {
  display: grid;
  grid-template-rows: auto 1fr auto;
  height: 100%;
  min-height: 100vh;
  min-height: 100dvh;
}

/*
 * Full-screen manifold: viewport is the product; side panel optional.
 *
 * CRITICAL LAYOUT LAW — do NOT use `grid-template-columns: 0 1fr` when
 * `.panel { display: none }`. With the panel removed from the grid, #viewport
 * becomes the sole item and occupies column 1 (the 0px track). overflow:hidden
 * then clips canvas + HUD → blank black page (proved 2026-07-25).
 */
body.manifold-viewer .shell,
body.fullscreen-manifold .shell {
  grid-template-rows: 1fr;
}

body.manifold-viewer header,
body.manifold-viewer footer,
body.fullscreen-manifold header,
body.fullscreen-manifold footer {
  display: none;
}

body.manifold-viewer main,
body.fullscreen-manifold main {
  grid-template-columns: 1fr;
  min-height: 100%;
  height: 100%;
}

body.manifold-viewer .panel,
body.fullscreen-manifold .panel {
  display: none;
}

body.manifold-viewer #viewport,
body.fullscreen-manifold #viewport {
  min-height: 100vh;
  min-height: 100dvh;
  height: 100%;
  width: 100%;
  min-width: 100%;
}

/* Body-level chrome — never clipped by #viewport overflow (WebGL fail / layout shear) */
#openusd-surface-chrome {
  position: fixed;
  left: 12px;
  bottom: 48px;
  top: auto;
  z-index: 100;
  pointer-events: none;
  max-width: min(420px, 70vw);
  font-family: var(--sans);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}

/* Once ATC map chrome mounts, keep only the action strip (avoid title overlap) */
body.manifold-viewer #openusd-surface-chrome .surface-title,
body.manifold-viewer #openusd-surface-chrome .surface-status {
  display: none;
}

#openusd-surface-chrome .surface-title {
  font: 650 14px/1.3 var(--sans);
  color: #f0f4f8;
}

#openusd-surface-chrome .surface-status {
  margin-top: 3px;
  font: 11px/1.35 var(--mono);
  color: #f5c518;
}

#openusd-surface-chrome .surface-actions {
  margin-top: 8px;
  display: flex;
  gap: 6px;
  pointer-events: auto;
}

#openusd-surface-chrome .surface-actions button {
  width: auto;
  min-width: 36px;
  padding: 0.35rem 0.55rem;
  font-size: 0.8rem;
  background: rgba(18, 26, 34, 0.92);
  color: var(--fg);
  border: 1px solid var(--line);
}

#openusd-surface-chrome .surface-actions button:hover {
  border-color: var(--accent);
  color: var(--accent);
}

body.show-chrome.fullscreen-manifold .panel,
body.show-chrome.manifold-viewer .panel {
  display: block;
}

body.show-chrome.fullscreen-manifold main,
body.show-chrome.manifold-viewer main {
  grid-template-columns: minmax(240px, 300px) 1fr;
}

body.show-chrome.fullscreen-manifold header,
body.show-chrome.manifold-viewer header {
  display: flex;
}

body.show-chrome.fullscreen-manifold .shell,
body.show-chrome.manifold-viewer .shell {
  grid-template-rows: auto 1fr auto;
}

body.show-chrome.fullscreen-manifold footer,
body.show-chrome.manifold-viewer footer {
  display: block;
}

header {
  padding: 0.65rem 1rem;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  align-items: center;
  justify-content: space-between;
  background: #0e141c;
}

header h1 {
  margin: 0;
  font-size: 1.05rem;
  letter-spacing: 0.03em;
  font-weight: 650;
}

header h1 .brand-yellow { color: var(--accent); }

header .tag {
  color: var(--muted);
  font-size: 0.8rem;
}

main {
  display: grid;
  grid-template-columns: minmax(260px, 320px) 1fr;
  min-height: 0;
}

@media (max-width: 900px) {
  main { grid-template-columns: 1fr; }
}

.panel {
  border-right: 1px solid var(--line);
  padding: 0.85rem;
  overflow: auto;
  background: var(--panel);
}

.panel h2 {
  margin: 0.85rem 0 0.55rem;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
}

.panel h2:first-child { margin-top: 0; }

label {
  display: block;
  font-size: 0.72rem;
  color: var(--muted);
  margin: 0.5rem 0 0.2rem;
}

input, select, textarea, button {
  font: inherit;
  width: 100%;
}

input, select, textarea {
  background: #0b1115;
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0.4rem 0.5rem;
}

textarea {
  min-height: 70px;
  font-family: var(--mono);
  font-size: 0.75rem;
}

.row {
  display: flex;
  gap: 0.4rem;
  margin-top: 0.55rem;
  flex-wrap: wrap;
}

.row button { flex: 1; min-width: 0; }

.airport-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.35rem;
  margin-top: 0.45rem;
}

.airport-grid button {
  font-size: 0.78rem;
  padding: 0.4rem 0.35rem;
}

button {
  background: var(--accent);
  color: #1a1400;
  border: none;
  border-radius: 4px;
  padding: 0.5rem 0.65rem;
  font-weight: 650;
  cursor: pointer;
}

button.secondary {
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--line);
}

button.active-airport {
  background: var(--accent);
  color: #1a1400;
  box-shadow: 0 0 0 1px var(--accent-dim);
}

button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

#viewport {
  position: relative;
  min-height: 420px;
  background: var(--ocean);
  overflow: hidden;
  width: 100%;
  height: 100%;
}

/* Compact chrome toggle (panel still reachable with ?chrome=1) */
body:not(.show-chrome):not(.manifold-viewer) main {
  grid-template-columns: minmax(200px, 260px) 1fr;
}

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

.map-chrome {
  position: absolute;
  z-index: 3;
  pointer-events: none;
}

.map-chrome.top-left {
  top: 10px;
  left: 12px;
  pointer-events: none;
}

.map-title {
  font: 650 13px/1.3 var(--sans);
  color: #f0f4f8;
  text-shadow: 0 1px 4px rgba(0,0,0,0.85);
}

.map-sub {
  font: 11px/1.35 var(--mono);
  color: #a8b8c4;
  margin-top: 2px;
}

.map-band {
  margin-top: 6px;
  display: inline-block;
  font: 650 10px/1.3 var(--mono);
  letter-spacing: 0.04em;
  color: #0a1018;
  background: var(--accent);
  padding: 2px 7px;
  border-radius: 3px;
  text-shadow: none;
}

.band-legend {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.25rem 0.5rem;
  margin: 0.45rem 0 0.35rem;
  font: 10px/1.3 var(--mono);
  color: var(--muted);
}

.band-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  margin-right: 4px;
  vertical-align: middle;
}
.band-dot.hemi { background: #3a6a8a; }
.band-dot.reg { background: #5a9ab0; }
.band-dot.metro { background: #7ec8a0; }
.band-dot.walk { background: #f5c518; }

#viewport[data-walk-mode="1"] {
  box-shadow: inset 0 0 0 1px rgba(245, 197, 24, 0.35);
}

.warn-panel {
  position: absolute;
  top: 10px;
  right: 56px;
  z-index: 5;
  width: min(320px, 42vw);
  max-height: 42%;
  overflow: auto;
  background: rgba(10, 16, 24, 0.9);
  border: 1px solid #2a3a44;
  border-radius: 4px;
  padding: 8px 10px;
  font: 11px/1.35 var(--mono);
  color: #c8d0d6;
  pointer-events: auto;
}

.warn-panel-title {
  font-weight: 700;
  letter-spacing: 0.06em;
  color: #e8eef2;
  margin-bottom: 4px;
}

.warn-badge {
  display: inline-block;
  min-width: 1.4em;
  text-align: center;
  background: #243240;
  color: #e8eef2;
  border-radius: 3px;
  padding: 0 5px;
  margin-left: 4px;
}
.warn-badge.sev-critical { background: #ff3344; color: #fff; }
.warn-badge.sev-high { background: #ff7a2e; color: #1a1000; }
.warn-badge.sev-med { background: #e0a35c; color: #1a1000; }

.warn-panel-meta {
  color: #8aa0ad;
  font-size: 10px;
  margin-bottom: 6px;
}

.warn-row {
  border-left: 2px solid #3a4a58;
  padding: 3px 0 3px 6px;
  margin-bottom: 4px;
  color: #b8c8d4;
}
.warn-row .sev {
  font-weight: 700;
  margin-right: 4px;
}
.warn-row.sev-critical { border-color: #ff3344; color: #ffc8ce; }
.warn-row.sev-high { border-color: #ff7a2e; color: #ffd2b0; }
.warn-row.sev-medium { border-color: #e0a35c; color: #f0d8b0; }
.warn-row.sev-low { border-color: #8aa0ad; }

.zoom-stack {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 4;
  display: flex;
  flex-direction: column;
  gap: 4px;
  pointer-events: auto;
}

.zoom-stack button {
  width: 36px;
  height: 36px;
  padding: 0;
  border-radius: 4px;
  background: rgba(18, 26, 34, 0.9);
  color: var(--fg);
  border: 1px solid var(--line);
  font-size: 1.1rem;
  font-weight: 700;
}

.zoom-stack button:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.log {
  margin-top: 0.65rem;
  padding: 0.55rem;
  background: #0b1115;
  border: 1px solid var(--line);
  border-radius: 4px;
  font-family: var(--mono);
  font-size: 0.7rem;
  white-space: pre-wrap;
  max-height: 180px;
  overflow: auto;
  color: var(--muted);
}

footer {
  border-top: 1px solid var(--line);
  padding: 0.5rem 1rem;
  font-size: 0.72rem;
  color: var(--muted);
  background: #0e141c;
}

.ok { color: var(--ok); }
.err { color: var(--warn); }

.hint {
  margin: 0.3rem 0 0.55rem;
  font-size: 0.7rem;
  color: var(--muted);
  line-height: 1.35;
}

.nav-hint {
  border-left: 2px solid var(--accent);
  padding-left: 0.45rem;
  color: #b8c8d4;
}

.nav-hint strong { color: var(--accent); font-weight: 650; }

#openusd-live-hud {
  border-color: #3a4a38 !important;
}

/* Always-visible UUM-8D ATC twin chrome (airport + manifold views + wx) */
.atc-twin-chrome {
  position: absolute;
  left: 12px;
  top: 78px;
  z-index: 6;
  width: min(520px, 92vw);
  pointer-events: auto;
  background: rgba(10, 16, 24, 0.9);
  border: 1px solid #2a3a44;
  border-radius: 4px;
  padding: 8px 10px 10px;
  font: 11px/1.35 var(--mono);
  color: #c8d0d6;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.45);
}

.atc-twin-chrome .twin-label {
  font: 650 10px/1.3 var(--sans);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #8aa0ad;
  margin: 4px 0 5px;
}

.atc-twin-chrome .twin-c4 {
  text-transform: none;
  letter-spacing: 0;
  color: #f5c518;
  font-weight: 600;
  margin-left: 6px;
}

.twin-airports,
.twin-views {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 6px;
}

.twin-airports button,
.twin-views button {
  width: auto;
  min-width: 52px;
  padding: 0.35rem 0.45rem;
  font: 650 11px/1 var(--mono);
  background: rgba(18, 26, 34, 0.95);
  color: #e8eef2;
  border: 1px solid #2a3a44;
  border-radius: 3px;
  cursor: pointer;
}

.twin-views button {
  min-width: 0;
  font-size: 10px;
  letter-spacing: 0.02em;
}

.twin-airports button:hover,
.twin-views button:hover {
  border-color: #f5c518;
  color: #f5c518;
}

.twin-airports button.active,
.twin-views button.active {
  background: #f5c518;
  color: #0a1018;
  border-color: #f5c518;
}

.twin-wx {
  margin-top: 4px;
  padding: 6px 8px;
  background: rgba(8, 14, 20, 0.85);
  border: 1px solid #3a4a38;
  border-radius: 3px;
  color: #b8c8d4;
  font-size: 10px;
}

.twin-wx .wx-status {
  color: #3ecf8e;
  font-weight: 700;
}

.twin-wx .wx-metar {
  color: #d0d8de;
}

.twin-wx .wx-goes {
  color: #e0a35c;
}

.twin-doctrine {
  margin-top: 6px;
  padding: 5px 7px;
  border-left: 2px solid #e0a35c;
  color: #e0a35c;
  font: 600 9px/1.35 var(--mono);
  letter-spacing: 0.02em;
}

@media (max-width: 720px) {
  .atc-twin-chrome {
    top: auto;
    bottom: 56px;
    left: 8px;
    width: calc(100vw - 16px);
    max-height: 38vh;
    overflow: auto;
  }
  .warn-panel {
    top: 8px;
    right: 8px;
    width: min(280px, 48vw);
    max-height: 28%;
  }
}

/* ── Data freshness ──────────────────────────────────────────────────────────
   Measured on the live apex 2026-07-27: swiftGen=60492ms, c4stitch=2.0ms,
   warn=29 — a minute-old picture driving 29 separation alerts, with nothing on
   screen saying so. Separation is computed FROM those positions, so a stale
   feed invalidates the alerts while they keep rendering as though current.
   Freshness is therefore styled as a primary signal, not a debug field. */

/* The HUD sets `color` in inline style.cssText, which outranks a stylesheet
   rule — hence !important here. The id is openusd-live-hud (set in makeHud). */
#openusd-live-hud[data-feed-state] { transition: color .2s ease; }
#openusd-live-hud[data-feed-state="LIVE"]    { color: #9fe8bf !important; }
#openusd-live-hud[data-feed-state="WARMING"] { color: #9bb4c4 !important; }
#openusd-live-hud[data-feed-state="LAGGING"] { color: #ffc46b !important; }
#openusd-live-hud[data-feed-state="STALE"] {
  color: #ffd0c9 !important;
  background: rgba(70, 18, 14, .92) !important;
  border-color: #8c2f28 !important;
}

/* The warnings panel must not look authoritative when its inputs are not. */
.warn-panel[data-feed-state="LAGGING"] { border-color: #7a5a1e; }
.warn-panel[data-feed-state="STALE"] {
  border-color: #8c2f28;
  box-shadow: inset 0 0 0 9999px rgba(140, 47, 40, .10);
}
.warn-panel[data-feed-state="STALE"] .warn-panel-list,
.warn-panel[data-feed-state="STALE"] .warn-panel-meta { opacity: .62; }

/* Dim the count too — a bright red "29" on stale data reads as 29 live
   conflicts, which is the specific misreading this prevents. */
.warn-badge[data-feed-state="STALE"] { filter: grayscale(.55) brightness(.85); }

.warn-panel-meta[data-stale-note]::after {
  content: attr(data-stale-note);
  display: block;
  margin-top: 4px;
  padding: 3px 6px;
  border-left: 3px solid #ff6a5a;
  background: rgba(255, 106, 90, .12);
  color: #ffb3a8;
  font-weight: 600;
  letter-spacing: .01em;
  white-space: normal;
}

/* STALE gets an unmissable strip across the panel head. */
.warn-panel[data-feed-state="STALE"] .warn-panel-title::before {
  content: "STALE FEED";
  display: block;
  margin: -2px -2px 6px;
  padding: 3px 6px;
  background: #8c2f28;
  color: #ffe6e2;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .09em;
  text-align: center;
  border-radius: 2px;
}

/* ── Structured separation rows ──────────────────────────────────────────────
   Rows were a single concatenated monospace string, so every alert read at the
   same visual weight and the severity was printed twice ("MEDIUM MEDIUM …").
   A controller surface has to answer "which one first?" at a glance, so the
   row is now: severity chip · callsign pair · measurements · band. */

.warn-row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  padding: 5px 7px;
  border-left-width: 3px;
  border-left-style: solid;
  line-height: 1.4;
}
.warn-row + .warn-row { margin-top: 3px; }

.warn-row .sev {
  flex: 0 0 auto;
  min-width: 58px;
  padding: 1px 5px;
  border-radius: 2px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .08em;
  text-align: center;
}
.warn-row.sev-critical .sev { background: #ff3344; color: #fff; }
.warn-row.sev-high     .sev { background: #ff7a2e; color: #1a1000; }
.warn-row.sev-medium   .sev { background: #e0a35c; color: #1a1000; }
.warn-row.sev-low      .sev { background: #46586a; color: #cfe0ec; }

/* The pair is the identity of the conflict — it carries the most weight. */
.warn-pair {
  flex: 0 0 auto;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  letter-spacing: .01em;
}

/* Measurements: number loud, unit quiet, minimum quieter still. */
.warn-detail { flex: 1 1 100%; font-variant-numeric: tabular-nums; opacity: .95; }
.warn-detail b { font-weight: 700; font-size: 12px; }
.warn-detail i { font-style: normal; opacity: .62; margin-left: 1px; font-size: 10px; }
.warn-detail u {
  text-decoration: none; opacity: .45; margin-left: 4px; font-size: 9.5px;
}
.warn-detail s { display: inline-block; width: 10px; text-decoration: none; }
.warn-msg { opacity: .82; }

.warn-row em {
  font-style: normal; opacity: .5; font-size: 9.5px;
  letter-spacing: .05em; text-transform: uppercase;
}

/* CRITICAL must not be findable only by reading — give it a tint too. */
.warn-row.sev-critical { background: rgba(255, 51, 68, .10); }
.warn-row.sev-high     { background: rgba(255, 122, 46, .07); }
/* A measurement that actually breaches minima must not look like one that does not. */
.warn-detail b.breach { color: #ff8a7a; }

/* ── Warning panel layout corrections ────────────────────────────────────────
   Measured on a 752x1058 viewport 2026-07-27: rows overlapped and the last row
   was clipped by the panel edge. Two causes, both mine.

   1. `.warn-detail { flex: 1 1 100% }` forced the measurements onto their own
      line, so every row was two lines tall and the content outgrew the clip
      before the scroll region engaged. The detail now flows beside the pair and
      wraps only when it genuinely needs to.
   2. The panel had no row gap and no content-driven height, so a wrapped row
      visually collided with the next one. */

.warn-detail {
  flex: 1 1 auto;
  min-width: 0;           /* without this a long detail refuses to shrink and pushes the row wide */
}

.warn-panel {
  /* Give the list room before it scrolls; a 2-row clip on a 5-alert panel hides
     the alerts that matter. */
  max-height: min(58%, 420px) !important;
  width: min(360px, 46vw) !important;
}

.warn-panel-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Rows must never overlap: own their box, and let text wrap inside it. */
.warn-row {
  align-items: baseline;
  row-gap: 2px;
  overflow-wrap: anywhere;
}

/* ── Bottom-left collision ───────────────────────────────────────────────────
   The live HUD sits at bottom:8px and so does the mode-button row, so the HUD
   text ran underneath "Panel / HEMI / REG / METRO / WALK". Lift the HUD clear of
   the control row rather than shrinking the text, because the HUD is the thing
   an operator reads for freshness. */
#openusd-live-hud {
  bottom: 46px !important;
  max-width: min(72ch, 78%) !important;
}
