/* ───────────────────────── Scott Lucchini — Research site ───────────────────────── */
:root {
  --hue: 220;
  --maxw: 1280px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --font-sans: "IBM Plex Sans", system-ui, sans-serif;
  --font-display: "Space Grotesk", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;
  --chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 5l7 7-7 7' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* ── Cosmic (dark) theme ── */
[data-theme="cosmic"] {
  --bg:       oklch(0.165 0.024 265);
  --bg2:      oklch(0.205 0.028 265);
  --surface:  oklch(0.225 0.030 265);
  --surface2: oklch(0.255 0.032 265);
  --line:     oklch(1 0 0 / 0.10);
  --line2:    oklch(1 0 0 / 0.16);
  --text:     oklch(0.955 0.010 265);
  --muted:    oklch(0.74 0.022 265);
  --faint:    oklch(0.60 0.022 265);
  --accent:   oklch(0.82 0.135 var(--hue));
  --accent-d: oklch(0.82 0.135 var(--hue) / 0.16);
  --accent-b: oklch(0.82 0.135 var(--hue) / 0.42);
  --glow:     oklch(0.82 0.135 var(--hue) / 0.22);
}
/* ── Editorial (light) theme ── */
[data-theme="editorial"] {
  --bg:       oklch(0.965 0.006 265);
  --bg2:      oklch(0.985 0.004 265);
  --surface:  oklch(1 0 0);
  --surface2: oklch(0.975 0.005 265);
  --line:     oklch(0.2 0.02 265 / 0.12);
  --line2:    oklch(0.2 0.02 265 / 0.22);
  --text:     oklch(0.235 0.022 265);
  --muted:    oklch(0.46 0.022 265);
  --faint:    oklch(0.58 0.020 265);
  --accent:   oklch(0.55 0.155 var(--hue));
  --accent-d: oklch(0.55 0.155 var(--hue) / 0.10);
  --accent-b: oklch(0.55 0.155 var(--hue) / 0.40);
  --glow:     oklch(0.55 0.155 var(--hue) / 0.14);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--font-sans);
  background: oklch(0.165 0.024 265);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  transition: background 0.5s var(--ease), color 0.5s var(--ease);
}
::selection { background: var(--accent-b); color: var(--bg); }
a { color: inherit; }

/* #root sits above the persistent #viz-stage canvas (see viz3d.js); the canvas
   shows through wherever .site / the hero backgrounds are transparent. */
#root { position: relative; z-index: 2; }
.site {
  min-height: 100vh; display: flex; flex-direction: column;
  background: transparent; color: var(--text);
  transition: background 0.5s var(--ease), color 0.5s var(--ease);
}

/* ── Persistent WebGL viz stage (single canvas, follows the active .viz-anchor) ── */
#viz-stage {
  position: fixed; z-index: 1; pointer-events: none;
  overflow: hidden;
}
#viz-stage canvas { display: block; }
/* layout-only anchor the canvas tracks. It sits in <main> OUTSIDE the sliding
   .stage and matches the hero's footprint, so the 3D viz stays static while the
   page content slides past it. Height mirrors the hero min-heights. */
/* ONE height for home AND area anchors (and the hero boxes below, which must
   mirror it): the canvas is glued to this rect, so any home↔area difference
   makes the model visibly jump (or drift, if transitioned) at navigation the
   moment nothing opaque covers it. The shared value is home's — the model
   spans a fixed fraction of canvas height (fixed vertical FOV), so area pages
   look identical at any height, but home's movie scales with WIDTH, so
   changing home's height would break the tuned movie↔model match cut. */
.viz-anchor {
  position: absolute; top: 0; left: 0; right: 0;
  height: clamp(460px, 78vh, 820px);
  z-index: -3; pointer-events: none;
}
/* transparent drag surface OrbitControls attaches to (above the decorative hero
   layers, below the arrows/controls at z-index 6 so those stay clickable).
   Only interactive areas enable it; elsewhere clicks/scroll pass through. */
.viz-hit { position: absolute; inset: 0; z-index: 4; pointer-events: none; cursor: grab; }
.viz-hit.on { pointer-events: auto; }
.viz-hit.on:active { cursor: grabbing; }

/* Resolution comparison wipe. The container is pointer-transparent so drag-to-
   rotate reaches .viz-hit; only the thin divider strip captures pointer events. */
.viz-compare {
  position: absolute; inset: 0; z-index: 5; pointer-events: none; --split: 50%;
  /* start the line + labels below the header, level with the "Gas volume" box:
     hero padding-top + the title-height spacer (see .viz-overlay-inner) */
  --content-top: calc(clamp(28px, 5vh, 52px) + clamp(20px, 6vw, 64px));
}
.viz-compare-label {
  position: absolute; top: var(--content-top);
  font: 500 clamp(11px, 1.3vw, 15px)/1 "IBM Plex Mono", monospace;
  letter-spacing: 0.04em; color: rgba(255,255,255,0.92);
  text-shadow: 0 1px 10px rgba(0,0,0,0.7); white-space: nowrap;
}
.viz-compare-label.left  { right: calc(100% - var(--split) + 12px); text-align: right; }
.viz-compare-label.right { left: calc(var(--split) + 12px); }
.viz-divider {
  position: absolute; top: var(--content-top); bottom: 0; left: var(--split);
  width: 30px; transform: translateX(-50%);
  pointer-events: auto; cursor: ew-resize; touch-action: none;
}
.viz-divider::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: 50%;
  width: 1.5px; transform: translateX(-50%);
  background: rgba(255,255,255,0.9); box-shadow: 0 0 10px rgba(255,255,255,0.35);
}
.viz-divider-handle {
  position: absolute; top: 50%; left: 50%;
  width: 34px; height: 34px; transform: translate(-50%,-50%);
  border-radius: 50%; background: rgba(20,26,34,0.55);
  border: 1.5px solid rgba(255,255,255,0.9);
  backdrop-filter: blur(3px);
}
.viz-divider-handle::before, .viz-divider-handle::after {
  content: ""; position: absolute; top: 50%; width: 0; height: 0;
  transform: translateY(-50%); /* top:50% alone left the triangles 5px low */
  border-top: 5px solid transparent; border-bottom: 5px solid transparent;
}
.viz-divider-handle::before { left: 7px;  border-right: 6px solid rgba(255,255,255,0.95); }
.viz-divider-handle::after  { right: 7px; border-left: 6px solid rgba(255,255,255,0.95); }
.viz-divider:focus-visible { outline: none; }
.viz-divider:focus-visible .viz-divider-handle { box-shadow: 0 0 0 3px var(--accent); }

