/* GeologiQ HUD design system — tokens + shared chrome. GMUNK/Oblivion direction: deep navy space,
   ice/blue-grey chrome, dot-grid textures, hairlines. Halliburton RED kept as the accent.
   Per-feature layout stays in scene.css; this file owns the look. */
@font-face { font-family: "Rajdhani"; font-weight: 400; font-display: swap; src: url("/public/vendor/fonts/rajdhani-400.woff2") format("woff2"); }
@font-face { font-family: "Rajdhani"; font-weight: 600; font-display: swap; src: url("/public/vendor/fonts/rajdhani-600.woff2") format("woff2"); }
@font-face { font-family: "Share Tech Mono"; font-weight: 400; font-display: swap; src: url("/public/vendor/fonts/sharetechmono-400.woff2") format("woff2"); }

/* Tokens live in hud-theme.css — this file spells out the PARTS, that one the values, and the split
   is what lets a theme switch animate instead of requiring a second stylesheet. Anything hardcoded
   here is either brand-pinned (the boot cinematic's #cc0000) or genuinely geometric. */

/* Panel chrome: glass fill, hairline, one notched corner, bracket ticks, faint glow. */
.hud-panel { position: relative; background-color: var(--hud-glass); background-image: var(--hud-dotgrid); background-size: 14px 14px; color: var(--hud-ink);
  border: 1px solid var(--hud-line); border-radius: var(--hud-radius);
  /* Five points at every theme — the notch shrinks to 0px rather than the shape becoming `none`,
     which is the difference between the corner sliding square and it popping. */
  clip-path: polygon(0 0, calc(100% - var(--hud-notch)) 0, 100% var(--hud-notch), 100% 100%, 0 100%);
  backdrop-filter: blur(var(--hud-blur)); box-shadow: var(--hud-shadow);
  font-family: var(--hud-font); }
.hud-panel::before, .hud-panel::after { content: ""; position: absolute; width: 10px; height: 10px; pointer-events: none;
  opacity: var(--hud-ornament); }
.hud-panel::before { left: 0; top: 0; border-left: var(--hud-bracket) solid var(--hud-accent-dim); border-top: var(--hud-bracket) solid var(--hud-accent-dim); }
.hud-panel::after { right: 0; bottom: 0; border-right: var(--hud-bracket) solid var(--hud-accent-dim); border-bottom: var(--hud-bracket) solid var(--hud-accent-dim); }

/* Three title levels, because one 10px uppercase style for panels, sections and rows alike is
   exactly the flatness that made the hierarchy hard to read. Panel > section > row. */
.hud-title { font-family: var(--hud-font); font-weight: var(--hud-weight); text-transform: var(--hud-case);
  font-size: 11px; letter-spacing: var(--hud-tracking); color: var(--hud-ink); }
.hud-title::before { content: "▮ "; color: var(--hud-hot); font-size: 0.8em; opacity: var(--hud-ornament); }
.hud-subtitle { font-family: var(--hud-font); font-weight: var(--hud-weight); text-transform: var(--hud-case);
  font-size: 10px; letter-spacing: var(--hud-tracking); color: var(--hud-dim); }

.hud-btn { background: var(--hud-btn-bg); color: var(--hud-ink); border: 1px solid var(--hud-line);
  border-radius: var(--hud-radius-sm); padding: 6px 12px; cursor: pointer; font-family: var(--hud-font);
  font-weight: var(--hud-weight); font-size: 12px; letter-spacing: var(--hud-tracking-ui); text-transform: var(--hud-case);
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s, color 0.15s; }
.hud-btn:hover { border-color: var(--hud-accent); background: var(--hud-accent-05); box-shadow: 0 0 10px var(--hud-accent-dim), inset 0 0 8px var(--hud-accent-05); }
.hud-btn:active { border-color: var(--hud-hot); color: var(--hud-hot); }
.hud-btn:disabled { opacity: 0.4; cursor: default; box-shadow: none; }
/* Pressed/current state, distinct from hover — a toggle that is ON has to look different from a
   toggle the pointer happens to be over, and nothing in the HUD said which was which. */
.hud-btn[aria-pressed="true"] { background: var(--hud-hot); border-color: var(--hud-hot); color: var(--hud-bg); box-shadow: var(--hud-glow); }

/* Keyboard focus. There was none anywhere in the HUD: every control was reachable by Tab and
   invisible once reached. :focus-visible so a mouse press never draws the ring. */
:where(.hud-btn, .hud-select, .hud-check, .hud-range, .ctx-item, .dock-row-head, .rail-caret, .theme-opt):focus-visible,
:where(.rail-row, .facet-row):focus-within {
  outline: 2px solid var(--hud-ring); outline-offset: 2px; border-radius: var(--hud-radius-sm); }

/* ---- Theme switch (scene-theme.ts) — a segmented control in the top bar. Classic radio-group
   behaviour (one option pressed, arrows move within it) rather than a cycling button, so the
   available themes are VISIBLE instead of discovered by clicking.

   Lives HERE, not in hud-theme.css: that file is tokens only and is linked FIRST, so a component
   rule there loses every tie to hud.css — `.hud-panel { position: relative }` beat this box's
   `position: fixed` on source order alone and the switch laid out in the document flow, off the
   bottom of the page. Values in one file, parts in the other, with no exceptions.

   right: 173px clears the STORYBOARD button (24px margin + its ~133px) so the two read as one
   top-right cluster; the whole right column below is the well planner. */
