:root {
  --bg: #0f1419;
  --panel: #171d26;
  --panel2: #1f2733;
  --ink: #e6edf3;
  --muted: #8b97a7;
  --accent: #4ea1ff;
  --accent2: #36d7d7;
  --ok: #3fb950;
  --warn: #f0a020;
  --border: #2a3441;
  /* brand signature: deep-space amber/gold. CHROME ONLY (logo, primary CTA,
     focus) — kept clear of the functional link/node data colors above so the
     two systems never compete. The brand gold echoes the OWLT "signal" tone. */
  --brand: #F2B441;
  --brand-bright: #FFD173;
  --brand-deep: #B97E1A;
  --brand-ink: #1b1305;            /* text/icon on a gold surface */
  --brand-glow: rgba(242, 180, 65, .35);
  /* link categories (colorblind-safe Okabe–Ito trio) */
  --cat-isl: #56B4E9;      /* orbiter <-> orbiter, same body */
  --cat-access: #009E73;   /* ground  <-> orbiter, same body */
  --cat-deep: #E69F00;     /* interplanetary / cross-body / DSN */
  /* node role tints (muted; node TYPE is carried by shape) */
  --node-orbiter: #9fb3c8;
  --node-lander: #cdb4a0;
  --node-freeflyer: #b48ead;
  /* reserved single accent for SELECTION only */
  --select: #ff5db1;
  /* physical (unscheduled) state */
  --ghost: #6b7686;
}

* { box-sizing: border-box; }

html, body { margin: 0; height: 100%; }
body {
  display: flex; flex-direction: column;
  background: var(--bg); color: var(--ink);
  font: 14px/1.45 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
}
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.muted { color: var(--muted); }

/* ---- header ---------------------------------------------------------- */
header {
  padding: 10px 16px; border-bottom: 1px solid var(--border); background: var(--panel);
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
}
.brand { display: flex; align-items: center; gap: 11px; }
.brand-mark { width: 30px; height: 30px; display: block; flex: 0 0 auto; filter: drop-shadow(0 0 6px var(--brand-glow)); }
.brand-text { display: flex; flex-direction: column; line-height: 1.12; }
.brand h1 { margin: 0; font-size: 16px; font-weight: 680; letter-spacing: .01em; }
.brand .tag { color: var(--muted); font-size: 11px; letter-spacing: .02em; }

.toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; flex: 1; }

/* ---- public-demo limit banner (chrome notice; neutral, never a data hue) -- */
.demo-banner {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 7px 16px; font-size: 12.5px;
  background: var(--panel2); color: var(--muted);
  border-bottom: 1px solid var(--border);
}
.demo-banner[hidden] { display: none; }
.demo-banner b { color: var(--ink); font-weight: 600; }
.demo-banner .demo-dismiss {
  margin-left: auto; padding: 2px 8px; line-height: 1; cursor: pointer;
  background: transparent; color: var(--muted);
  border: 1px solid var(--border); border-radius: 6px;
}
.demo-banner .demo-dismiss:hover { color: var(--ink); border-color: var(--accent); }

/* ---- privacy consent banner (bottom chrome notice; mirrors .demo-banner) -- */
.consent-banner {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 7px 16px; font-size: 12.5px;
  background: var(--panel2); color: var(--muted);
  border-top: 1px solid var(--border);
  /* Above the views (vis-timeline items carry positive z-indexes that would
     otherwise paint through), below the builder/busy overlays (z 60+). */
  position: relative; z-index: 40;
}
.consent-banner[hidden] { display: none; }
.consent-banner b { color: var(--ink); font-weight: 600; }
.consent-banner a { color: var(--accent); }
.consent-banner .consent-btn {
  padding: 3px 10px; line-height: 1; cursor: pointer; font-size: 12px;
  background: transparent; color: var(--muted);
  border: 1px solid var(--border); border-radius: 6px;
}
.consent-banner .consent-btn:first-of-type { margin-left: auto; }
.consent-banner .consent-btn:hover { color: var(--ink); border-color: var(--accent); }
.consent-banner .consent-btn.primary {
  background: var(--brand); color: var(--brand-ink); border-color: var(--brand-deep);
}
.consent-banner .consent-btn.primary:hover { background: var(--brand-bright); color: var(--brand-ink); }
.toolbar .spacer { flex: 1; }
.ctl { color: var(--muted); display: flex; gap: 6px; align-items: center; font-size: 13px; }

button, select, input[type="number"] {
  background: var(--panel2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 12px; font-size: 13px; cursor: pointer;
}
button:hover { border-color: var(--accent); }
button.primary { background: var(--brand); color: var(--brand-ink); border-color: var(--brand); font-weight: 650; }
button.primary:hover { background: var(--brand-bright); border-color: var(--brand-bright); }
button.primary:active { background: var(--brand-deep); border-color: var(--brand-deep); }
button:disabled { opacity: .45; cursor: not-allowed; }
/* branded keyboard focus ring (accessibility + a consistent gold signature) */
button:focus-visible, select:focus-visible, input:focus-visible, .tab:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 1px;
}
button.mini { padding: 3px 9px; font-size: 12px; }