/* ───────────────────────── Topbar ───────────────────────── */
.topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; padding: 16px clamp(20px, 4vw, 56px);
  background: color-mix(in oklch, var(--bg) 78%, transparent);
  backdrop-filter: blur(14px) saturate(1.2);
  border-bottom: 1px solid var(--line);
}
.brand { text-decoration: none; display: flex; flex-direction: column; gap: 2px; line-height: 1; transition: opacity 0.22s var(--ease); }
.brand-name { font-family: var(--font-display); font-weight: 700; font-size: 18.2px; letter-spacing: 0.09em; text-transform: uppercase; transition: color 0.22s var(--ease); }
.brand-role { font-family: var(--font-mono); font-size: 13.3px; color: var(--faint); letter-spacing: 0.01em; }
.brand.is-home .brand-name { color: var(--accent); }

.tabs { display: flex; gap: 4px; }
.tab {
  display: flex; align-items: center; gap: 9px;
  background: transparent; border: 1px solid transparent; cursor: pointer;
  color: var(--muted); padding: 9px 15px; border-radius: 999px;
  font-family: var(--font-sans); font-size: 16.4px; font-weight: 500;
  transition: color 0.25s var(--ease), background 0.25s var(--ease), border-color 0.25s var(--ease);
}
.tab:hover { color: var(--text); background: var(--surface); }
.tab .tab-i { font-family: var(--font-mono); font-size: 13.3px; color: var(--faint); transition: color 0.25s; }
.tab.active {
  color: oklch(0.82 0.135 var(--thue));
  background: oklch(0.82 0.135 var(--thue) / 0.13);
  border-color: oklch(0.82 0.135 var(--thue) / 0.34);
}
[data-theme="editorial"] .tab.active { color: oklch(0.5 0.16 var(--thue)); background: oklch(0.55 0.155 var(--thue) / 0.1); }
.tab.active .tab-i { color: oklch(0.82 0.135 var(--thue) / 0.75); }
[data-theme="editorial"] .tab.active .tab-i { color: oklch(0.5 0.16 var(--thue) / 0.8); }

/* ───────────────────────── Stage / transition ───────────────────────── */
main { flex: 1; overflow: hidden; position: relative; }
/* Transform-led entrance: a frozen frame still shows visible (only shifted/dimmed)
   content — never opacity:0 — so static captures & throttled iframes stay legible. */
.stage { animation: slideIn 0.5s var(--ease) both; }
.stage[data-dir="1"]  { --from: 38px; }
.stage[data-dir="-1"] { --from: -38px; }
@keyframes slideIn {
  from { opacity: 0.4; transform: translateX(var(--from, 38px)); }
  to   { opacity: 1; transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
  .stage { animation: none; }
}

/* ───────────────────────── Hero — full-bleed morphing viz ───────────────────────── */
.hero { position: relative; width: 100%; }
.hero-viz-full {
  position: relative; width: 100%;
  min-height: clamp(460px, 78vh, 820px);  /* = .viz-anchor (see there) */
  overflow: hidden;
  /* transparent so the persistent #viz-stage canvas shows through; a faint
     top-left glow is laid over the viz for depth */
  background: radial-gradient(120% 90% at 30% 16%, var(--glow), transparent 55%);
}
.hero-viz-full > .ph { position: absolute; inset: 0; }
.hero-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

/* ISM disk-simulation movie: sits above the 3D canvas + grid but below the
   scrim/title/controls, and crossfades in once the 3D model has reoriented. */
.hero-movie {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  /* The movie is square (1024x1024) in a much wider hero box, so `contain` is
     height-limited and fits 100% of the frame vertically; the scale then crops
     it back (.hero-viz-full clips the overflow). scale = 1/fraction-shown, so
     1.25 shows 80% of the movie's height — raise it to zoom in further.
     Square in a wide box means it pillarboxes; the black backdrop covers the
     whole hero box (the element is scaled past it), hiding the 3D model behind
     it and blending into the movie's own black edges. */
  object-fit: contain;
  transform: scale(1.25);
  background: #000;
  opacity: 0; transition: opacity 1.1s var(--ease);
  pointer-events: none;
}
.hero-movie.show { opacity: 1; }

/* ISM: SNe magnifier. The panel is parked on the right of the hero (clear of
   .arrow-r, which is right ~20px and 46px wide) and the cursor carries a small
   reticle ring drawn at the true size of the sampled region — the pair reads as
   "this ring → that panel". z-index 2 = above the scrim (the lens "clears" the
   dimming) but below the hero title. Sizes come from JS (canvas dia, ring =
   dia/mag); CSS only parks and styles them. */
.sne-lens-panel {
  position: absolute; z-index: 2;
  right: clamp(72px, 7vw, 112px); top: 50%; transform: translateY(-50%);
  pointer-events: none;
  opacity: 0; visibility: hidden;
  transition: opacity 0.18s var(--ease), visibility 0.18s;
}
.sne-lens-panel.on { opacity: 1; visibility: visible; }
/* Below ~1150px the movie square (side = hero height x 1.25) leaves no gutter
   worth docking into, so the panel moves to the top-right corner. That regime
   is placed from JS (SneLens `layout`), which overrides top/right/transform
   here — it has to measure the hero title to know how far down to start. */
.sne-lens {
  display: block; border-radius: 50%;
  border: 1px solid color-mix(in oklch, var(--accent) 55%, transparent);
  box-shadow: 0 6px 30px rgba(0, 0, 0, 0.55);
  background: #000;
}
.sne-lens-cap {
  margin-bottom: 8px; text-align: center;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.06em;
  color: var(--faint); font-variant-numeric: tabular-nums;
}
/* the cursor ring: same area the panel is showing, 1:1 on the movie */
.sne-reticle {
  position: absolute; left: 0; top: 0; z-index: 2;
  border-radius: 50%;
  border: 1px solid color-mix(in oklch, var(--accent) 70%, transparent);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45), 0 0 12px color-mix(in oklch, var(--accent) 25%, transparent);
  pointer-events: none;
  opacity: 0; transition: opacity 0.18s var(--ease);
}
.sne-reticle.on { opacity: 1; }
/* pinned: solid accent rings so the locked state reads at a glance */
.sne-lens-panel.pinned .sne-lens {
  border-color: color-mix(in oklch, var(--accent) 90%, transparent);
  box-shadow: 0 6px 30px rgba(0, 0, 0, 0.55), 0 0 14px color-mix(in oklch, var(--accent) 35%, transparent);
}
.sne-reticle.pinned {
  border-width: 2px;
  border-color: color-mix(in oklch, var(--accent) 95%, transparent);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45), 0 0 16px color-mix(in oklch, var(--accent) 40%, transparent);
}
/* the ring marks the spot, so the arrow stays — but sharpen it to a crosshair */
.hero-viz-full.lens-active { cursor: crosshair; }
.lens-hint {
  position: absolute; z-index: 2; right: 18px; bottom: 12px;
  font-family: var(--font-mono); font-size: 12.1px; color: var(--faint);
  opacity: 0; transition: opacity 0.8s var(--ease);
  pointer-events: none;
}
.lens-hint.show { opacity: 1; }
/* phones: bottom-right is where the scrubber's frame counter already sits, so
   center the hint just above the scrub bar instead */