.theme-switch { position: fixed; top: 24px; right: 173px; z-index: 12; display: flex; gap: 2px;
  padding: 3px; align-items: center; }
.theme-opt { background: transparent; color: var(--hud-dim); border: 0; cursor: pointer;
  font-family: var(--hud-font); font-weight: var(--hud-weight); font-size: 10px;
  letter-spacing: var(--hud-tracking-ui); text-transform: var(--hud-case);
  padding: 5px 10px; border-radius: var(--hud-radius-sm);
  transition: background var(--hud-fast) ease, color var(--hud-fast) ease; }
.theme-opt:hover { color: var(--hud-ink); background: var(--hud-accent-05); }
.theme-opt[aria-pressed="true"] { color: var(--hud-bg); background: var(--hud-hot); box-shadow: var(--hud-glow); }

/* De-natived select: square, with a two-gradient chevron (CSP-safe, no image assets). */
.hud-select { appearance: none; -webkit-appearance: none; background: var(--hud-glass); color: var(--hud-ink);
  backdrop-filter: blur(var(--hud-blur)); border: 1px solid var(--hud-line); border-radius: var(--hud-radius-sm); padding: 5px 22px 5px 8px;
  font-family: var(--hud-font); font-size: 12px; letter-spacing: 0.06em;
  background-image: linear-gradient(45deg, transparent 50%, var(--hud-ink) 50%), linear-gradient(135deg, var(--hud-ink) 50%, transparent 50%);
  background-position: calc(100% - 13px) 55%, calc(100% - 9px) 55%; background-size: 4px 4px; background-repeat: no-repeat; }
.hud-select:hover { border-color: var(--hud-accent); }

/* Checkboxes: square hairline frame; checked = "armed" — red core inside a dark gap ring. */
.hud-check { appearance: none; -webkit-appearance: none; width: 12px; height: 12px; margin: 0;
  border: 1px solid var(--hud-accent-dim); border-radius: var(--hud-radius-sm); background: transparent; cursor: pointer; flex: none; }
.hud-check:hover { border-color: var(--hud-accent); }
.hud-check:checked { accent-color: var(--hud-hot); border-color: var(--hud-hot); background: var(--hud-hot);
  box-shadow: inset 0 0 0 2px var(--hud-bg), var(--hud-glow); }

/* Range inputs: 1px sharp track; glass thumb — hairline outline in the track colour, faint
   translucent fill, backdrop blur of whatever sits behind it. Brand red while dragging. */
.hud-range { appearance: none; -webkit-appearance: none; height: 18px; background: transparent; }
.hud-range::-webkit-slider-runnable-track { height: 1px; background: var(--hud-accent-50); }
.hud-range::-webkit-slider-thumb { -webkit-appearance: none; width: 7px; height: 14px; margin-top: -6.5px;
  border: 1px solid var(--hud-accent-50); background: var(--hud-accent-12);
  backdrop-filter: blur(4px); border-radius: var(--hud-radius-sm); }
.hud-range:active::-webkit-slider-thumb { border-color: var(--hud-hot); background: var(--hud-hot-35); box-shadow: 0 0 8px var(--hud-hot); }
.hud-range::-moz-range-track { height: 1px; background: var(--hud-accent-50); }
.hud-range::-moz-range-thumb { width: 7px; height: 14px; border: 1px solid var(--hud-accent-50);
  background: var(--hud-accent-12); backdrop-filter: blur(4px); border-radius: var(--hud-radius-sm); }
.hud-range:active::-moz-range-thumb { border-color: var(--hud-hot); background: var(--hud-hot-35); box-shadow: 0 0 8px var(--hud-hot); }

.hud-readout { font-family: var(--hud-mono); font-variant-numeric: tabular-nums; color: var(--hud-accent); }

/* Entrance: slide in + CRT power-on strike (discrete opacity steps read as a tube striking),
   staggered by .hud-enter-N. hud-strike is listed last so it owns opacity during the strike.
   The reveal is GATED on the boot overlay: while #loading is up, panels stay hidden — the whole
   sequence only plays once the overlay lifts (hideLoading re-triggers .hud-enter), so it is
   actually SEEN instead of racing the page paint. */