/* ---- toolbar dropdown (Export ▾) ------------------------------------- */
.menu { position: relative; display: inline-flex; }
.menu-pop {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 50;
  display: flex; flex-direction: column; min-width: 132px; padding: 4px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .45);
}
.menu-pop[hidden] { display: none; }
/* Examples popup lives in the narrow scenario pane and lists many entries: left-align
   it under the button (the default right-align would push off the pane) and scroll. */
#examples-menu .menu-pop { right: auto; left: 0; min-width: 200px; max-height: 320px; overflow-y: auto; }
.menu-item {
  background: transparent; border: 1px solid transparent; border-radius: 6px;
  text-align: left; padding: 6px 10px; font-size: 13px; width: 100%;
}
.menu-item:hover { background: var(--panel2); border-color: var(--accent); }
.check { display: inline-flex; align-items: center; gap: 5px; color: var(--muted); font-size: 12px; cursor: pointer; }

/* ---- layout ---------------------------------------------------------- */
main { flex: 1; display: flex; min-height: 0; }

#left {
  width: 32%; min-width: 280px; max-width: 540px;
  display: flex; flex-direction: column; padding: 12px;
  background: var(--panel);
}
#right { flex: 1; display: flex; flex-direction: column; min-width: 0; }

/* ---- resizable gutters ----------------------------------------------- */
/* Thin draggable bars between panes; the divider line lives in ::before so
   the hit area can be a few px wider than the visible 1px rule. Sizes are
   driven from layout.js (inline styles); double-click a gutter to reset. */
.gutter { flex: 0 0 6px; position: relative; z-index: 5; background: transparent; }
.gutter::before { content: ""; position: absolute; background: var(--border); transition: background .12s; }
.gutter:hover::before, .gutter.dragging::before { background: var(--accent); }
.gutter-v { cursor: col-resize; }
.gutter-v::before { top: 0; bottom: 0; left: 50%; width: 1px; transform: translateX(-.5px); }
.gutter-h { cursor: row-resize; margin: 0 14px; }
.gutter-h::before { left: 0; right: 0; top: 50%; height: 1px; transform: translateY(-.5px); }

/* while dragging, force the resize cursor everywhere and kill text/SVG selection */
body.resizing-x, body.resizing-x * { cursor: col-resize !important; user-select: none !important; }
body.resizing-y, body.resizing-y * { cursor: row-resize !important; user-select: none !important; }

h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin: 6px 0 8px; }
h2 small { text-transform: none; letter-spacing: 0; color: var(--muted); font-weight: 400; }

/* Syntax-highlighted YAML editor: a transparent <textarea> stacked over a
   colored <pre>. Both layers MUST share font / padding / wrapping or the colors
   drift out from under the caret — so the shared box rules live in one selector. */
.yaml-editor {
  position: relative; flex: 1; min-height: 0;
  background: #0b0f14; border: 1px solid var(--border); border-radius: 6px;
  overflow: hidden;
}
.yaml-editor > #yaml,
.yaml-editor > .yaml-highlight {
  position: absolute; inset: 0; margin: 0; border: 0; border-radius: 0;
  padding: 10px; font: 12.5px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  tab-size: 2; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word;
}
.yaml-editor > .yaml-highlight {
  z-index: 0; pointer-events: none; overflow: hidden; color: var(--ink); background: transparent;
}
.yaml-editor > .yaml-highlight code { font: inherit; }
.yaml-editor > #yaml {
  z-index: 1; resize: none; width: 100%; background: transparent;
  color: transparent; caret-color: var(--ink);
}
#yaml::placeholder { color: var(--muted); }
#yaml::selection { background: rgba(78, 161, 255, .30); }
/* YAML token colors */
.yk-key { color: var(--accent); }
.yk-str { color: var(--cat-access); }
.yk-num { color: var(--brand-bright); }
.yk-bool { color: var(--node-freeflyer); }
.yk-comment { color: var(--muted); font-style: italic; }
.yk-punct { color: var(--muted); }

/* scenario-panel collapse toggle (⟨/⟩) — lives in the right-pane tab bar so it
   stays reachable while the panel is hidden; flips body.left-collapsed below. */
.tab-toggle { height: 26px; padding: 0 9px; margin-right: 6px; color: var(--muted); border-color: var(--border); border-radius: 6px; }
.tab-toggle:hover { color: var(--ink); border-color: var(--accent); }

/* collapsed scenario panel: animate #left shut (right-to-left) and hand the full
   width to the viewers. !important overrides any inline width left by a gutter
   drag (layout.js); the gutter is pointless once collapsed, so hide it too. */
#left { transition: width .16s ease, padding .16s ease; }
body.left-collapsed #left {
  width: 0 !important; min-width: 0 !important; max-width: 0 !important;
  padding: 0; border-right: none; overflow: hidden;
}
body.left-collapsed #gutter-main { display: none; }

/* ---- narrow / mobile viewports -------------------------------------- */
/* On a phone there's no room for the parenthetical pane descriptions or the
   mouse-interaction hints, and they crowd the small pane heads. Hide them so the
   titles, controls and the views themselves get the space. The YAML band also
   starts collapsed on mobile (see initLeftToggle in app.js). */