@media (pointer: coarse) {
  .lens-hint {
    left: 0; right: 0; bottom: 82px; text-align: center; font-size: 11px;
  }
}

/* Mobile-only gesture guard: on touch devices one finger scrolls the page and
   two fingers rotate/zoom the model (see _attachControls in viz3d.js). The whole
   viz dims and says so while a one-finger drag is scrolling past it — Google
   Maps' pattern — raised and cleared by TouchGestureHint in app.js. Hidden on
   fine-pointer devices, where the drag it describes doesn't exist.
   Pointer-transparent, so the two-finger gesture it asks for lands on .viz-hit
   underneath rather than on the message. */
.touch-gesture { display: none; }
@media (pointer: coarse) {
  .touch-gesture {
    display: grid; place-items: center;
    position: absolute; inset: 0; z-index: 7; pointer-events: none;
    background: rgba(12,16,22,0.52); backdrop-filter: blur(1.5px);
    opacity: 0; transition: opacity 0.25s var(--ease);
  }
  .touch-gesture.show { opacity: 1; }
  .touch-gesture-msg {
    max-width: 74%; text-align: center; text-wrap: balance;
    font-family: var(--font-mono); font-size: 15.4px; line-height: 1.45;
    letter-spacing: 0.02em; color: #fff; text-shadow: 0 1px 8px rgba(0,0,0,0.55);
  }
}
@media (prefers-reduced-motion: reduce) {
  .touch-gesture { transition: none; }
}

/* ISM: movie scrubber + speed toggle, bottom-center; fades in with the movie */
.movie-scrub {
  position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%);
  z-index: 2; width: min(420px, 80vw);
  opacity: 0; transition: opacity 0.8s var(--ease);
  pointer-events: none;
}
.movie-scrub.show { opacity: 1; pointer-events: auto; }
.scrub-row {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 6px;
}
.scrub-left { display: flex; align-items: center; gap: 8px; }
.scrub-play {
  appearance: none; cursor: pointer;
  border: 1px solid var(--line2); border-radius: 8px; background: transparent;
  color: var(--muted); font-size: 9.9px; line-height: 1;
  min-width: 34px; padding: 7px 0;
}
.scrub-play:hover { color: var(--text); }
.scrub-speed button { min-width: 34px; }
.scrub-frame {
  font-family: var(--font-mono); font-size: 12.1px; color: var(--faint);
  font-variant-numeric: tabular-nums;
}

/* Top + bottom fades into the page background — same gesture as the home hero.
   The top one darkens the viz behind .hero-title (which sits at the top of the
   panel, z-index 3, above this); it's strongest over the title band (~0-15% of
   the panel) and gone by ~34%. Both ends use var(--bg), NOT literal black — the
   light theme's --bg is near-white, so hardcoding black would break it. */
.hero-viz-scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    radial-gradient(120% 80% at 50% 26%, transparent 30%, color-mix(in oklch, var(--bg) 26%, transparent) 72%, color-mix(in oklch, var(--bg) 64%, transparent) 100%),
    linear-gradient(to bottom,
      color-mix(in oklch, var(--bg) 82%, transparent) 0%,
      color-mix(in oklch, var(--bg) 40%, transparent) 16%,
      transparent 34%,
      transparent 42%,
      color-mix(in oklch, var(--bg) 50%, transparent) 76%,
      var(--bg) 100%);
}

/* title overlaid at the TOP of the panel (align-items: flex-start), inside the
   readable fade; the lede sits below the viz in .hero-foot */
.hero-overlay {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  display: flex; align-items: flex-start;
}
.hero-overlay-inner {
  max-width: var(--maxw); margin: 0 auto; width: 100%;
  padding: clamp(28px, 5vh, 52px) clamp(20px, 4vw, 56px) 0;
  display: flex; flex-direction: column; gap: clamp(12px, 1.6vw, 20px);
}
.hero-overlay-inner > * { pointer-events: auto; }