body:has(#loading:not(.hidden)) .hud-enter { visibility: hidden; animation-play-state: paused; }
.hud-enter { animation: hud-in 0.7s cubic-bezier(0.2, 0.9, 0.3, 1) backwards, hud-strike 0.9s steps(1, end) backwards; }
.hud-enter-1 { animation-delay: 0.15s; } .hud-enter-2 { animation-delay: 0.7s; }
.hud-enter-3 { animation-delay: 0.95s; } .hud-enter-4 { animation-delay: 1.2s; }
@keyframes hud-in {
  0% { opacity: 0; transform: translateY(6px); }
  60% { opacity: 1; box-shadow: 0 0 22px var(--hud-accent-dim); }
  100% { opacity: 1; transform: translateY(0); }
}
@keyframes hud-strike {
  0% { opacity: 0; }
  45% { opacity: 0; }
  50% { opacity: 1; }
  58% { opacity: 0.3; }
  64% { opacity: 1; }
  72% { opacity: 0.55; }
  78%, 100% { opacity: 1; }
}

/* Rail reveal: the outline unfolds like a striking tube — a horizontal line that snaps open
   vertically — then every element inside pops scale(0,0)->scale(1,1) top-to-bottom. */
.rail { transform-origin: 18px 14px; }
.rail.hud-enter { animation: rail-unfold 0.9s cubic-bezier(0.25, 0.9, 0.3, 1) 0.5s backwards, hud-strike 1.1s steps(1, end) 0.5s backwards; }
@keyframes rail-unfold {
  0% { transform: scale(0, 0.035); }
  40% { transform: scale(1.03, 0.035); }
  55% { transform: scale(1, 0.25); }
  100% { transform: scale(1, 1); }
}
@keyframes hud-pop {
  0% { transform: scale(0, 0); opacity: 0; filter: brightness(2.2); }
  55% { transform: scale(1.08, 0.65); opacity: 1; filter: brightness(1.6); }
  70% { transform: scale(0.96, 1.05); filter: none; }
  82% { transform: scale(1.02, 0.98); filter: brightness(1.25); }
  100% { transform: scale(1, 1); filter: none; }
}
.hud-enter > * { animation: hud-pop 0.34s cubic-bezier(0.3, 1.1, 0.4, 1) backwards; transform-origin: left center; }
.hud-enter > *:nth-child(1) { animation-delay: 1.4s; }  .hud-enter > *:nth-child(2) { animation-delay: 1.52s; }
.hud-enter > *:nth-child(3) { animation-delay: 1.64s; } .hud-enter > *:nth-child(4) { animation-delay: 1.76s; }
.hud-enter > *:nth-child(5) { animation-delay: 1.88s; } .hud-enter > *:nth-child(6) { animation-delay: 2s; }
.hud-enter > *:nth-child(7) { animation-delay: 2.12s; } .hud-enter > *:nth-child(8) { animation-delay: 2.24s; }
.hud-enter > *:nth-child(9) { animation-delay: 2.36s; } .hud-enter > *:nth-child(n+10) { animation-delay: 2.48s; }
/* STATUS rows are their own entities in the reveal: keyed to .hud-enter (so the dive re-trigger
   replays them) and sequenced AFTER their section frame pops (~2s), one per 80ms. Category rows
   (.rail-cats) keep the quick insertion stagger below — they appear on caret-expand, not reveal. */
.hud-enter .rail-section .facet-row { animation: hud-pop 0.3s cubic-bezier(0.3, 1.1, 0.4, 1) backwards; transform-origin: left center; }
.hud-enter .rail-section .facet-row:nth-child(1) { animation-delay: 2.4s; }
.hud-enter .rail-section .facet-row:nth-child(2) { animation-delay: 2.48s; }
.hud-enter .rail-section .facet-row:nth-child(3) { animation-delay: 2.56s; }
.hud-enter .rail-section .facet-row:nth-child(4) { animation-delay: 2.64s; }
.hud-enter .rail-section .facet-row:nth-child(5) { animation-delay: 2.72s; }
.hud-enter .rail-section .facet-row:nth-child(6) { animation-delay: 2.8s; }
.hud-enter .rail-section .facet-row:nth-child(7) { animation-delay: 2.88s; }
.hud-enter .rail-section .facet-row:nth-child(8) { animation-delay: 2.96s; }
.hud-enter .rail-section .facet-row:nth-child(9) { animation-delay: 3.04s; }
.hud-enter .rail-section .facet-row:nth-child(10) { animation-delay: 3.12s; }
.hud-enter .rail-section .facet-row:nth-child(n+11) { animation-delay: 3.2s; }
/* Status/category rows insert AFTER data loads — they pop in as the data arrives, staggered. */
.facet-row { animation: hud-pop 0.24s cubic-bezier(0.3, 1.1, 0.4, 1) backwards; transform-origin: left center; }
.facet-row:nth-child(2) { animation-delay: 0.04s; } .facet-row:nth-child(3) { animation-delay: 0.08s; }
.facet-row:nth-child(4) { animation-delay: 0.12s; } .facet-row:nth-child(5) { animation-delay: 0.16s; }
.facet-row:nth-child(6) { animation-delay: 0.2s; }  .facet-row:nth-child(7) { animation-delay: 0.24s; }
.facet-row:nth-child(8) { animation-delay: 0.28s; } .facet-row:nth-child(9) { animation-delay: 0.32s; }
.facet-row:nth-child(n+10) { animation-delay: 0.36s; }

/* Interaction glitch: one quick burst on hover/press — never ambient. */
@keyframes hud-glitch-hit {
  0%, 100% { filter: none; text-shadow: none; }
  20% { filter: brightness(0.5); text-shadow: -2px 0 var(--hud-hot-80), 2px 0 var(--hud-accent-50); }
  45% { filter: none; text-shadow: none; }
  70% { filter: brightness(1.6); text-shadow: 1px 0 var(--hud-hot-60); }
}
.hud-btn:hover, .hud-select:hover, .hud-check:hover { animation: hud-glitch-hit 0.28s steps(1, end) 1; }
.hud-btn:active, .hud-range:active { animation: hud-glitch-hit 0.22s steps(1, end) 1; }

/* Left-rail menu rows: a plain, reliable mouse-over — a faint tint + a brand-red edge tick +
   the label brightening to accent. No glitch animation (its dark frame made rows vanish in
   Safari); pure state changes so a row can never blink out on hover. */
.rail-row, .facet-row { transition: background 0.12s ease, box-shadow 0.12s ease; }
.rail-row:hover, .facet-row:hover { background: var(--hud-accent-05); box-shadow: inset 2px 0 0 var(--hud-hot); }
.rail-row:hover .rail-name, .facet-row:hover .facet-name { color: var(--hud-accent); }

/* Faint rolling scanlines over the HUD layer (pointer-events none; rides the HUD toggle). */
.hud-overlay::after { content: ""; position: absolute; inset: -3px 0 0 0; pointer-events: none; opacity: var(--hud-ornament);
  background: repeating-linear-gradient(0deg, var(--hud-accent-05) 0 1px, transparent 1px 3px);
  animation: hud-scan 8s linear infinite; }
@keyframes hud-scan { to { transform: translateY(3px); } }

/* Boot cinematic: the HALLIBURTON wordmark (inlined SVG in .brand, which sits ABOVE the boot
   overlay) starts large centre-screen, DRAWS its letter outlines along the combined path
   (pathLength=1 dash reveal), fills brand red, then FLIES to its top-left slot as the overlay
   lifts and the preloaded 3D scene fades in beneath it. GEOLOGIQ fades in once it lands.
   Boot-only rules key off body:not(.booted) — dives re-show the overlay, never the cinematic. */
.brand-svg { overflow: visible; /* leaders draw outside the viewBox */
  transform: translate(calc(50vw - 50% - 24px), calc(44vh - 50% - 27px)) scale(1.033); } /* held centre-screen at the drift's END scale (no snap-back), drains in place */
body:not(.booted) .brand-svg {
  /* scale(~1) = the svg's NATIVE 72px layout size: WebKit rasterizes layers at layout size
     (scaling a small logo UP blurs in Safari; the corner is the scaled-DOWN state instead).
     No filter here either — a filter flattens the layer the same way; the glow lives on the
     overlay (.loading::before). */
  transform: translate(calc(50vw - 50% - 24px), calc(44vh - 50% - 27px)) scale(1);
  transform-origin: center; /* pure zoom drift; interpolates to the corner's top-left in flight */
  transition: none; /* the flight animates on .booted, never back */
  animation: boot-drift 7.5s linear both; /* slow push-in while the letters trace */
}
.loading::before { content: ""; position: absolute; left: 50%; top: 44vh; width: 70vw; height: 34vh;
  transform: translate(-50%, -50%); pointer-events: none;
  background: radial-gradient(ellipse closest-side, rgba(204, 0, 0, 0.13), transparent 72%); }
@keyframes boot-drift {
  from { transform: translate(calc(50vw - 50% - 24px), calc(44vh - 50% - 27px)) scale(0.97); }
  to { transform: translate(calc(50vw - 50% - 24px), calc(44vh - 50% - 27px)) scale(1.033); }
}
/* Leader lines (one per letter, server-built from a fanned point far above the mark to the
   letter's start). Each STREAKS IN from off-screen to the letter's start point, then FADES as
   that letter's outline begins to draw. Hidden by default (opacity 0) so they never show in the
   settled corner logo; only the boot animation reveals+retracts them. */
.lead-in { stroke: #cc0000; stroke-width: 0.6; stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0; /* brand red pinned — boot mark keeps #cc0000 */ }
body:not(.booted) .lead-in { animation: lead-in 1.05s cubic-bezier(0.16, 0.7, 0.2, 1) both; }
@keyframes lead-in {
  0% { stroke-dashoffset: 1; opacity: 0; }
  12% { opacity: 1; }
  52% { stroke-dashoffset: 0; opacity: 1; }   /* fully drawn to the letter start */
  100% { stroke-dashoffset: 0; opacity: 0; }  /* then retracts (fades) as the letter takes over */
}
/* One path per letter (split server-side, left-to-right): each outline TRACES along its
   normalized dash starting the instant its leader arrives, staggered; the red fill then ROLLS
   across the word once every letter has drawn. */
body:not(.booted) .brand-svg path {
  stroke: #cc0000; stroke-width: 0.6; fill-opacity: 0; stroke-dasharray: 1; stroke-dashoffset: 1;
  animation: letter-draw 1.25s cubic-bezier(0.45, 0.05, 0.45, 0.95) both, letter-fill 0.9s ease both;
}
@keyframes letter-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes letter-fill { from { fill-opacity: 0; } to { fill-opacity: 1; } }
/* Per-letter stagger. Leader starts at Ln; the outline draws from Ln+0.5s (as the leader lands);
   fills roll from 5.3s. Both lists left-to-right, 0.28s apart. */
body:not(.booted) .lead-in:nth-of-type(1)  { animation-delay: 0.3s; }
body:not(.booted) .lead-in:nth-of-type(2)  { animation-delay: 0.58s; }
body:not(.booted) .lead-in:nth-of-type(3)  { animation-delay: 0.86s; }
body:not(.booted) .lead-in:nth-of-type(4)  { animation-delay: 1.14s; }
body:not(.booted) .lead-in:nth-of-type(5)  { animation-delay: 1.42s; }
body:not(.booted) .lead-in:nth-of-type(6)  { animation-delay: 1.7s; }
body:not(.booted) .lead-in:nth-of-type(7)  { animation-delay: 1.98s; }
body:not(.booted) .lead-in:nth-of-type(8)  { animation-delay: 2.26s; }
body:not(.booted) .lead-in:nth-of-type(9)  { animation-delay: 2.54s; }
body:not(.booted) .lead-in:nth-of-type(10) { animation-delay: 2.82s; }
body:not(.booted) .lead-in:nth-of-type(11) { animation-delay: 3.1s; }
body:not(.booted) .brand-svg path:nth-of-type(1)  { animation-delay: 0.8s, 5.3s; }
body:not(.booted) .brand-svg path:nth-of-type(2)  { animation-delay: 1.08s, 5.36s; }
body:not(.booted) .brand-svg path:nth-of-type(3)  { animation-delay: 1.36s, 5.42s; }
body:not(.booted) .brand-svg path:nth-of-type(4)  { animation-delay: 1.64s, 5.48s; }
body:not(.booted) .brand-svg path:nth-of-type(5)  { animation-delay: 1.92s, 5.54s; }
body:not(.booted) .brand-svg path:nth-of-type(6)  { animation-delay: 2.2s, 5.6s; }
body:not(.booted) .brand-svg path:nth-of-type(7)  { animation-delay: 2.48s, 5.66s; }
body:not(.booted) .brand-svg path:nth-of-type(8)  { animation-delay: 2.76s, 5.72s; }
body:not(.booted) .brand-svg path:nth-of-type(9)  { animation-delay: 3.04s, 5.78s; }
body:not(.booted) .brand-svg path:nth-of-type(10) { animation-delay: 3.32s, 5.84s; }
body:not(.booted) .brand-svg path:nth-of-type(11) { animation-delay: 3.6s, 5.9s; }
/* LASER TIP: a short bright-WHITE dash (0.06 of the normalized length) rides the draw front and
   sweeps off the end, so the leading edge glows white and the trail settles red. dasharray
   "0.06 1" shows a single 6%-long segment; tip-draw offsets it 0.06 -> -0.94 in lockstep with the
   main draw's 1 -> 0 (same duration + easing per type), keeping the white tip pinned to the red
   front. Hidden by default; the glow is a WIDER, translucent white sibling (no filter — filters
   rasterize + blur under the boot scale in WebKit). Tips are the 12th..22nd of their type. */
/* Two white layers ride the draw front: a translucent, LONGER trail (the falloff — extends
   further back at low opacity, reading white->red behind the point) under a short bright core tip.
   Each is a SINGLE animation (opacity + dashoffset in one keyframe) — the split into two broke
   WebKit's stagger, so keep it combined. Both leading edges track the main front (dashoffset ->
   the same 0->1 sweep). Trails are 12..22 of their type, tips 23..33. */
.brand-svg .tip, .brand-svg .trail { opacity: 0; } /* hidden in the settled logo */
body:not(.booted) .brand-svg .tip, body:not(.booted) .brand-svg .trail { fill: none; stroke-linecap: round; }
body:not(.booted) .brand-svg .tip { stroke: #fff; } /* bright white core */
body:not(.booted) .brand-svg .trail { stroke: #fff; } /* white tail at 50% (opacity from the keyframe) */
body:not(.booted) .brand-svg .trail { stroke-dasharray: 0.16 1; }
body:not(.booted) .brand-svg .tip { stroke-dasharray: 0.025 1; }
/* tip + trail match the red line's stroke-width (0.6) */
body:not(.booted) .brand-svg line.trail { stroke-width: 0.6; animation: trail-draw 0.55s cubic-bezier(0.16, 0.7, 0.2, 1) both; }
body:not(.booted) .brand-svg path.trail { stroke-width: 0.6; animation: trail-draw 1.25s cubic-bezier(0.45, 0.05, 0.45, 0.95) both; }
body:not(.booted) .brand-svg line.tip { stroke-width: 0.6; animation: tip-draw 0.55s cubic-bezier(0.16, 0.7, 0.2, 1) both; }
body:not(.booted) .brand-svg path.tip { stroke-width: 0.6; animation: tip-draw 1.25s cubic-bezier(0.45, 0.05, 0.45, 0.95) both; }
@keyframes tip-draw {
  0% { stroke-dashoffset: 0.025; opacity: 0; }
  8% { opacity: 1; } 88% { opacity: 1; }
  100% { stroke-dashoffset: -0.975; opacity: 0; }
}
@keyframes trail-draw {
  0% { stroke-dashoffset: 0.16; opacity: 0; }
  10% { opacity: 0.5; } 84% { opacity: 0.5; }
  100% { stroke-dashoffset: -0.84; opacity: 0; }
}
/* trail (line 12..22) + core tip (line 23..33) of each leader share the leader's delay Ln */
body:not(.booted) .brand-svg line.trail:nth-of-type(12), body:not(.booted) .brand-svg line.tip:nth-of-type(23) { animation-delay: 0.3s; }
body:not(.booted) .brand-svg line.trail:nth-of-type(13), body:not(.booted) .brand-svg line.tip:nth-of-type(24) { animation-delay: 0.58s; }
body:not(.booted) .brand-svg line.trail:nth-of-type(14), body:not(.booted) .brand-svg line.tip:nth-of-type(25) { animation-delay: 0.86s; }
body:not(.booted) .brand-svg line.trail:nth-of-type(15), body:not(.booted) .brand-svg line.tip:nth-of-type(26) { animation-delay: 1.14s; }
body:not(.booted) .brand-svg line.trail:nth-of-type(16), body:not(.booted) .brand-svg line.tip:nth-of-type(27) { animation-delay: 1.42s; }
body:not(.booted) .brand-svg line.trail:nth-of-type(17), body:not(.booted) .brand-svg line.tip:nth-of-type(28) { animation-delay: 1.7s; }
body:not(.booted) .brand-svg line.trail:nth-of-type(18), body:not(.booted) .brand-svg line.tip:nth-of-type(29) { animation-delay: 1.98s; }
body:not(.booted) .brand-svg line.trail:nth-of-type(19), body:not(.booted) .brand-svg line.tip:nth-of-type(30) { animation-delay: 2.26s; }
body:not(.booted) .brand-svg line.trail:nth-of-type(20), body:not(.booted) .brand-svg line.tip:nth-of-type(31) { animation-delay: 2.54s; }
body:not(.booted) .brand-svg line.trail:nth-of-type(21), body:not(.booted) .brand-svg line.tip:nth-of-type(32) { animation-delay: 2.82s; }
body:not(.booted) .brand-svg line.trail:nth-of-type(22), body:not(.booted) .brand-svg line.tip:nth-of-type(33) { animation-delay: 3.1s; }
body:not(.booted) .brand-svg path.trail:nth-of-type(12), body:not(.booted) .brand-svg path.tip:nth-of-type(23) { animation-delay: 0.8s; }
body:not(.booted) .brand-svg path.trail:nth-of-type(13), body:not(.booted) .brand-svg path.tip:nth-of-type(24) { animation-delay: 1.08s; }
body:not(.booted) .brand-svg path.trail:nth-of-type(14), body:not(.booted) .brand-svg path.tip:nth-of-type(25) { animation-delay: 1.36s; }
body:not(.booted) .brand-svg path.trail:nth-of-type(15), body:not(.booted) .brand-svg path.tip:nth-of-type(26) { animation-delay: 1.64s; }
body:not(.booted) .brand-svg path.trail:nth-of-type(16), body:not(.booted) .brand-svg path.tip:nth-of-type(27) { animation-delay: 1.92s; }
body:not(.booted) .brand-svg path.trail:nth-of-type(17), body:not(.booted) .brand-svg path.tip:nth-of-type(28) { animation-delay: 2.2s; }
body:not(.booted) .brand-svg path.trail:nth-of-type(18), body:not(.booted) .brand-svg path.tip:nth-of-type(29) { animation-delay: 2.48s; }
body:not(.booted) .brand-svg path.trail:nth-of-type(19), body:not(.booted) .brand-svg path.tip:nth-of-type(30) { animation-delay: 2.76s; }
body:not(.booted) .brand-svg path.trail:nth-of-type(20), body:not(.booted) .brand-svg path.tip:nth-of-type(31) { animation-delay: 3.04s; }
body:not(.booted) .brand-svg path.trail:nth-of-type(21), body:not(.booted) .brand-svg path.tip:nth-of-type(32) { animation-delay: 3.32s; }
body:not(.booted) .brand-svg path.trail:nth-of-type(22), body:not(.booted) .brand-svg path.tip:nth-of-type(33) { animation-delay: 3.6s; }
.brand-name { transition: opacity 0.35s ease 1.6s; } /* fades in once the corner mark has filled */
body:not(.booted) .brand-name { opacity: 0; transition: none; }

/* Boot handoff: on .booted the big centre mark DRAINS right→left (clip wipe) while the corner mark
   FILLS right→left in lockstep, a red connector streaking between them — the mark is handed from
   centre-screen to the top-left HUD slot. Negative top/bottom insets keep letter tops/tails uncut. */
.booted .brand-svg { animation: boot-drain 1.5s cubic-bezier(0.62, 0, 0.38, 1) both; }
@keyframes boot-drain { from { clip-path: inset(-12% 0 -12% 0); } to { clip-path: inset(-12% 100% -12% 0); } }
.corner-svg { clip-path: inset(-12% 0 -12% 100%); } /* empty until the handoff reveals it right→left */
.booted .corner-svg { animation: corner-fill 1.5s cubic-bezier(0.62, 0, 0.38, 1) both; }
@keyframes corner-fill { from { clip-path: inset(-12% 0 -12% 100%); } to { clip-path: inset(-12% 0 -12% 0); } }
.boot-link { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 21; pointer-events: none; opacity: 0; }
.boot-link line { stroke: #cc0000; stroke-width: 2; filter: drop-shadow(0 0 2px #cc0000); } /* viewBox is set to viewport px in JS */
.booted .boot-link { animation: link-flash 1.5s ease both; }
@keyframes link-flash { 0% { opacity: 0; } 16% { opacity: 0.95; } 66% { opacity: 0.7; } 100% { opacity: 0; } }
.boot-tail { margin-top: 32vh; display: flex; flex-direction: column; align-items: center; gap: 14px; }
.boot-sweep { width: 220px; height: 1px; overflow: hidden; position: relative; background: var(--hud-line); }
.boot-sweep::before { content: ""; position: absolute; top: 0; left: -40%; width: 40%; height: 100%;
  background: linear-gradient(90deg, transparent, #cc0000); animation: sweep 1.4s ease-in-out infinite; }
@keyframes sweep { to { left: 100%; } }

/* Callout overlay (scene-hud). pointer-events none — the scene stays interactive under it. */
.hud-overlay { position: fixed; inset: 0; z-index: 8; pointer-events: none; overflow: hidden; }
.hud-overlay[hidden] { display: none; }
.hud-overlay svg { position: absolute; inset: 0; width: 100%; height: 100%; }
/* Callout reveal: the leader line DRAWS from its anchor point (dash-offset sweep — polyline
   points start at the anchor, so the growth radiates out of it), then the label wipes open.
   scene-hud.ts restarts these + sets per-slot animation-delay so fresh callouts appear one at a
   time. dasharray 320 exceeds any leader length, so the resting state renders solid. */
.hud-lead { fill: none; stroke: var(--hud-accent-dim); stroke-width: 1;
  stroke-dasharray: 320; animation: lead-draw 0.4s ease-out backwards; }
@keyframes lead-draw { from { stroke-dashoffset: 320; } to { stroke-dashoffset: 0; } }
.hud-callout { animation: callout-reveal 0.3s ease-out backwards; }
.hud-callout-r { animation-name: callout-reveal-r; }
@keyframes callout-reveal { from { clip-path: inset(0 100% 0 0); filter: brightness(2.2); } to { clip-path: inset(0 0 0 0); filter: none; } }
@keyframes callout-reveal-r { from { clip-path: inset(0 0 0 100%); filter: brightness(2.2); } to { clip-path: inset(0 0 0 0); filter: none; } }
.hud-callout { position: absolute; left: 0; top: 0; will-change: transform; white-space: nowrap;
  font-size: 11px; line-height: 1.25; padding: 2px 6px; border-left: 2px solid var(--hud-accent);
  background: var(--hud-glass); border-radius: var(--hud-radius-sm); }
.hud-callout-r { border-left: none; border-right: 2px solid var(--hud-accent); transform-origin: right; }
.hud-callout-name { font-family: var(--hud-font); font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--hud-ink); display: block; }
.hud-callout .busy { color: var(--hud-hot); text-shadow: 0 0 6px var(--hud-hot-35); } /* glow keeps brand red legible at 11px */
.hud-callout[hidden] { display: none; }

/* ---- Right-click context menu (scene-context-menu.ts) — type-aware, JARVIS reveal ---------------
   Cursor-anchored FUI panel: a red laser connector snaps from the click point, the panel CRT-strikes
   in from the cursor corner, its title wipes and its items cascade left→right, a scanline rolls over
   it. Notched corners + glass reuse the --hud vocabulary; z above the brand (21). */
.ctx-menu {
  position: fixed; z-index: 30; min-width: 172px; overflow: hidden;
  background-color: var(--hud-glass); background-image: var(--hud-dotgrid); background-size: 14px 14px;
  border: 1px solid var(--hud-line); /* opaque enough to read over bright surfaces */
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  box-shadow: var(--hud-shadow-pop); border-radius: var(--hud-radius);
  clip-path: polygon(0 0, calc(100% - var(--hud-notch-sm)) 0, 100% var(--hud-notch-sm), 100% 100%, var(--hud-notch-sm) 100%, 0 calc(100% - var(--hud-notch-sm)));
  transform-origin: var(--ox, 0px) var(--oy, 0px);
  animation: ctx-in 0.28s cubic-bezier(0.2, 1.16, 0.34, 1) both;
}
.ctx-menu::after { /* rolling scanlines, clipped to the notch by the parent */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(0deg, var(--hud-accent-05) 0 1px, transparent 1px 3px);
  animation: ctx-scan 0.9s steps(3) infinite; opacity: calc(0.6 * var(--hud-ornament));
}
.ctx-title {
  font-family: var(--hud-mono); font-size: 9px; letter-spacing: var(--hud-tracking); text-transform: var(--hud-case);
  color: var(--hud-dim); padding: 7px 13px 5px 16px; border-bottom: 1px solid var(--hud-line);
  box-shadow: inset var(--hud-bracket) 0 0 var(--hud-hot); /* brand tick down the title */
  animation: ctx-title-in 0.5s ease 0.06s both;
}
.ctx-item {
  display: block; width: 100%; text-align: left; background: none; border: 0; cursor: pointer;
  font-family: var(--hud-font); font-size: 12px; letter-spacing: 0.06em; color: var(--hud-ink);
  padding: 7px 14px 7px 16px; position: relative; border-radius: var(--hud-radius-sm);
  animation: ctx-item-in 0.34s cubic-bezier(0.16, 0.8, 0.3, 1) both;
  animation-delay: calc(0.12s + var(--i, 0) * 0.06s);
  transition: background 0.12s ease, color 0.12s ease, box-shadow 0.12s ease;
}
.ctx-item::before { /* left edge tick that lights up on hover */
  content: ""; position: absolute; left: 0; top: 50%; width: 2px; height: 0; border-radius: inherit;
  background: var(--hud-hot); transform: translateY(-50%); transition: height 0.14s ease;
}
.ctx-item:hover { background: var(--hud-hot-12); color: var(--hud-accent); box-shadow: inset 0 0 0 1px var(--hud-line); }
.ctx-item:hover::before { height: 100%; }
.ctx-danger { color: var(--hud-danger); }
.ctx-danger:hover { background: var(--hud-hot-26); color: var(--hud-accent); }
.ctx-link { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 29; pointer-events: none; }
.ctx-link line { stroke: var(--hud-hot); stroke-width: 1.4; stroke-dasharray: 1400;
  filter: drop-shadow(0 0 3px var(--hud-hot)); animation: ctx-link-in 0.5s ease both; }
@keyframes ctx-in {
  0% { opacity: 0; transform: scale(0.42, 0.42); filter: brightness(2.6); }
  55% { opacity: 1; filter: brightness(1.5); }
  72% { transform: scale(1.04, 0.97); }
  100% { opacity: 1; transform: scale(1, 1); filter: none; }
}
@keyframes ctx-item-in { from { opacity: 0; clip-path: inset(0 100% 0 0); filter: brightness(2.2); } to { opacity: 1; clip-path: inset(0 0 0 0); filter: none; } }
@keyframes ctx-title-in { from { clip-path: inset(0 0 0 100%); } to { clip-path: inset(0 0 0 0); } }
@keyframes ctx-scan { to { background-position: 0 3px; } }
@keyframes ctx-link-in { 0% { stroke-dashoffset: 1400; opacity: 0; } 30% { opacity: 1; } 100% { stroke-dashoffset: 0; opacity: 0.9; } }

/* ---- Page frame: a purely ornamental faceted outline (scene-hud-frame.ts draws the polygon in
   viewport px) — chamfered corners, diagonal edge jogs, and a lower-right pocket around the readout
   + greeble. The OUTSIDE band is a frosted dark-glass fill (clipped to the even-odd donut in JS);
   the crisp line sits on top. Background layers (z 5/6), never interactive. */
/* Atmospheric falloff over the whole scene — the boards' framing depth. Below the frame band (z 5). */
.page-frame-glass { position: fixed; inset: 0; width: 100vw; height: 100vh; z-index: 5; pointer-events: none;
  background-color: var(--hud-scrim); background-image: var(--hud-dotgrid); background-size: 14px 14px;
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px); }
.page-frame { position: fixed; inset: 0; width: 100vw; height: 100vh; z-index: 6; pointer-events: none; opacity: var(--hud-ornament); }
.page-frame polygon { fill: none; stroke: var(--hud-line); stroke-width: 1; stroke-linejoin: round; }

/* ---- Lower-right busy greeble (scene-busy-greeble.ts) ------------------------------------------
   A 3×20 matrix of square dots that twinkle in transparent white at random phases — an ambient sci-fi
   detail. While the server works it powers up (staying white): the dots swing from very faint to
   bright/opaque and pulse ~2× faster, and the label flips to PROC. Per-dot --d (phase) / --dur
   (period) are randomised in JS so they never sync. */
.greeble { position: fixed; right: 24px; bottom: 24px; z-index: 12; pointer-events: none; opacity: var(--hud-ornament);
  display: flex; align-items: center; gap: 9px; }
.greeble-grid { display: grid; grid-template-columns: repeat(20, 3px); grid-auto-rows: 3px; gap: 2px; }
.greeble-grid i { width: 3px; height: 3px; background: var(--hud-speck); opacity: 0.08;
  animation: greeble-twinkle var(--dur, 2s) ease-in-out var(--d, 0s) infinite; }
.greeble[data-busy="true"] .greeble-grid i { animation-name: greeble-twinkle-hot; animation-duration: calc(var(--dur, 2s) * 0.22); } /* stays snappy despite the slow idle --dur */
.greeble-tag { display: flex; align-items: center; gap: 5px; min-width: 40px;
  font-family: var(--hud-mono); font-size: 9px; letter-spacing: var(--hud-tracking-ui); color: var(--hud-accent-dim); }
.greeble-dot { width: 5px; height: 5px; background: var(--hud-accent-dim);
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); } /* diamond */
.greeble[data-busy="true"] .greeble-dot { background: var(--hud-accent); animation: greeble-pulse 0.7s ease-in-out infinite; }
.greeble[data-busy="true"] .greeble-label { color: var(--hud-warn); }
@keyframes greeble-twinkle { 0%, 100% { opacity: 0.05; } 50% { opacity: 0.26; } }
@keyframes greeble-twinkle-hot { 0%, 100% { opacity: 0.06; } 50% { opacity: 0.98; } }
@keyframes greeble-pulse { 0%, 100% { opacity: 0.4; box-shadow: 0 0 0 rgba(255, 255, 255, 0); } 50% { opacity: 1; box-shadow: 0 0 7px rgba(255, 255, 255, 0.7); } }

@media (prefers-reduced-motion: reduce) {
  .greeble-grid i, .greeble-dot { animation: none; opacity: 0.2; }
  .greeble[data-busy="true"] .greeble-grid i { opacity: 0.7; } /* static bright white instead of the twinkle */
  .hud-enter { animation: none; }
  .boot-sweep::before, .hud-overlay::after { animation: none; }
  .brand-name { transition: none; }
  .brand-svg { display: none; } /* skip the big cinematic + drain entirely */
  .corner-svg { clip-path: none; animation: none; } /* corner mark shown filled straight away */
  .boot-link { display: none; }
  body:not(.booted) .brand-name { opacity: 1; }
  .hud-btn:hover, .hud-select:hover, .hud-check:hover, .rail-row:hover, .facet-row:hover, .hud-btn:active, .hud-range:active { animation: none; }
  .rail.hud-enter, .hud-enter > *, .facet-row { animation: none; }
  .hud-lead, .hud-callout, .hud-callout-r { animation: none; }
  .hud-btn { transition: none; }
  .ctx-menu, .ctx-title, .ctx-item, .ctx-menu::after { animation: none; }
  .ctx-item { opacity: 1; clip-path: none; }
  .ctx-link { display: none; }
}