@media (max-width: 760px) {
  .pane-head small,
  .hint { display: none; }
}

/* ---- status + selection --------------------------------------------- */
.status { margin: 10px 0; min-height: 36px; font-size: 13px; }
.badge { display: inline-block; padding: 2px 9px; border-radius: 999px; font-weight: 600; font-size: 12px; }
.badge.ok { background: rgba(63,185,80,.15); color: var(--ok); }
.badge.warn { background: rgba(240,160,32,.15); color: var(--warn); }
.status .err { color: #ff7b72; white-space: pre-wrap; }
.status .line { margin-top: 6px; color: var(--muted); }

.selection {
  background: var(--panel2); border: 1px solid var(--border); border-radius: 6px;
  padding: 10px; font-size: 13px;
}
.sel-title { font-weight: 650; margin-bottom: 6px; }
.sel-title.node { color: var(--ok); }
.sel-title.body { color: var(--accent); }
.sel-title.link { color: var(--accent2); }
.sel-row { display: flex; justify-content: space-between; gap: 12px; padding: 2px 0; color: var(--muted); }
.sel-row b { color: var(--ink); font-weight: 600; }

/* ---- tabs ------------------------------------------------------------ */
.tabs { display: flex; align-items: center; gap: 4px; padding: 8px 12px 0; border-bottom: 1px solid var(--border); background: var(--panel); }
.tab {
  background: transparent; border: 1px solid transparent; border-bottom: none;
  border-radius: 6px 6px 0 0; padding: 7px 14px; color: var(--muted); font-size: 13px;
}
.tab.active { background: var(--bg); color: var(--ink); border-color: var(--border); }

.tab-panel { flex: 1; min-height: 0; display: none; }
.tab-panel.active { display: flex; flex-direction: column; }

/* ---- playbar --------------------------------------------------------- */
#playbar {
  flex: 0 0 auto; display: flex; align-items: center; gap: 10px;
  padding: 8px 14px; border-bottom: 1px solid var(--border); background: var(--panel);
}
#ph-play { width: 34px; text-align: center; padding: 5px 0; }
#ph-slider { flex: 1; cursor: pointer; accent-color: var(--accent); }
#ph-time { min-width: 64px; }

/* ---- dynamic 2D (orrery) --------------------------------------------- */
.playbar {
  flex: 0 0 auto; display: flex; align-items: center; gap: 10px;
  padding: 8px 14px; border-bottom: 1px solid var(--border); background: var(--panel);
}
#or-play { width: 34px; text-align: center; padding: 5px 0; }
#or-slider { flex: 1; cursor: pointer; accent-color: var(--accent); }
#or-time { min-width: 64px; }

#orrery-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 8px 14px; }
#orrery-stage { position: relative; flex: 1; min-height: 0; }
#orrery {
  position: absolute; inset: 0; width: 100%; height: 100%;
  background: radial-gradient(ellipse at center, #131a24 0%, #0c1118 100%);
  border: 1px solid var(--border); border-radius: 6px; cursor: grab;
}
#orrery:active { cursor: grabbing; }

/* ---- legend ---------------------------------------------------------- */
.legend-bar {
  flex: 0 0 auto; display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 5px 14px; border-bottom: 1px solid var(--border); background: var(--panel);
  font-size: 11px; color: var(--muted);
}
.lg { display: inline-flex; align-items: center; gap: 5px; }
.lg-sep { width: 1px; height: 14px; background: var(--border); }
.sw { display: inline-block; width: 16px; height: 0; border-top: 3px solid; }
.sw-isl { border-color: var(--cat-isl); }
.sw-access { border-color: var(--cat-access); }
.sw-deep { border-color: var(--cat-deep); }
.sw-ghost { border-top: 2px dashed var(--ghost); }
.mk { display: inline-block; width: 9px; height: 9px; }
.mk-orbiter { background: var(--node-orbiter); border-radius: 50%; }
.mk-lander { background: var(--node-lander); }
.mk-freeflyer { background: var(--node-freeflyer); transform: rotate(45deg); }