/* lede + matching divider below the viz, inset to the home-page line width */
.hero-foot {
  position: relative;
  max-width: var(--maxw); margin: 0 auto; width: 100%;
  padding: clamp(22px, 3vw, 34px) clamp(20px, 4vw, 56px) clamp(8px, 2vw, 18px);
}
.hero-foot::before {
  content: ""; position: absolute; top: 0;
  left: clamp(20px, 4vw, 56px); right: clamp(20px, 4vw, 56px);
  border-top: 1px solid var(--line);
}
.hero-grid {
  position: absolute; inset: 0; pointer-events: none; opacity: 0.5;
  background-image:
    linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 46px 46px;
  mask-image: radial-gradient(120% 100% at 50% 50%, black 30%, transparent 78%);
}
.hero-viz-tag {
  position: absolute; left: 16px; top: 14px; z-index: 2;
  display: flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 12.7px; color: var(--muted);
  background: color-mix(in oklch, var(--bg) 62%, transparent);
  backdrop-filter: blur(6px);
  padding: 7px 12px; border-radius: 999px; border: 1px solid var(--line);
  letter-spacing: 0.01em; max-width: calc(100% - 32px);
}
.hero-viz-tag .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 12px var(--accent); flex: none; }
.hero-viz-corner {
  position: absolute; right: 14px; top: 13px; z-index: 2; white-space: nowrap;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--faint); border: 1px dashed var(--line2); padding: 5px 9px; border-radius: 6px;
}

.hero-title {
  font-family: var(--font-display); font-weight: 600; letter-spacing: -0.02em;
  font-size: clamp(20px, 6vw, 64px); line-height: 1.0; white-space: nowrap;
  text-shadow: 0 2px 30px rgba(0,0,0,0.55), 0 1px 5px rgba(0,0,0,0.45);
}
[data-theme="editorial"] .hero-title { text-shadow: 0 2px 24px var(--bg), 0 1px 4px var(--bg); }
.hero-lede {
  font-size: clamp(17.1px, 1.71vw, 19.8px); line-height: 1.62; color: var(--muted);
  max-width: 72ch; text-wrap: pretty;
}

/* arrows — vertically centered on the full-width viz */
.arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 6;
  width: 46px; height: 46px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: color-mix(in oklch, var(--surface) 80%, transparent);
  border: 1px solid var(--line2); color: var(--text);
  backdrop-filter: blur(8px);
  transition: transform 0.2s var(--ease), background 0.2s, border-color 0.2s, box-shadow 0.2s, color 0.2s;
}
.arrow:hover { background: var(--accent-d); border-color: var(--accent-b); color: var(--accent); box-shadow: 0 0 0 4px var(--accent-d); }
.arrow:active { transform: translateY(-50%) scale(0.92); }
.arrow-l { left: clamp(12px, 1.6vw, 20px); }
.arrow-r { right: clamp(12px, 1.6vw, 20px); }

/* ───────────────────────── Content ───────────────────────── */
.content { max-width: var(--maxw); margin: 0 auto; padding: clamp(8px, 2vw, 22px) clamp(20px, 4vw, 56px) 64px; }
.content-inner { display: block; }

.sec-label {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--font-mono); font-size: 13.2px; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--muted); padding-bottom: 16px; margin-bottom: 22px; border-bottom: 1px solid var(--line);
}
.sec-count {
  margin-left: auto; font-size: 12.1px; color: var(--accent);
  background: var(--accent-d); padding: 2px 9px; border-radius: 999px; border: 1px solid var(--accent-b);
}

/* data-release callout (e.g. ENGAWA on the CGM page) — sits above publications */
.data-release {
  background: var(--accent-d); border: 1px solid var(--accent-b); border-radius: 14px;
  padding: clamp(20px, 2.6vw, 28px); margin-bottom: 26px;
}
.release-eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 12.7px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 12px;
}
.release-eyebrow::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 8px var(--accent);
}
.release-text {
  font-size: clamp(15.4px, 1.65vw, 17.1px); line-height: 1.62; color: var(--text);
  max-width: 78ch; text-wrap: pretty; margin-bottom: 16px;
}
.release-link {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-mono); font-size: 13.8px; font-weight: 500; text-decoration: none;
  color: oklch(0.16 0.03 265); background: var(--accent); border: 1px solid transparent;
  padding: 8px 15px; border-radius: 8px; transition: all 0.2s var(--ease);
}
.release-link:hover { background: oklch(0.86 0.14 var(--hue)); transform: translateY(-1px); }
.release-link svg { transition: transform 0.2s var(--ease); }
.release-link:hover svg { transform: translateX(2px); }

/* publication rows — text chunk + space for an image or two */
.pubs { display: flex; flex-direction: column; }
.pub {
  display: grid; grid-template-columns: 1.5fr 1fr; gap: clamp(28px, 4vw, 60px);
  align-items: start;
  padding: clamp(30px, 3.6vw, 52px) 0;
  border-top: 1px solid var(--line);
}
.pubs > .pub:first-child { border-top: none; padding-top: clamp(6px, 1.4vw, 16px); }
.pub.no-fig { grid-template-columns: 1fr; }
.pub.no-fig .pub-text { max-width: 80ch; }
.pub-text { min-width: 0; }
.pub .paper-title { font-size: clamp(20.9px, 1.87vw, 24.8px); margin-bottom: 11px; }
.pub .paper-sum { font-size: 16px; }
.pub-figs { display: flex; flex-direction: column; gap: 16px; }
.paper-title {
  font-family: var(--font-display); font-weight: 600; font-size: 20.4px; line-height: 1.22;
  letter-spacing: -0.01em; margin-bottom: 9px; text-wrap: balance;
  position: relative;
}
/* Marker hangs in the left margin so the title itself stays flush with the cite
   and summary below it. `right: 100%` pins the glyph's right edge to the title's
   left edge, so the gap is exactly margin-right whatever the glyph measures. The
   whole hang (glyph + gap) has to stay inside .content's padding, which floors at
   20px on narrow screens — hence the clamped gap rather than a fixed one. */
/* The chevron is an SVG mask rather than a mono ">" glyph: the glyph's stroke
   weight is locked to the two loaded font weights and its box sits wherever the
   font's metrics put it, while the mask gives an exact stroke-width and a shape
   that is truly centred in its box — so `top` alone aims it at the first line. */
.paper-title::before {
  content: "";
  position: absolute; right: 100%; top: 0.66em; transform: translateY(-50%);
  margin-right: clamp(6px, 0.7vw, 9px);
  width: 0.62em; height: 0.62em;
  background: var(--accent);
  -webkit-mask: var(--chev) center / contain no-repeat;
  mask: var(--chev) center / contain no-repeat;
}
.paper-sum { font-size: 15.1px; line-height: 1.62; color: var(--muted); text-wrap: pretty; }
/* Astronomy notation (Z⊙, M⊙) sets ⊙ as a subscript. Default <sub> is
   `vertical-align: sub` + `font-size: smaller`, which grows the line box and
   opens a gap in the surrounding paragraph. Offset it manually instead, so a
   mid-sentence M⊙ leaves the line rhythm untouched. Site-wide: the standalone
   /engawa page carries its own styles and is unaffected. */
sub {
  font-size: 0.72em; line-height: 0; vertical-align: baseline;
  position: relative; bottom: -0.22em;
}
.paper-cite { font-family: var(--font-mono); font-size: 12.7px; color: var(--faint); margin-bottom: 14px; line-height: 1.5; }
.paper-links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 13px; }
.paper-link {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-mono); font-size: 12.7px; text-decoration: none;
  color: var(--text); background: var(--surface2); border: 1px solid var(--line2);
  padding: 5px 10px; border-radius: 7px; transition: all 0.2s var(--ease);
}
.paper-link:hover { color: var(--accent); border-color: var(--accent-b); background: var(--accent-d); }
.paper-link svg { opacity: 0.6; }

/* gallery */
.ph-note {
  font-family: var(--font-mono); font-size: 12.7px; line-height: 1.5; color: var(--faint);
  border-left: 2px solid var(--accent-b); padding: 2px 0 2px 12px; margin-bottom: 18px;
}

/* ───────────────────────── Placeholders ───────────────────────── */
.ph { display: flex; flex-direction: column; gap: 9px; }
.ph-stripe {
  position: relative; border-radius: 11px; aspect-ratio: 16 / 10; overflow: hidden;
  border: 1px solid var(--line2);
  background:
    repeating-linear-gradient(135deg,
      oklch(0.82 0.135 var(--phue, var(--hue)) / 0.07) 0 11px,
      transparent 11px 22px),
    var(--bg2);
  display: grid; place-items: center;
  transition: border-color 0.25s;
}
.pub-figs .ph-stripe { aspect-ratio: 16 / 10; }
.ph-tall .ph-stripe { aspect-ratio: auto; height: 100%; min-height: 360px; }
.ph:hover .ph-stripe { border-color: var(--accent-b); }
.ph-tag {
  position: absolute; left: 11px; bottom: 10px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--faint); border: 1px solid var(--line2); padding: 4px 8px; border-radius: 5px;
  background: color-mix(in oklch, var(--bg) 55%, transparent);
}
.ph-play {
  width: 46px; height: 46px; border-radius: 50%;
  display: grid; place-items: center; color: var(--accent);
  background: color-mix(in oklch, var(--bg) 55%, transparent);
  border: 1px solid var(--accent-b); box-shadow: 0 0 28px var(--glow);
  transition: transform 0.25s var(--ease);
}
.ph:hover .ph-play { transform: scale(1.08); }
/* Real figures. Journal figures come at every aspect ratio (a portrait magazine
   cover next to a wide plot), so neither dimension is pinned: each renders at
   its own size, shrunk only if it would overflow the column. Constraining both
   would letterbox the ground colour in around the image. The white ground
   itself has to stay — matplotlib PNGs are often exported with a transparent
   background and black text (HVC_origins.png is), which would otherwise vanish
   on the dark theme; natural sizing keeps it from letterboxing, and the white
   edge band below is a deliberate frame rather than leftover ground. */
.ph-img {
  display: block; width: auto; max-width: 100%; height: auto;
  /* .ph is a flex column, and a stretched flex item would blow `width: auto`
     back up to the column width (and upscale a small figure past its pixels) */
  align-self: start;
  /* square corners + a solid white band, so each figure reads as a printed
     plate against the dark theme. box-sizing is border-box site-wide, so the
     band eats into max-width rather than overflowing the column. */
  border: 5px solid #fff;
  background: #fff;
}
.ph-cap { font-size: 13.2px; line-height: 1.5; color: var(--muted); text-wrap: pretty; }
.pub-figs .ph-cap { font-size: 12.7px; color: var(--faint); }

/* ───────────────────────── Home / About ───────────────────────── */
.home-page { width: 100%; }

/* Impact hero */
.home-hero {
  position: relative;
  width: 100%;
  min-height: clamp(460px, 78vh, 820px);
  display: grid;
  place-items: center;
  overflow: hidden;
  isolation: isolate;
}
/* The movie is square (1848²) and `cover` scales it to the box's *width*, so on a
   wide display it zooms up hard (while the 3D model behind it is scaled by the
   canvas HEIGHT via the camera's vertical FOV — so the two drift apart and the
   movie over-zooms relative to the model it's meant to match-cut into).
   Capping the width holds that in check. `margin-inline: auto` centers it, since
   .home-hero-bg sets inset:0 and the transform holds the scale.
   NB the painted width is this x the scale (1.06), so ~1590. */
.home-hero-video {
  width: 100%; height: 100%; margin-inline: auto;
  object-fit: cover; object-position: center 42%;
}
/* Static scale only — the old heroDrift zoom-pan animation was a still-image
   effect and is gone now that the hero is a movie; the scale stays because the
   movie→model match cut was tuned against this framing. */
.home-hero-bg {
  position: absolute; inset: 0; z-index: -2;
  background-size: cover;
  background-position: center 42%;
  background-repeat: no-repeat;
  transform: scale(1.06);
}
.home-hero-scrim {
  position: absolute; inset: 0; z-index: -1;
  background:
    radial-gradient(115% 85% at 50% 44%, transparent 14%, color-mix(in oklch, var(--bg) 38%, transparent) 56%, color-mix(in oklch, var(--bg) 88%, transparent) 100%),
    linear-gradient(to bottom, color-mix(in oklch, var(--bg) 40%, transparent) 0%, transparent 24%, transparent 58%, var(--bg) 100%);
}
.home-hero-inner {
  position: relative;
  text-align: center;
  padding: clamp(40px, 8vh, 96px) clamp(20px, 4vw, 56px);
  display: flex; flex-direction: column; align-items: center; gap: clamp(20px, 3vw, 34px);
}
.home-hero-kicker {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-mono); font-size: clamp(12.1px, 1.21vw, 14.3px);
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent);
}
.home-hero-kicker .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 12px var(--accent); }
.home-hero-title {
  font-family: var(--font-display); font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  line-height: 0.98;
  font-size: clamp(30px, 6vw, 94px);
  display: flex; flex-direction: column; max-width: 100%;
  color: #f3f7ff;
  text-shadow: 0 2px 30px rgba(0,0,0,0.1), 0 1px 4px rgba(0,0,0,0.5);
}
.home-hero-line { display: block; white-space: nowrap; }
/* "GAS & GALAXIES": the original accent gradient, lifted brighter (higher
   oklch L, slightly tamer chroma so the lightness reads through). */