/* active toggle (razor) + razor cursor over contacts */
button.mini.active { background: var(--accent); color: #06121f; border-color: var(--accent); font-weight: 600; }
#timeline-wrap.razor .vis-item { cursor: crosshair; }

/* ---- statistics ------------------------------------------------------ */
#stats-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 8px 14px; }
.stats-body { flex: 1; min-height: 0; overflow: auto; padding: 8px 2px 16px; }
.stats-section-title { margin: 18px 0 8px; font-size: 13px; font-weight: 600; color: var(--ink); }
.stats-section-title:first-child { margin-top: 4px; }
.stats-section-title small { font-weight: 400; }

.stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 10px; }
.stat-card { background: var(--panel2); border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; }
.stat-card .k { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
.stat-card .v { font-size: 20px; font-weight: 700; margin-top: 4px; line-height: 1.2; word-break: break-word; }
.stat-card .sub { color: var(--muted); font-size: 11px; margin-top: 3px; }

.stats-dist { background: var(--panel2); border: 1px solid var(--border); border-radius: 10px; padding: 6px 12px; }
.stats-dist .sel-row b { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.stats-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.stats-table th, .stats-table td { text-align: right; padding: 6px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.stats-table th { color: var(--muted); font-weight: 600; position: sticky; top: 0; background: var(--bg); }
.stats-table th:first-child, .stats-table td:first-child { text-align: left; }
.stats-table tbody tr.stats-node-row,
.stats-table tbody tr.stats-pair-row { cursor: pointer; }
.stats-table tbody tr.stats-node-row:hover,
.stats-table tbody tr.stats-pair-row:hover { background: var(--panel2); }
.stats-table tbody tr.stats-node-row.sel,
.stats-table tbody tr.stats-pair-row.sel { background: rgba(255, 93, 177, .14); }
.cat-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; vertical-align: middle; }

/* ---- panes ----------------------------------------------------------- */
.pane-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.pane-actions { display: flex; align-items: center; gap: 8px; }
.pane-actions .muted { font-size: 11px; }

#schematic-wrap { flex: 1 1 44%; min-height: 0; display: flex; flex-direction: column; padding: 8px 14px; }
#schematic {
  flex: 1; min-height: 0; width: 100%;
  background: radial-gradient(ellipse at center, #131a24 0%, #0c1118 100%);
  border: 1px solid var(--border); border-radius: 6px; cursor: grab;
}
#schematic:active { cursor: grabbing; }
#schematic-spacing { width: 84px; accent-color: var(--accent); cursor: pointer; }

#timeline-wrap { flex: 1 1 56%; min-height: 0; display: flex; flex-direction: column; padding: 8px 14px; position: relative; }
#timeline { flex: 1; min-height: 0; }

/* ---- schematic SVG --------------------------------------------------- */
.orbit { fill: none; stroke: #26303d; stroke-width: 1; }
.orbit-moon { stroke: #222b36; stroke-dasharray: 2 3; }
.body { stroke: rgba(0,0,0,.35); cursor: pointer; }
.body-star { fill: #f6c64b; }
.body-planet { fill: #4a78c0; }
.body-moon { fill: #9aa7b5; }
.body:hover { stroke: var(--accent); stroke-width: 2; }
.node-marker { cursor: pointer; }
.node-orbiter { fill: var(--node-orbiter); }
.node-lander { fill: var(--node-lander); }
.node-freeflyer { fill: var(--node-freeflyer); }
.node-marker:hover { stroke: var(--ink); stroke-width: 1.5; }
.lbl { fill: #c7d2e0; font-size: 11px; }
.lbl-sm { fill: #8b97a7; font-size: 9px; }
/* leader line from a de-cluttered (displaced) label back to its marker */
.lbl-leader { stroke: #4b5666; stroke-width: 1; vector-effect: non-scaling-stroke; opacity: .7; }

/* visible link strokes are decorative; clicks are caught by .link-hit so that
   node markers keep their own clicks near the link endpoints */
.link { fill: none; stroke-width: 1.6; vector-effect: non-scaling-stroke; pointer-events: none; }
.link-glow { fill: none; stroke-width: 5; opacity: .18; vector-effect: non-scaling-stroke; pointer-events: none; }
.link.link-isl, .link-glow.link-isl { stroke: var(--cat-isl); }
.link.link-access, .link-glow.link-access { stroke: var(--cat-access); }
.link.link-deep, .link-glow.link-deep { stroke: var(--cat-deep); }
/* A little transparency on the lit signal stroke: the forward (tx->rx) and
   return (rx->tx) envelopes ride the same line, so where they overlap it reads
   brighter — you can tell when a link's return leg is also propagating back. */
.link.link-isl, .link.link-access, .link.link-deep { stroke-opacity: .6; }
/* physical-but-unscheduled LOS: subtle gray, behind the colored logical links */
.link.link-ghost { stroke: var(--ghost); stroke-width: 1; stroke-dasharray: 3 3; opacity: .55; }
.link-glow.link-ghost { stroke: none; }
/* wide invisible click target for each link */
.link-hit { fill: none; stroke: transparent; stroke-width: 12; vector-effect: non-scaling-stroke; cursor: pointer; }
/* selected link highlight */
.link-sel { fill: none; stroke: var(--select); stroke-width: 3; opacity: .9; vector-effect: non-scaling-stroke; pointer-events: none; }
.sel-ring { fill: none; stroke: var(--select); stroke-width: 2; vector-effect: non-scaling-stroke; pointer-events: none; }

/* ---- timeline overlay (guides + OWLT) -------------------------------- */
.tl-overlay { position: absolute; pointer-events: none; z-index: 4; overflow: visible; }
/* concurrency guides, painted inside the vis center panel so the contact bars
   render over them (top-left origin; no z-index -> kept behind by DOM order) */
.tl-guide-layer { position: absolute; left: 0; top: 0; pointer-events: none; overflow: visible; }
.tl-guide { stroke: #3a4757; stroke-width: 1; opacity: .35; }
/* OWLT reception marker: brand "signal" gold — reads on both the light category
   bars and the dark gaps between contacts (was near-black, invisible on the gap). */
.tl-owlt-link { stroke: var(--brand); stroke-width: 1; stroke-dasharray: 2 2; opacity: .9; }
.tl-owlt-tick { stroke: var(--brand-bright); stroke-width: 1.5; opacity: 1; }
/* Apparent (T2) reception that lands PAST the contact bar's right edge: brighter +
   solid, so the "reception continues past the window" overhang reads at a glance. */
.tl-owlt-link.tl-rx-past { stroke: var(--brand-bright); stroke-width: 1.6; stroke-dasharray: none; opacity: 1; }

/* vis-timeline dark overrides */
.vis-timeline { border: 1px solid var(--border); border-radius: 6px; }
.vis-item { color: #06121f; font-size: 11px; border-width: 1px; }
/* contact items colored by link category (matches the schematic links) */
.vis-item.cat-isl { background-color: var(--cat-isl); border-color: #7cc6f0; }
.vis-item.cat-access { background-color: var(--cat-access); border-color: #3cc59c; }
.vis-item.cat-deep { background-color: var(--cat-deep); border-color: #f3bb45; }
/* Transit-only ("ghost") apparent contact: keeps its category hue but a dashed
   bright outline + glow marks a contact with NO instant of simultaneous line of
   sight — one that exists only over the air (the headline apparent effect). */
.vis-item.transit-only {
  border-style: dashed; border-color: var(--brand-bright);
  box-shadow: 0 0 0 1px var(--brand-glow), 0 0 7px var(--brand-glow);
}
/* Designer-ASSERTED contact (no physical LOS backing: a manual add outside every
   pass of its pair, or a pair that never has line of sight). Fully editable and
   exported — the flag is an audit cue, not a block. Category hue kept, with a
   dark diagonal hatch + neutral dashed outline; distinct from .transit-only
   (dashed GOLD + glow = a real apparent-physics contact). */
.vis-item.asserted {
  border-style: dashed; border-color: rgba(230, 237, 243, .75);
  background-image: repeating-linear-gradient(45deg, rgba(6, 18, 31, .38) 0 4px, transparent 4px 8px);
}
/* Per-direction sub-rows (a pair split into A→B / B→A when OWLT exceeds the threshold):
   a small leading arrow marks the transmit direction; A→B sits above B→A in the group.
   NO padding on these items: they carry no text (the arrow is absolutely positioned),
   and with box-sizing:border-box any padding FLOORS the rendered width at padding +
   borders — a contact shrunk toward the 1s minimum then paints ~15px wide, its right
   edge overhanging its true end by hours' worth of pixels at day-scale zoom (looked
   like the contact end drifting past where it should be, "fixed" by zooming in). */
.vis-item.dir-ab::before, .vis-item.dir-ba::before {
  content: "\25B8"; position: absolute; left: 3px; top: 50%; transform: translateY(-50%);
  font-size: 9px; line-height: 1; opacity: .9; pointer-events: none;
}
.vis-item.dir-ba::before { content: "\25C2"; }

/* ---- timeline category / system filter ------------------------------- */
.tl-filter { display: inline-flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.tl-sep { width: 1px; align-self: stretch; margin: 2px 4px; background: var(--border); }
.tl-filter .chip {
  font: inherit; font-size: 11px; line-height: 1; padding: 3px 8px; border-radius: 11px;
  border: 1px solid var(--border); background: var(--panel2); color: var(--muted);
  cursor: pointer; user-select: none;
}
.tl-filter .chip[aria-pressed="true"].cat-isl    { background: var(--cat-isl);    border-color: var(--cat-isl);    color: #06121c; }
.tl-filter .chip[aria-pressed="true"].cat-access { background: var(--cat-access); border-color: var(--cat-access); color: #04140d; }
.tl-filter .chip[aria-pressed="true"].cat-deep   { background: var(--cat-deep);   border-color: var(--cat-deep);   color: #1d1404; }
.tl-filter .chip[aria-pressed="false"] { opacity: .5; }
.tl-filter select {
  font: inherit; font-size: 11px; padding: 2px 6px; border-radius: 6px;
  background: var(--panel2); color: var(--ink); border: 1px solid var(--border);
}
/* selection is a reserved outline, so it never recolors the category */
.vis-item.vis-selected { box-shadow: 0 0 0 2px var(--select); border-color: var(--select); }
/* per-item delete control (shown on a selected, removable contact): a small
   circular × badge floating at the top-right corner — subtle until hover, where
   it warms to the destructive red. Overrides vis-timeline's default red square.
   The #timeline prefix raises specificity above vis's runtime-injected CSS. */
#timeline .vis-item .vis-delete {
  position: absolute; top: -7px; right: -7px; width: 16px; height: 16px;
  padding: 0; box-sizing: border-box;
  background: rgba(6, 18, 31, .6); border: 1px solid rgba(255, 255, 255, .35);
  border-radius: 50%; cursor: pointer; opacity: .55;
  transition: opacity .15s ease, background-color .15s ease, border-color .15s ease, transform .15s ease;
}
#timeline .vis-item .vis-delete::after {
  content: "\00D7";                 /* × glyph; replaces vis's default red box */
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; font-size: 12px; font-weight: 700; line-height: 1;
  color: #fff;
}
#timeline .vis-item.vis-selected .vis-delete { opacity: .9; }
#timeline .vis-item .vis-delete:hover {
  opacity: 1; background: #ff7b72; border-color: #ff7b72; transform: scale(1.12);
}
#timeline .vis-item .vis-delete:hover::after { color: #fff; }
/* physical contact plan: gray "ghost" behind the colored logical items */
.vis-item.vis-background.phys-ghost { background-color: rgba(107, 118, 134, .18); border-left: 1px solid rgba(107,118,134,.4); border-right: 1px solid rgba(107,118,134,.4); }
.vis-labelset .vis-label, .vis-foreground .vis-group { border-color: var(--border); }
.vis-time-axis .vis-text, .vis-labelset .vis-label { color: var(--muted); }
.vis-panel.vis-center, .vis-panel.vis-left, .vis-panel.vis-top, .vis-panel.vis-bottom { border-color: var(--border); }
.vis-custom-time.playhead { background-color: var(--accent2); width: 2px; cursor: ew-resize; }

/* ---- 3D view (CesiumJS) ---------------------------------------------- */
#tdv-play { width: 34px; text-align: center; padding: 5px 0; }
#tdv-slider { flex: 1; cursor: pointer; accent-color: var(--accent); }
#tdv-time { min-width: 64px; }

#cesium-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 8px 14px; }
#cesium-stage { position: relative; flex: 1; min-height: 0; }
#cesium {
  position: absolute; inset: 0;
  border: 1px solid var(--border); border-radius: 6px; overflow: hidden;
  background: radial-gradient(ellipse at center, #0a1018 0%, #05070b 100%);
}
/* the CesiumJS attribution credit stays visible (Apache-2.0 requirement) */
#cesium .cesium-widget-credits { font-size: 10px; opacity: .6; }
/* empty/error state painted into #cesium by cesium3d.js before a scene exists */
#cesium .placeholder {
  position: absolute; inset: 0; display: flex;
  align-items: center; justify-content: center; flex-direction: column; gap: 6px;
  text-align: center; padding: 16px;
}
#cesium .placeholder h2 { margin: 0; font-size: 15px; color: var(--text); }
#cesium .placeholder .err { color: var(--bad, #e06c75); }

.cesium-hint {
  position: absolute; inset: 0; display: none;
  align-items: center; justify-content: center; flex-direction: column; gap: 6px;
  text-align: center; padding: 24px; color: var(--muted);
  pointer-events: none; font-size: 13px;
}
.cesium-hint .err { color: #ff7b72; }

/* on-canvas focus toolbar — centers the view on a body, a node, or the whole
   scene. Shared by the 3D view (#cesium-focus) and the 2D Dynamic view
   (#orrery-focus). */
.focus-bar {
  position: absolute; top: 8px; left: 8px; z-index: 5; display: none;
  flex-wrap: wrap; gap: 5px; max-width: calc(100% - 16px);
  pointer-events: none;   /* the bar gap is click-through; only the buttons catch clicks */
}
.focus-bar .focus-btn {
  pointer-events: auto; cursor: pointer;
  font-size: 11px; font-weight: 500; line-height: 1;
  padding: 5px 9px; border-radius: 5px;
  color: var(--ink); background: rgba(12, 18, 28, .72);
  border: 1px solid var(--border);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.focus-bar .focus-btn:hover { background: rgba(30, 42, 60, .92); border-color: var(--accent); }
/* nodes read a touch lighter than bodies so the two groups are distinguishable */
.focus-bar .focus-btn.focus-node { color: var(--muted); font-weight: 400; }
.focus-bar .focus-btn.focus-node:hover { color: var(--ink); }

/* ---- dialog ---------------------------------------------------------- */
.dialog { background: var(--panel); color: var(--ink); border: 1px solid var(--border); border-radius: 10px; padding: 18px; min-width: 300px; }
.dialog::backdrop { background: rgba(0,0,0,.5); }
.dialog h3 { margin: 0 0 12px; font-size: 15px; }
.field { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin: 8px 0; font-size: 13px; color: var(--muted); }
.field select, .field input { min-width: 160px; }
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }
.dialog-hint { margin: 0 0 10px; font-size: 12px; }

/* ---- busy overlay (load / design) ------------------------------------ */
/* Semi-transparent so the app stays visible behind it; blocks pointer events
   so a long compute can't be double-triggered. z-index sits above everything
   (builder overlay is 60). The spinner is transform-animated, so it keeps
   turning smoothly even if the main thread briefly blocks on a render. */
#busy {
  position: fixed; inset: 0; z-index: 200;
  display: none; align-items: center; justify-content: center;
  background: rgba(8, 12, 18, .5);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
#busy.show { display: flex; }
.busy-card {
  display: flex; align-items: center; gap: 14px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  padding: 16px 22px; box-shadow: 0 12px 40px rgba(0, 0, 0, .5);
}
.busy-spinner {
  width: 24px; height: 24px; flex: 0 0 auto; border-radius: 50%;
  border: 3px solid var(--border); border-top-color: var(--accent);
  animation: busy-spin .8s linear infinite;
}
.busy-msg { font-size: 14px; color: var(--ink); }
@keyframes busy-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .busy-spinner { animation-duration: 2.4s; } }

/* ---- scenario builder overlay ---------------------------------------- */
/* Full-height guided form. Shown by clearing the [hidden] attribute; the
   :not([hidden]) guard lets the UA `display:none` win while hidden. */
.builder-overlay {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(4, 7, 11, .66);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  align-items: center; justify-content: center; padding: 2vh 2vw;
}
.builder-overlay:not([hidden]) { display: flex; }
.builder-frame {
  width: min(1240px, 96vw); height: min(940px, 96vh);
  display: flex; flex-direction: column; min-height: 0;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: 12px; box-shadow: 0 24px 60px rgba(0,0,0,.55); overflow: hidden;
}
.builder-head, .builder-foot {
  display: flex; align-items: center; gap: 12px; padding: 12px 18px;
  background: var(--panel2);
}
.builder-head { border-bottom: 1px solid var(--border); }
.builder-foot { border-top: 1px solid var(--border); }
.builder-head .brand h1 { font-size: 16px; }
.builder-main { flex: 1; min-height: 0; display: grid; grid-template-columns: 1fr 420px; }
.builder-form { min-height: 0; overflow-y: auto; padding: 14px 18px 28px; }
.builder-side {
  min-height: 0; display: flex; flex-direction: column;
  border-left: 1px solid var(--border); background: #0c1118; padding: 10px 14px 14px;
}
.yaml-preview {
  flex: 1; min-height: 80px; margin: 0 0 10px; overflow: auto;
  background: #0b0f14; color: var(--ink); border: 1px solid var(--border);
  border-radius: 6px; padding: 10px; font-size: 12px; line-height: 1.5;
  white-space: pre; tab-size: 2;
}
.builder-problems { flex: 0 1 34%; min-height: 40px; overflow: auto; font-size: 12.5px; }
.builder-problems .prob-head { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; margin: 2px 0 4px; }
.builder-problems .prob { display: flex; gap: 8px; padding: 4px 0; color: #ff9b92; }
.builder-problems .prob::before { content: "▸"; color: var(--warn); }
.builder-problems .prob.ok { color: var(--ok); }
.builder-problems .prob.ok::before { content: "✓"; color: var(--ok); }
.builder-problems .prob.linked { cursor: pointer; border-radius: 4px; }
.builder-problems .prob.linked:hover { color: #ffb3ab; text-decoration: underline; }
.builder-problems .prob.linked:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#builder-valid.badge.ok { background: rgba(63,185,80,.15); color: var(--ok); }
#builder-valid.badge.warn { background: rgba(240,160,32,.15); color: var(--warn); }

/* builder sections */
.b-section { margin: 0 0 16px; }
.b-section > .b-sec-head {
  display: flex; align-items: center; gap: 10px; margin: 0 0 8px;
  font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted);
}
.b-section > .b-sec-head .count { color: var(--ink); font-weight: 600; }
.b-sec-head .spacer { flex: 1; }

/* quick-add + template chips */
.b-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.b-chip {
  background: var(--panel2); color: var(--ink); border: 1px solid var(--border);
  border-radius: 999px; padding: 5px 11px; font-size: 12px; cursor: pointer;
}
.b-chip:hover { border-color: var(--accent); }
.b-chip.add { border-style: dashed; color: var(--muted); }
.b-chip.add:hover { color: var(--ink); }

/* stacked field (label above control) packed into a responsive grid */
.b-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 8px 10px; }
.b-grid.wide { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); }
.b-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.b-field > label { font-size: 11px; color: var(--muted); }
.b-field input, .b-field select {
  width: 100%; padding: 5px 8px; font-size: 12.5px;
  background: var(--panel2); color: var(--ink); border: 1px solid var(--border); border-radius: 6px;
}
.b-field input:focus, .b-field select:focus { outline: none; border-color: var(--accent); }
.b-field.err input, .b-field.err select { border-color: #ff7b72; }
.b-field input::placeholder { color: #5a6573; }
.field-help { font-size: 11px; color: var(--muted); }
.field-err { font-size: 10.5px; color: #ff7b72; }
.b-field.col-2 { grid-column: span 2; }

/* node / constellation / body cards */
.b-card {
  border: 1px solid var(--border); border-radius: 9px; background: var(--panel2);
  margin: 0 0 9px; overflow: hidden;
}
.b-card-head {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px; cursor: pointer;
  background: rgba(255,255,255,.015);
}
.b-card-head:hover { background: rgba(78,161,255,.06); }
.b-card-head:focus-visible, .seg button:focus-visible, .b-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.b-card-head .twirl { color: var(--muted); width: 12px; transition: transform .12s; }
.b-card.collapsed .twirl { transform: rotate(-90deg); }
.b-card-head .b-title { font-weight: 600; font-size: 13px; }
.b-card-head .b-badge {
  font-size: 10.5px; padding: 1px 7px; border-radius: 999px; color: #06121f; font-weight: 600;
}
.b-badge.orbiter { background: var(--node-orbiter); }
.b-badge.lander { background: var(--node-lander); }
.b-badge.freeflyer { background: var(--node-freeflyer); }
.b-badge.constellation { background: var(--cat-isl); }
.b-badge.body { background: var(--accent); }
.b-card-head .sub { color: var(--muted); font-size: 11.5px; }
.b-card-head .spacer { flex: 1; }
.b-card-head .icon-btn {
  border: 1px solid transparent; background: transparent; color: var(--muted);
  padding: 2px 7px; font-size: 12px; border-radius: 5px;
}
.b-card-head .icon-btn:hover { color: var(--ink); border-color: var(--border); background: var(--panel); }
.b-card-head .icon-btn.del:hover { color: #ff7b72; border-color: #ff7b72; }
.b-card-body { padding: 10px; border-top: 1px solid var(--border); }
.b-card.collapsed .b-card-body { display: none; }
.b-card.invalid { border-color: rgba(255,123,114,.5); }

.b-subhead { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin: 10px 0 6px; }
.b-subhead:first-child { margin-top: 0; }

/* segmented control (type, XOR toggles) */
.seg { display: inline-flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.seg button {
  border: none; border-radius: 0; background: var(--panel); color: var(--muted);
  padding: 4px 10px; font-size: 12px; border-right: 1px solid var(--border);
}
.seg button:last-child { border-right: none; }
.seg button.on { background: var(--accent); color: #06121f; font-weight: 600; }
.seg button:hover:not(.on) { color: var(--ink); }
.seg-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 4px; }
.seg-row > .seg-label { font-size: 11px; color: var(--muted); min-width: 54px; }

/* collapsible <details> groups inside a card */
.b-details { margin-top: 10px; border-top: 1px dashed var(--border); padding-top: 8px; }
.b-details > summary { cursor: pointer; font-size: 11.5px; color: var(--muted); list-style: none; user-select: none; }
.b-details > summary::-webkit-details-marker { display: none; }
.b-details > summary::before { content: "▸ "; }
.b-details[open] > summary::before { content: "▾ "; }
.b-details > summary .tag { color: var(--ink); font-weight: 600; }
.b-details > .b-grid, .b-details > .seg-row { margin-top: 8px; }

@media (max-width: 860px) {
  /* stack the columns, but keep each a bounded, independently-scrolling row so
     the form never pushes the YAML preview + validation panel off-screen */
  .builder-main { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1.5fr) minmax(0, 1fr); }
  .builder-side { border-left: none; border-top: 1px solid var(--border); }
}

/* ---- help dialog ----------------------------------------------------- */
.help-dialog { max-width: 600px; }
.help-body { max-height: 68vh; overflow-y: auto; padding-right: 4px; }
.help-section { margin: 0 0 16px; }
.help-section:last-child { margin-bottom: 0; }
.help-section h4 { margin: 0 0 6px; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--accent); }
.help-section p { margin: 0 0 6px; color: var(--ink); line-height: 1.55; }
.help-section a { color: var(--accent); text-decoration: none; }
.help-section a:hover { color: var(--brand-bright); text-decoration: underline; }
.help-steps { margin: 0; padding-left: 18px; }
.help-steps li { margin: 5px 0; color: var(--ink); line-height: 1.5; }
.help-dialog b { color: var(--ink); font-weight: 600; }
.help-dialog .badge { vertical-align: baseline; }

/* Supporter / funding logos — footer of the help dialog. The updated marks are
   light/dark-friendly, so they float directly on the dark dialog (no plate).
   Each logo sits in an equal, aspect-fitted cell so marks of very different
   shapes read at a comparable visual size on one row. */
.supporters-footer {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}
.supporters {
  display: flex; align-items: center;
  gap: 16px;
}
.supporter {
  flex: 1 1 0; min-width: 0; height: 44px;
  display: flex; align-items: center; justify-content: center;
  transition: transform .12s ease, opacity .12s ease;
}
.supporter:hover { transform: translateY(-1px); opacity: .8; }
.supporters img { max-width: 100%; max-height: 100%; width: auto; height: auto; display: block; }

/* ♥ Support toolbar button + donate dialog — DISABLED on main / the deployed build
   (markup commented out in index.html; initSupport() in app.js; CPD_DONATE in
   config.js). Re-enable all of them together.
.support-btn .heart { color: var(--brand); }
.support-btn:hover .heart { color: var(--brand-bright); }
.support-dialog { max-width: 460px; }
.support-body p { margin: 0 0 10px; color: var(--ink); line-height: 1.55; }
.support-dialog b { color: var(--ink); font-weight: 600; }
.donate-links { display: flex; flex-direction: column; gap: 10px; margin: 14px 0 4px; }
.donate-btn {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 14px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--panel2);
  color: var(--ink); text-decoration: none;
  transition: border-color .12s, background .12s;
}
.donate-btn:hover { background: #1d2530; }
.donate-btn.cafecito:hover { border-color: var(--brand); }
.donate-btn.sponsors:hover { border-color: #db61a2; }
.donate-btn .donate-ico { font-size: 22px; line-height: 1; flex: 0 0 auto; }
.donate-btn .donate-txt { display: flex; flex-direction: column; gap: 1px; }
.donate-btn .donate-txt small { color: var(--muted); font-size: 11px; }
*/