.home-hero-line:nth-child(2) {
  background: linear-gradient(96deg, oklch(0.92 0.11 var(--hue)) 0%, oklch(0.96 0.045 60) 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.home-hero-scroll {
  position: absolute; left: 50%; bottom: clamp(22px, 4vh, 44px); transform: translateX(-50%);
  display: inline-flex; flex-direction: column; align-items: center; gap: 7px;
  background: none; border: 0; cursor: pointer; padding: 8px;
  font-family: var(--font-mono); font-size: 12.1px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted); transition: color 0.22s var(--ease);
}
.home-hero-scroll svg { animation: scrollBounce 2.2s ease-in-out infinite; }
.home-hero-scroll:hover { color: var(--text); }
@keyframes scrollBounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(5px); } }
@media (prefers-reduced-motion: reduce) { .home-hero-scroll svg { animation: none; } }

.home {
  max-width: var(--maxw); margin: 0 auto; width: 100%;
  display: flex; flex-direction: column; gap: clamp(36px, 5vw, 64px);
  padding: clamp(28px, 4vw, 60px) clamp(20px, 4vw, 56px) clamp(36px, 4vw, 64px);
}
.home-about {
  display: grid; grid-template-columns: 1fr 2fr;
  gap: clamp(24px, 4vw, 56px); align-items: start;
}
.home-about-id { display: flex; flex-direction: column; gap: 14px; }
.home-role {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-mono); font-size: clamp(13.2px, 1.32vw, 15.4px);
  letter-spacing: 0.04em; color: var(--accent); white-space: nowrap;
}
.home-role .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 12px var(--accent); }
.home-meta {
  display: grid; grid-template-columns: minmax(200px, 300px) 1fr;
  gap: clamp(28px, 4vw, 56px); align-items: start;
  padding-top: clamp(28px, 4vw, 48px); border-top: 1px solid var(--line);
}
.home-photo { position: relative; }
.home-photo img {
  display: block; width: 100%; height: auto; border-radius: 18px;
  aspect-ratio: 4 / 5; object-fit: cover; object-position: center 18%;
  border: 1px solid var(--line2);
  box-shadow: 0 40px 90px -50px var(--glow), inset 0 1px 0 var(--line2);
}
.home-photo-ring {
  position: absolute; inset: -1px; border-radius: 19px; pointer-events: none;
  background: radial-gradient(120% 80% at 30% 0%, var(--glow), transparent 55%);
  mix-blend-mode: screen; opacity: 0.9;
}

.home-text { padding-top: 6px; }
.home-kicker {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-mono); font-size: 13.2px; letter-spacing: 0.04em;
  color: var(--accent); margin-bottom: 18px;
}
.home-kicker .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 12px var(--accent); }
.home-name {
  font-family: var(--font-display); font-weight: 600; letter-spacing: -0.01em;
  text-transform: uppercase;
  font-size: clamp(30px, 3.8vw, 54px); line-height: 1.0; margin: 0;
}
.home-tagline {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(15.4px, 1.54vw, 18.7px); line-height: 1.48; color: var(--muted);
  max-width: 32ch; margin: 0; text-wrap: balance;
}
.home-bio { display: flex; flex-direction: column; gap: 15px; max-width: 72ch; }
.home-bio p { font-size: clamp(16px, 1.65vw, 18.2px); line-height: 1.66; color: var(--muted); text-wrap: pretty; }

.home-contact { border-top: 1px solid var(--line); padding-top: clamp(16px, 2.5vw, 24px); }
.contact-grid { display: grid; grid-template-columns: 110px repeat(4, 1fr); gap: 12px; align-items: center; }
.contact-photo { width: 110px; height: 110px; border-radius: 50%; overflow: hidden; flex: none; }
.contact-photo img { width: 100%; height: 100%; object-fit: cover; object-position: center 10%; display: block; }
.contact {
  display: flex; flex-direction: column; gap: 3px; text-decoration: none;
  background: var(--surface); border: 1px solid var(--line2); border-radius: 11px;
  padding: 12px 16px; transition: all 0.22s var(--ease);
}
.contact:hover { border-color: var(--accent-b); background: var(--accent-d); transform: translateY(-2px); box-shadow: 0 18px 40px -34px var(--glow); }
.contact-label { font-family: var(--font-mono); font-size: 11.6px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint); }
.contact-value { font-size: 15.4px; color: var(--text); }
.contact.is-ph .contact-value { color: var(--faint); font-style: italic; }
.contact.is-ph { border-style: dashed; }

.home-jump {
  position: relative;
  max-width: var(--maxw); margin: 0 auto; width: 100%;
  padding: clamp(20px, 3vw, 36px) clamp(20px, 4vw, 56px);
}
.home-jump::after {
  content: ""; position: absolute; bottom: 0;
  left: clamp(20px, 4vw, 56px); right: clamp(20px, 4vw, 56px);
  border-bottom: 1px solid var(--line);
}
.jump-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.jump-card {
  --jhue: var(--hue);
  display: flex; flex-direction: column; gap: 7px; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 14px; padding: 20px 20px 18px; position: relative;
  transition: border-color 0.25s var(--ease), transform 0.25s var(--ease), box-shadow 0.25s;
}
.jump-card::before {
  content: ""; position: absolute; left: 0; top: 18px; bottom: 18px; width: 3px;
  border-radius: 3px; background: oklch(0.82 0.135 var(--jhue)); opacity: 0.85;
}
[data-theme="editorial"] .jump-card::before { background: oklch(0.55 0.155 var(--jhue)); }
.jump-card:hover { border-color: oklch(0.82 0.135 var(--jhue) / 0.5); transform: translateY(-3px); box-shadow: 0 26px 54px -42px oklch(0.82 0.135 var(--jhue) / 0.5); }
.jump-label { font-family: var(--font-display); font-weight: 600; font-size: 18.7px; letter-spacing: -0.01em; padding-left: 14px; }
.jump-note { font-size: 14.3px; line-height: 1.5; color: var(--muted); padding-left: 14px; }
.jump-arrow { position: absolute; right: 18px; top: 18px; color: oklch(0.82 0.135 var(--jhue)); transition: transform 0.25s var(--ease); }
[data-theme="editorial"] .jump-arrow { color: oklch(0.55 0.155 var(--jhue)); }
.jump-card:hover .jump-arrow { transform: translateX(4px); }

@media (max-width: 860px) {
  .home-intro { grid-template-columns: 1fr; }
  .jump-grid { grid-template-columns: 1fr; }
}

/* ───────────────────────── Footer ───────────────────────── */
.foot {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  max-width: var(--maxw); margin: 0 auto; width: 100%;
  padding: 22px clamp(20px, 4vw, 56px); border-top: 1px solid var(--line);
  font-family: var(--font-mono); font-size: 12.7px; color: var(--faint);
}
.foot-dots { display: flex; gap: 9px; }
.fdot { width: 8px; height: 8px; border-radius: 50%; border: 1px solid var(--line2); background: transparent; cursor: pointer; padding: 0; transition: all 0.25s var(--ease); }
.fdot:hover { border-color: var(--accent); }
.fdot.on { background: var(--accent); border-color: var(--accent); box-shadow: 0 0 10px var(--accent); width: 22px; }
.foot-hint { color: var(--faint); }

/* ───────────────────────── Responsive ───────────────────────── */
@media (max-width: 980px) {
  .pub { grid-template-columns: 1fr; gap: clamp(18px, 3vw, 28px); }
}
@media (max-width: 720px) {
  .topbar { flex-direction: column; align-items: stretch; gap: 12px; padding: 14px 18px; }
  .tabs { justify-content: space-between; }
  .tab { flex: 1; justify-content: center; padding: 9px 8px; }
  .tab-l { font-size: 14.5px; }
  .pub-figs .ph-stripe { aspect-ratio: 16/10; }
  .foot { flex-wrap: wrap; }
  .contact-grid { grid-template-columns: 110px 1fr; }
  .contact-photo { grid-row: 1 / span 4; align-self: center; }
  .contact { padding: 8px 12px; }
  .contact-label { font-size: 10.5px; }
  .contact-value { font-size: 13.8px; }
  .home-about { grid-template-columns: 1fr; gap: clamp(14px, 3vw, 22px); }
  .home-name { text-align: justify; text-align-last: justify; font-size: clamp(40px, 11vw, 58px); }
  .home-name br { display: none; }
  .home-tagline { font-size: 14.3px; white-space: nowrap; max-width: none; }
  /* Phones navigate by tapping the header tabs. The prev/next arrows sit on top
     of a stage that is mostly viz at this width (and the left one lands on the
     species panel), and the footer's dots and keyboard hint are the same journey
     the tabs already offer — the hint's keyboard isn't even there. */
  .arrow { display: none; }
  .foot-hint, .foot-dots { display: none; }
}

/* ───────────────────────── Viz controls (overlay over #viz-stage) ───────────────────────── */
/* Home hero movie fade: on returning to Home the movie fades IN over the 3D
   model as it settles into home framing (start time = HERO_REVEAL_DELAY in
   app.js). Shorten/lengthen this duration to trade off the overlap window where
   the model is still visible through the movie. */
.home-hero-bg { transition: opacity 0.8s var(--ease); }
.home-hero-bg.viz-faded { opacity: 0; }

/* Persistent overlay holding the viz controls — a sibling of (and above) the
   sliding .stage, so the controls stay put while page content slides. Sized to
   the hero so the time scrubber can anchor to its bottom. */
.viz-overlay {
  position: absolute; top: 0; left: 0; right: 0;
  height: clamp(460px, 78vh, 820px);  /* = .viz-anchor / hero height */
  z-index: 5; pointer-events: none;
}
.viz-overlay-inner {
  max-width: var(--maxw); margin: 0 auto; width: 100%;
  padding: clamp(28px, 5vh, 52px) clamp(20px, 4vw, 56px) 0;
  display: flex; flex-direction: column; align-items: flex-start;
  gap: clamp(12px, 1.6vw, 20px);
}
/* mimics the hero title's height so the panels land right under the title */
.viz-overlay-spacer { height: clamp(20px, 6vw, 64px); flex: none; }

/* Generic control panel — glass chip like .hero-viz-tag */
.viz-controls {
  align-self: flex-start; z-index: 6;
  display: flex; flex-direction: column; gap: 10px;
  padding: 12px 14px; max-width: min(260px, 60vw);
  font-family: var(--font-mono); font-size: 12.7px; color: var(--text);
  background: color-mix(in oklch, var(--bg) 64%, transparent);
  backdrop-filter: blur(8px) saturate(1.1);
  border: 1px solid var(--line); border-radius: 12px;
  pointer-events: auto;
}
.viz-controls .vc-row { display: flex; align-items: center; gap: 8px; }
.viz-controls label { display: flex; align-items: center; gap: 7px; cursor: pointer; }
/* Species picker — one button per ion, 2x2 so "Total gas" fits (4 across would be
   ~58px each in the 260px panel; phones go to a single column, see below).
   Single-select: the shader binds one ion pair at a time, so these behave like
   radios, not toggles. */
.viz-controls .vc-fields {
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px;
}
.viz-controls .vc-fields button {
  appearance: none; cursor: pointer; width: 100%; position: relative;
  font-family: var(--font-mono); font-size: 12.7px; color: var(--muted);
  background: color-mix(in oklch, var(--bg) 40%, transparent);
  border: 1px solid var(--line); border-radius: 7px;
  padding: 5px 6px;
  transition: color 0.15s var(--ease), background 0.15s var(--ease),
              border-color 0.15s var(--ease);
}
.viz-controls .vc-fields button:hover { color: var(--text); }
.viz-controls .vc-fields button.active {
  background: var(--accent-d); color: var(--accent);
  border-color: color-mix(in oklch, var(--accent) 45%, transparent);
}
/* Switching ion fetches a new pair of cubes — a second or two on a cold one, on
   a phone longer. The pressed button carries its own spinner so the click reads
   as registered while the image is still the old ion's.
   The spinner takes the label's place rather than sitting beside it: the buttons
   are ~83px wide and "Total gas" needs 69px of that, so there is no free margin
   to put a ring in. The button box is unchanged, so nothing reflows, and the
   accent border still marks which ion is now selected.
   Both the spinner's fade-in and the label's fade-out are delayed by the same
   0.15s, so an already-cached ion — which swaps in a few ms — never flickers. */
.viz-controls .vc-fields button.is-loading {
  color: transparent; transition: color 0.1s linear 0.15s;
}
.viz-controls .vc-fields button.is-loading::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 12px; height: 12px; margin: -6px 0 0 -6px;
  border-radius: 50%; opacity: 0;
  border: 1.5px solid color-mix(in oklch, var(--accent) 28%, transparent);
  border-top-color: var(--accent);
  animation: vcSpin 0.7s linear infinite, vcSpinIn 0.12s linear 0.15s forwards;
}
@keyframes vcSpin { to { transform: rotate(360deg); } }
@keyframes vcSpinIn { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  /* a static ring still reads as pending, without the spin */
  .viz-controls .vc-fields button.is-loading::after { animation: vcSpinIn 0.12s linear 0.15s forwards; }
}
.viz-controls input[type="range"] { width: 110px; accent-color: var(--accent); cursor: pointer; vertical-align: middle; }
.viz-controls .vc-swatch { width: 11px; height: 11px; border-radius: 3px; flex: none; }

/* Phones: a 2x2 of ion buttons is most of a narrow stage, and the labels are
   wide enough that a shorter panel would wrap them. Stack the ions one per row
   instead and step the whole panel down a size — the column is narrow, so the
   Column/Volume toggle shrinks with it to stay on one line. */
@media (max-width: 720px) {
  .viz-controls {
    gap: 8px; padding: 9px 10px; font-size: 11.4px;
    max-width: min(160px, 46vw);
  }
  .viz-controls .vc-fields { grid-template-columns: 1fr; gap: 4px; }
  .viz-controls .vc-fields button { font-size: 11.4px; padding: 4px 6px; }
  .viz-controls .vc-fields button.is-loading::after {
    width: 10px; height: 10px; margin: -5px 0 0 -5px;
  }
  .viz-controls input[type="range"] { width: 100%; }
  .vc-seg button { font-size: 11px; padding: 5px 8px; }
}

/* Magellanic legend: the colored dot IS the show/hide toggle (desaturated when off) */
.viz-legend { gap: 8px; }
.legend-item {
  display: flex; align-items: center; gap: 8px;
  appearance: none; border: 0; background: transparent; cursor: pointer;
  padding: 2px 0; color: var(--text);
  font-family: var(--font-mono); font-size: 13.2px;
}
.legend-dot {
  width: 13px; height: 13px; border-radius: 50%; flex: none;
  box-shadow: 0 0 8px color-mix(in oklch, currentColor 0%, transparent);
  transition: filter 0.2s var(--ease), opacity 0.2s var(--ease);
}
.legend-item:hover .legend-name { color: var(--text); }
.legend-item.off { color: var(--muted); }
.legend-item.off .legend-dot { filter: saturate(0.18) brightness(0.85); opacity: 0.6; }
/* orbit-track glyph: a short line in the two galaxies' (brightened) colors */
.legend-dot.legend-line {
  height: 3px; border-radius: 999px;
  background: linear-gradient(90deg, #8cc6ff, #ffb885);
}

/* segmented toggle (mode / preset) */
.vc-seg { display: inline-flex; border: 1px solid var(--line2); border-radius: 8px; overflow: hidden; }
.vc-seg button {
  appearance: none; border: 0; background: transparent; color: var(--muted);
  font-family: var(--font-mono); font-size: 12.1px; padding: 6px 10px; cursor: pointer;
}
.vc-seg button + button { border-left: 1px solid var(--line); }
.vc-seg button:hover { color: var(--text); }
.vc-seg button.active { background: var(--accent-d); color: var(--accent); }

/* Magellanic time scrubber — bare slider, bottom-center, with a label that
   rides under the thumb */
.viz-timebar {
  position: absolute; left: 50%; bottom: 26px; transform: translateX(-50%);
  z-index: 6; width: min(360px, 84vw); box-sizing: border-box;
  pointer-events: auto;
  font-family: var(--font-mono); font-size: 13.2px; color: var(--text);
}
.time-slider {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 4px; border-radius: 999px; cursor: pointer;
  background: color-mix(in oklch, var(--text) 28%, transparent);
  outline: none;
}
.time-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 14px; height: 14px; border-radius: 50%; border: 0;
  background: var(--accent); box-shadow: 0 0 10px var(--accent); cursor: pointer;
}
.time-slider::-moz-range-thumb {
  width: 14px; height: 14px; border: 0; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 10px var(--accent); cursor: pointer;
}
.time-label-track { position: relative; width: 100%; height: 16px; margin-top: 7px; }
.time-label {
  position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap;
  font-weight: 600; color: var(--accent);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.5);
}
.time-preload { margin-top: 4px; text-align: center; font-size: 10.5px; color: var(--faint); }

/* small loading pill */
.viz-loading {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  z-index: 6; pointer-events: none;
  font-family: var(--font-mono); font-size: 13.2px; color: var(--muted);
  background: color-mix(in oklch, var(--bg) 66%, transparent);
  border: 1px solid var(--line); border-radius: 999px; padding: 8px 16px;
  backdrop-filter: blur(6px);
}
