/* ─── /lore v2 — single-page narrative app ───────────────────────
   Layered on top of lore.css (which still provides the design
   tokens: --lore-ink*, --lore-teal*, --lore-amber*, --lore-display,
   --lore-mono, etc.). All v2 components live under #lore-app.

   State model:
     #lore-app[data-state="cover"|"reading"|"hub"|"scene"]
     #lore-app[data-tone="amber"|"teal"|"violet"|"dark"]
     #lore-app[data-transition="entering"|"exiting"|"idle"]

   Mobile-first; landscape-favoured on small phones, with a soft
   suggestion banner when portrait is detected. Honours
   prefers-reduced-motion throughout.
   ──────────────────────────────────────────────────────────── */

/* ─── Boot preloader (overlay markup in lore.njk; driven by loader.js) ─── */
.lore-loader {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: radial-gradient(ellipse 95% 72% at 50% 40%, #07120c, #020604 72%);
  opacity: 1;
  transition: opacity 0.6s ease;
}
.lore-loader.is-done { opacity: 0; pointer-events: none; }
/* The real starfield + comets (mountStars) painted behind the panel. */
.lore-loader .lore-stars { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.lore-loader-inner {
  position: relative;
  z-index: 1;
  text-align: center;
  max-width: min(560px, 86vw);
  padding: 24px;
}
.lore-loader-glyph {
  font-size: 46px;
  line-height: 1;
  color: #00c8b4;
  text-shadow: 0 0 26px rgba(0, 200, 180, 0.7), 0 0 60px rgba(0, 200, 180, 0.35);
  animation: lore-loader-pulse 2.1s ease-in-out infinite;
}
.lore-loader-kicker {
  font-family: 'Share Tech Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.5em;
  text-indent: 0.5em;
  text-transform: uppercase;
  color: rgba(120, 165, 152, 0.5);
  margin: 14px 0 18px;
}
.lore-loader-track {
  display: block;
  width: min(360px, 70vw);
  margin: 18px auto 16px;
  font-family: 'Share Tech Mono', ui-monospace, monospace;
  font-size: 16px;
  letter-spacing: 0.18em;
  line-height: 1;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
}
/* Decoding-glyph progress: loader.js renders <b>decoded blocks</b> + a flickering
   scramble tail that resolves as the load completes (like the title decrypt). */
.lore-loader-bar {
  display: block;
  color: rgba(0, 200, 180, 0.34);                /* undecoded scramble = dim */
  text-shadow: 0 0 8px rgba(0, 200, 180, 0.22);
}
.lore-loader-bar b {
  font-weight: 400;
  color: #51e9b6;                                 /* decoded = bright */
  text-shadow: 0 0 12px rgba(0, 200, 180, 0.85);
}
/* Once loaded the bar has done its job — fade it out (a loading bar loads, then
   disappears), leaving the entry screen as just the tips + settings + Enter. */
.lore-loader.is-ready .lore-loader-track {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.7s ease 0.35s;
}
.lore-loader-fact {
  font-family: 'Share Tech Mono', ui-monospace, monospace;
  font-size: 12.5px;
  line-height: 1.65;
  letter-spacing: 0.03em;
  color: #8fb8b0;
  margin: 0;
  min-height: 3.3em;
  transition: opacity 0.35s ease;
}
.lore-loader-settings {
  display: flex;
  flex-direction: column;
  gap: 11px;
  width: min(326px, 78vw);
  margin: 20px auto 6px;
  font-family: 'Share Tech Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #9fc4bd;
}
.lore-loader-setting {
  display: grid;
  grid-template-columns: 1fr auto minmax(70px, 120px);
  align-items: center;
  gap: 14px;
  cursor: pointer;
}
.lore-loader-setting-name { text-align: left; white-space: nowrap; }

/* Sci-fi toggle: an angular track + a knob that slides across and lights up. */
.lore-loader-toggle {
  -webkit-appearance: none;
  appearance: none;
  position: relative;
  width: 42px;
  height: 20px;
  margin: 0;
  background: rgba(0, 200, 180, 0.06);
  border: 1px solid rgba(0, 200, 180, 0.38);
  border-radius: 2px;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.lore-loader-toggle::before {
  content: '';
  position: absolute;
  top: 1px;
  left: 1px;
  width: 16px;
  height: 16px;
  background: #466864;
  border-radius: 1px;
  transition: transform 0.2s cubic-bezier(.4, 1.3, .5, 1), background 0.2s ease, box-shadow 0.2s ease;
}
.lore-loader-toggle:checked {
  background: rgba(0, 200, 180, 0.16);
  border-color: #00c8b4;
  box-shadow: 0 0 10px rgba(0, 200, 180, 0.4), inset 0 0 7px rgba(0, 200, 180, 0.25);
}
.lore-loader-toggle:checked::before {
  transform: translateX(22px);
  background: #00c8b4;
  box-shadow: 0 0 9px rgba(0, 200, 180, 0.9);
}
.lore-loader-toggle:focus-visible { outline: 2px solid #51e9b6; outline-offset: 2px; }

/* Sci-fi slider: glowing track + diamond thumb. */
.lore-loader-range {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 16px;
  margin: 0;
  background: transparent;
  cursor: pointer;
}
.lore-loader-range::-webkit-slider-runnable-track {
  height: 3px;
  background: linear-gradient(90deg, rgba(0, 200, 180, 0.6), rgba(0, 200, 180, 0.12));
}
.lore-loader-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 11px;
  height: 15px;
  margin-top: -6px;
  background: #00c8b4;
  box-shadow: 0 0 9px rgba(0, 200, 180, 0.9);
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
}
.lore-loader-range::-moz-range-track {
  height: 3px;
  background: linear-gradient(90deg, rgba(0, 200, 180, 0.6), rgba(0, 200, 180, 0.12));
}
.lore-loader-range::-moz-range-thumb {
  width: 11px;
  height: 15px;
  border: 0;
  background: #00c8b4;
  box-shadow: 0 0 9px rgba(0, 200, 180, 0.9);
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
}

/* Camera-speed preview: a dot orbits at the speed the slider sets, so users judge
   the Reach camera feel in the loader (the scene is at the END of the lore).
   loader.js sets the spin duration; the dot revolves with the orbit container. */
.lore-loader-cam-preview {
  position: relative;
  width: 24px; height: 24px;
  justify-self: center;
  border-radius: 50%;
  border: 1px solid rgba(0, 200, 180, 0.28);
  box-shadow: inset 0 0 8px rgba(0, 200, 180, 0.1);
}
.lore-loader-cam-orbit { position: absolute; inset: 0; animation: lore-cam-spin 2.4s linear infinite; }
.lore-loader-cam-dot {
  position: absolute; top: -3px; left: 50%;
  width: 6px; height: 6px; margin-left: -3px;
  border-radius: 50%;
  background: #00c8b4; box-shadow: 0 0 7px rgba(0, 200, 180, 0.9);
}
@keyframes lore-cam-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .lore-loader-cam-orbit { animation: none; } }
.lore-loader-enter {
  position: relative;
  margin-top: 18px;
  padding: 14px 42px;
  font-family: 'Audiowide', 'Orbitron', monospace;
  font-size: 14px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: #eafff9;
  background: linear-gradient(120deg, rgba(0, 95, 85, 0.3), rgba(0, 168, 150, 0.34) 50%, rgba(0, 95, 85, 0.3));
  border: 1px solid #00c8b4;
  clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
  cursor: pointer;
  overflow: hidden;
  text-shadow: 0 0 12px rgba(0, 200, 180, 0.85);
  box-shadow: 0 0 22px rgba(0, 200, 180, 0.45), inset 0 0 18px rgba(0, 200, 180, 0.18);
  transition: transform 0.16s ease, box-shadow 0.3s ease, background 0.3s ease, opacity 0.35s ease;
}
.lore-loader-enter::before {
  content: '';
  position: absolute;
  top: 0;
  left: -60%;
  width: 45%;
  height: 100%;
  background: linear-gradient(100deg, transparent, rgba(220, 255, 250, 0.5), transparent);
  transform: skewX(-18deg);
  pointer-events: none;
}
.lore-loader-enter:not(:disabled) { animation: lore-enter-pulse 2.6s ease-in-out infinite; }
.lore-loader-enter:not(:disabled)::before { animation: lore-enter-sweep 3.4s ease-in-out infinite; }
.lore-loader-enter:hover {
  transform: translateY(-1px) scale(1.02);
  box-shadow: 0 0 36px rgba(0, 200, 180, 0.72), inset 0 0 24px rgba(0, 200, 180, 0.3);
}
.lore-loader-enter:focus-visible { outline: 2px solid #6bf0c6; outline-offset: 4px; }
.lore-loader-enter:disabled {
  cursor: progress;
  opacity: 0.3;
  color: #7fa8a0;
  background: rgba(36, 74, 69, 0.55);
  border-color: rgba(0, 200, 180, 0.22);
  box-shadow: none;
  text-shadow: none;
}
.lore-loader-enter:disabled::before { display: none; }
@keyframes lore-loader-pulse {
  0%, 100% { opacity: 0.6; transform: scale(1); }
  50%      { opacity: 1; transform: scale(1.08); }
}
@keyframes lore-enter-pulse {
  0%, 100% { box-shadow: 0 0 18px rgba(0, 200, 180, 0.4), inset 0 0 14px rgba(0, 200, 180, 0.16); }
  50%      { box-shadow: 0 0 32px rgba(0, 200, 180, 0.68), inset 0 0 20px rgba(0, 200, 180, 0.28); }
}
@keyframes lore-enter-sweep {
  0%        { left: -60%; }
  55%, 100% { left: 130%; }
}
@media (prefers-reduced-motion: reduce) {
  .lore-loader-glyph,
  .lore-loader-enter:not(:disabled),
  .lore-loader-enter:not(:disabled)::before { animation: none; }
}
@media (orientation: landscape) and (max-height: 540px) {
  .lore-loader-inner { padding: 12px; }
  .lore-loader-glyph { font-size: 32px; }
  .lore-loader-kicker { font-size: 10px; letter-spacing: 0.42em; margin: 8px 0 10px; }
  .lore-loader-fact { font-size: 10.5px; line-height: 1.5; min-height: 2.6em; }
  .lore-loader-settings { gap: 7px; margin: 11px auto 3px; }
  .lore-loader-track { margin: 10px auto 9px; font-size: 12.5px; letter-spacing: 0.1em; width: min(300px, 72vw); }
  .lore-loader-enter { margin-top: 8px; padding: 11px 32px; font-size: 12.5px; }
}

/* Desktop / large viewports: the loader is phone-sized by default and looks tiny
   with lots of empty space on a big screen. Scale the type, controls, decoding
   bar, and Enter button up. Phones keep the base/landscape sizes. */
@media (min-width: 760px) and (min-height: 600px) {
  .lore-loader-inner { max-width: min(680px, 80vw); padding: 32px; }
  .lore-loader-glyph { font-size: 64px; }
  .lore-loader-kicker { font-size: 13px; letter-spacing: 0.55em; margin: 20px 0 24px; }
  .lore-loader-track { width: min(460px, 56vw); font-size: 22px; letter-spacing: 0.24em; margin: 26px auto 22px; }
  .lore-loader-fact { font-size: 15px; line-height: 1.7; min-height: 3.4em; }
  .lore-loader-settings { width: min(440px, 66vw); gap: 16px; font-size: 13px; margin: 30px auto 12px; }
  .lore-loader-setting { gap: 18px; }
  .lore-loader-toggle { width: 52px; height: 25px; }
  .lore-loader-toggle::before { width: 21px; height: 21px; }
  .lore-loader-toggle:checked::before { transform: translateX(27px); }
  .lore-loader-range { height: 22px; }
  .lore-loader-range::-webkit-slider-runnable-track { height: 4px; }
  .lore-loader-range::-webkit-slider-thumb { width: 14px; height: 20px; margin-top: -8px; }
  .lore-loader-range::-moz-range-thumb { width: 14px; height: 20px; }
  .lore-loader-cam-preview { width: 30px; height: 30px; }
  .lore-loader-cam-dot { width: 7px; height: 7px; margin-left: -3.5px; top: -3.5px; }
  .lore-loader-enter { margin-top: 28px; padding: 18px 56px; font-size: 17px; letter-spacing: 0.3em; }
}

/* ─── App mount + global stage ──────────────────────────────── */
#lore-app {
  /* Legacy lore.css scoped its design tokens under `.lore-page`. v2's
     shell is `#lore-app`, so the embedded legacy scenes (Three.js reach,
     voyage scrubber) lose their token bindings unless we mirror them
     here. Mirror the full token block. */
  --lore-ink:    var(--text-bright, #e6edf3);
  --lore-ink-2:  var(--text, #b8d4cc);
  --lore-ink-3:  #8b9d96;
  --lore-ink-4:  #5a6e68;
  --lore-mono:   var(--font-mono, 'Share Tech Mono', ui-monospace, monospace);
  --lore-serif:  var(--font-body, 'Exo 2', sans-serif);
  --lore-display:'Audiowide', 'Orbitron', monospace;
  --lore-epic:   'Audiowide', 'Orbitron', monospace;
  --lore-impact: 'Saira Stencil One', 'Audiowide', system-ui, sans-serif;
  --lore-teal:        #00c8b4;
  --lore-teal-bright: #51e9b6;
  --lore-amber:       #e8a020;
  --lore-amber-bright:#ffc858;
  --lore-teal-dim:    rgba(0, 200, 180, 0.7);
  --lore-amber-dim:   rgba(232, 160, 32, 0.7);
  --lore-teal-glow:   0 0 24px rgba(0, 200, 180, 0.5);
  --lore-amber-glow:  0 0 24px rgba(232, 160, 32, 0.5);

  position: relative;
  display: block;
  min-height: 100dvh;
  background: #02080b;
  color: var(--lore-ink-2, #b8d4cc);
  overflow: hidden;
  isolation: isolate;
}

/* ── Immersive fullscreen: the lore experience IS the page ───────────────────
   The lore has its own top bar (the HUD: Index / Cover / Mute), so the global
   site nav was a redundant SECOND bar that (a) read as a "frame" the lore sat
   below, (b) ate 64px on a 390px-tall landscape phone, and (c) via
   .page-content's 64px padding-top against #lore-app's 100vh, made EVERY chapter
   overflow by exactly 64px (the stray scroll). Hide it and drop the offset so
   #lore-app fills the whole viewport. lore-v2.css loads ONLY on /lore, so these
   global selectors can't affect any other page. */
#nav { display: none; }
.page-content { padding-top: 0; }

/* A way OUT of the immersive archive (the site nav is hidden here, so without
   this a reader is trapped and would have to edit the URL): a small "Exit" in
   the reading HUD and a "Leave" on the cover, both linking back to the site. */
/* Exit is the way OUT (site nav is hidden here) — brighter than the other HUD
   buttons so it's unmistakable, with a faint accent tint. */
.lore-hud-btn--exit {
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  color: var(--lore-ink, #e6edf3);
  border-color: color-mix(in srgb, var(--tone-accent, #00c8b4) 38%, rgba(184,212,204,0.18));
}
.lore-cover-exit {
  position: fixed;
  top: clamp(12px, 3vh, 20px);
  left: clamp(14px, 4vw, 28px);
  z-index: 6;
  font-family: var(--lore-mono, monospace);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--lore-ink-2, #b8d4cc);
  text-decoration: none;
  padding: 6px 10px;
  border: 1px solid rgba(184,212,204,0.22);
  border-radius: 4px;
  transition: color 0.2s, border-color 0.2s;
}
.lore-cover-exit:hover {
  color: var(--tone-accent, #00c8b4);
  border-color: color-mix(in srgb, var(--tone-accent, #00c8b4) 40%, transparent);
}

/* ── Lore = fixed full-viewport app; the PAGE itself never scrolls ───────────
   On mobile a document even a hair taller than the visual viewport lets you
   drag-scroll, which shows/hides the URL bar — the stray "weird" scroll the user
   hit on chapters that already fit. Pin the app to the viewport and route any
   genuine overflow (tall chapters: covenants, telemetry) through ONE internal
   scroller (.lore-screen), with the HUD pinned so it never scrolls away. The hub
   (position:fixed) and scenes ([data-galaxy-active]) escape this on their own.
   (lore-v2.css loads only on /lore, so html/body here can't affect other pages.) */
html, body { height: 100%; overflow: hidden; overscroll-behavior: none; }
#lore-app { position: fixed; inset: 0; overflow: hidden; }
.lore-hud { position: fixed; }
.lore-screen {
  height: 100dvh;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}
/* A scene (Reach galaxy / voyage) paints a full-viewport WebGL canvas at
   #lore-app level, BELOW .lore-screen (z:0 vs z:2). If .lore-screen stays a
   100dvh scroll container it sits ON TOP of that canvas and swallows the
   finger-drag — which is what broke the Reach orbit on touch. While a scene is
   active, collapse .lore-screen to its content (height:auto) so the canvas
   underneath receives the gesture; the HUD / nav / Reach chrome are fixed +
   hoisted, so they keep their own taps. */
#lore-app[data-galaxy-active="true"] .lore-screen { height: auto; overflow: visible; }

/* Per-tone accent variables. Each Act sets data-tone on #lore-app,
   and the CSS below maps it to a single --tone-accent that the
   components reference. Keeps per-tone variants out of every rule. */
#lore-app                    { --tone-accent: var(--lore-teal,   #00c8b4); --tone-glow: rgba(0,200,180,0.5); --tone-soft: rgba(0,200,180,0.18); }
#lore-app[data-tone="amber"]  { --tone-accent: var(--lore-amber,  #e8a020); --tone-glow: rgba(232,160,32,0.5); --tone-soft: rgba(232,160,32,0.18); }
#lore-app[data-tone="teal"]   { --tone-accent: var(--lore-teal,   #00c8b4); --tone-glow: rgba(0,200,180,0.5);  --tone-soft: rgba(0,200,180,0.18); }
#lore-app[data-tone="violet"] { --tone-accent: #8a72e0;                     --tone-glow: rgba(138,114,224,0.5);--tone-soft: rgba(138,114,224,0.18); }
#lore-app[data-tone="dark"]   { --tone-accent: #4ac0a8;                     --tone-glow: rgba(74,192,168,0.5); --tone-soft: rgba(74,192,168,0.18); }

/* Ambient background — slow-drifting nebula, lower contrast than
   the v1 sky so the page feels paged-out rather than scrolled. */
.lore-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 60% 45% at 20% 20%, var(--tone-soft), transparent 70%),
    radial-gradient(ellipse 70% 50% at 80% 75%, rgba(0,150,130,0.10), transparent 70%),
    radial-gradient(ellipse 110% 25% at 50% 100%, rgba(0,150,130,0.12), transparent 70%),
    linear-gradient(to bottom, #02080b 0%, #030b10 60%, #02080b 100%);
  transition: background 0.8s ease;
}
.lore-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/></filter><rect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/></svg>");
  opacity: 0.04;
  mix-blend-mode: overlay;
  pointer-events: none;
}
/* Background stars + meteors canvas. Fixed so it follows the viewport
   on scroll; sits above the nebula gradient and below the vignette. */
.lore-stars {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.85;
}

.lore-vignette {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  /* Softer, more diffuse vignette so the edges of the stage don't read
     as a hard rectangular frame around the Act V galaxy. */
  background:
    radial-gradient(ellipse 120% 120% at 50% 50%, transparent 70%, rgba(0,0,0,0.32) 100%);
}
/* Inside the reach scene the vignette is fully off. */
#lore-app[data-tone="dark"] .lore-vignette { opacity: 0; }

/* ─── HUD chrome (top bar, mute, index) ─────────────────────── */
.lore-hud {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px clamp(14px, 3vw, 28px);
  font-family: var(--lore-mono, monospace);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--lore-ink-3, #8b9d96);
  pointer-events: none;
}
.lore-hud > * { pointer-events: auto; }

.lore-hud-progress {
  display: flex;
  align-items: center;
  gap: 12px;
  opacity: 0.75;
}
.lore-hud-progress-act {
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: 14px;
  letter-spacing: 0.22em;
  color: var(--tone-accent);
  text-shadow: 0 0 14px var(--tone-glow);
}
.lore-hud-progress-dots { display: inline-flex; gap: 6px; }
.lore-hud-progress-dots i {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: rgba(184,212,204,0.25);
  display: inline-block;
}
.lore-hud-progress-dots i.is-on { background: var(--tone-accent); box-shadow: 0 0 8px var(--tone-glow); }
.lore-hud-progress-dots i.is-current { background: var(--tone-accent); transform: scale(1.6); }

.lore-hud-actions { display: inline-flex; gap: 6px; }
.lore-hud-btn {
  appearance: none;
  background: transparent;
  border: 1px solid rgba(184,212,204,0.18);
  color: var(--lore-ink-3, #8b9d96);
  font: inherit;
  letter-spacing: 0.18em;
  padding: 6px 12px;
  cursor: pointer;
  transition: border-color 0.25s, color 0.25s, background 0.25s;
}
.lore-hud-btn:hover,
.lore-hud-btn:focus-visible {
  outline: none;
  border-color: var(--tone-accent);
  color: var(--tone-accent);
  background: rgba(0,200,180,0.04);
}
.lore-hud-btn[data-active="true"] { color: var(--tone-accent); border-color: var(--tone-accent); }
/* Mute + Fullscreen buttons hold an SVG icon instead of text — centre it. */
.lore-hud-btn[data-action="mute"],
.lore-hud-btn[data-action="fullscreen"] { display: inline-flex; align-items: center; justify-content: center; }

/* ─── Cover splash ─────────────────────────────────────────── */
.lore-cover {
  position: relative;
  z-index: 2;
  min-height: 100vh;
  display: grid;
  place-items: center;
  text-align: center;
  padding: clamp(24px, 6vw, 80px);
}
.lore-cover-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(18px, 3vw, 36px);
  max-width: 720px;
}
.lore-cover-eyebrow {
  font-family: var(--lore-mono, monospace);
  font-size: 11px;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--lore-ink-3, #8b9d96);
  opacity: 0.7;
}
.lore-cover-mark {
  width: clamp(120px, 16vw, 180px);
  height: clamp(120px, 16vw, 180px);
  border: 1px solid rgba(0,200,180,0.4);
  border-radius: 50%;
  position: relative;
  display: grid;
  place-items: center;
  box-shadow: 0 0 60px rgba(0,200,180,0.15), inset 0 0 30px rgba(0,200,180,0.08);
}
.lore-cover-mark::before,
.lore-cover-mark::after {
  content: "";
  position: absolute;
  border: 1px solid rgba(0,200,180,0.2);
  border-radius: 50%;
  pointer-events: none;
}
.lore-cover-mark::before { inset: -12px; }
.lore-cover-mark::after  { inset: -24px; opacity: 0.55; }
.lore-cover-mark-glyph {
  font-family: var(--lore-impact, 'Saira Stencil One', sans-serif);
  font-size: clamp(36px, 5vw, 56px);
  letter-spacing: 0.05em;
  color: var(--lore-teal-bright, #51e9b6);
  text-shadow: 0 0 24px rgba(0,200,180,0.6);
}
.lore-cover-mark-img {
  width: 80%;
  height: 80%;
  object-fit: contain;
  filter: drop-shadow(0 0 18px rgba(0,200,180,0.4));
}
.lore-cover-title {
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: clamp(36px, 6.4vw, 66px);
  letter-spacing: 0.06em;
  margin: 0;
  color: var(--lore-ink, #e6edf3);
  text-shadow: 0 0 32px rgba(0,200,180,0.35);
  line-height: 1.1;
}
.lore-cover-subtitle {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: clamp(15px, 1.6vw, 18px);
  color: var(--lore-ink-2, #b8d4cc);
  margin: 0;
  opacity: 0.85;
  max-width: 50ch;
}
.lore-cover-ctas {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: 8px;
  justify-content: center;
}
.lore-cover-cta {
  appearance: none;
  background: transparent;
  border: 1px solid var(--tone-accent);
  color: var(--tone-accent);
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: 14px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  padding: 16px 36px;
  cursor: pointer;
  position: relative;
  transition: background 0.25s, color 0.25s, transform 0.25s, box-shadow 0.25s;
}
.lore-cover-cta::before,
.lore-cover-cta::after {
  content: "";
  position: absolute;
  width: 12px; height: 12px;
  border: 1px solid var(--tone-accent);
}
.lore-cover-cta::before { top: -1px; left: -1px;  border-right: 0; border-bottom: 0; }
.lore-cover-cta::after  { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }
.lore-cover-cta:hover,
.lore-cover-cta:focus-visible {
  outline: none;
  background: rgba(0,200,180,0.08);
  transform: translateY(-2px);
}
.lore-cover-cta--ghost {
  border-color: rgba(184,212,204,0.3);
  color: var(--lore-ink-3, #8b9d96);
  letter-spacing: 0.24em;
  padding: 12px 24px;
  font-size: 12px;
}
.lore-cover-foot {
  position: absolute;
  bottom: clamp(20px, 4vw, 40px);
  left: 0; right: 0;
  text-align: center;
  font-family: var(--lore-mono, monospace);
  font-size: 10px;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--lore-ink-4, #5a6e68);
  opacity: 0.7;
}

/* ─── Stage (reading view) ────────────────────────────────── */
.lore-stage {
  position: relative;
  z-index: 2;
  min-height: 100dvh;
  display: grid;
  grid-template-rows: 1fr auto;
  padding: clamp(44px, 7vh, 88px) clamp(20px, 4vw, 48px) clamp(32px, 5vh, 56px);
  max-width: 1100px;
  margin: 0 auto;
  box-sizing: border-box;
}

/* Group the chapter head + body so a short chapter's title and prose sit
   TOGETHER, vertically centred, instead of the title pinning to the top while a
   single line floats in the middle of a tall screen (the big-screen "huge gap").
   The nav keeps its own bottom grid row. A tall chapter fills the row and
   top-aligns naturally (the row grows past the viewport and the page scrolls). */
.lore-stage-main {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: stretch;   /* keep children full-width (cards grid, centred prose); grouping is vertical */
  min-height: 0;
  width: 100%;
}

/* Big-screen comfort: the reading content reads a touch small on 1440p+ displays
   (FHD is already great). Scale ONLY the content group up — not the HUD (it's
   pixel-perfect) and not the layout structure, so every element keeps its place
   and just grows (text, sigils, cards, icons). `zoom` reflows (unlike
   transform:scale) so the centred block stays centred. No effect below 2560px. */
@media (min-width: 2560px) { #lore-app:not([data-galaxy-active="true"]) .lore-stage-main, .lore-cover-inner { zoom: 1.12; } }
@media (min-width: 3840px) { #lore-app:not([data-galaxy-active="true"]) .lore-stage-main, .lore-cover-inner { zoom: 1.24; } }

.lore-stage-head {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  margin-bottom: clamp(20px, 3vh, 32px);
}
.lore-stage-eyebrow {
  font-family: var(--lore-mono, monospace);
  font-size: 11px;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--tone-accent);
  opacity: 0.85;
  display: inline-flex;
  align-items: center;
  gap: 12px;
}
.lore-stage-eyebrow::before,
.lore-stage-eyebrow::after {
  content: "";
  width: 24px;
  height: 1px;
  background: currentColor;
  opacity: 0.5;
}
.lore-stage-act {
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: clamp(28px, 3.2vw, 36px);
  letter-spacing: 0.06em;
  color: var(--lore-ink, #e6edf3);
  text-shadow: 0 0 20px var(--tone-glow);
  margin: 0;
}
.lore-stage-chapter {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: 14px;
  color: var(--lore-ink-3, #8b9d96);
  margin: 0;
  letter-spacing: 0.03em;
}

.lore-stage-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  min-height: 0;   /* head+body are centred as a group by .lore-stage-main now */
}

/* ─── Event kinds ─────────────────────────────────────────── */

/* narrative - large prose, centered. Generous size for readability. */
.lore-event-narrative {
  max-width: 760px;
  text-align: center;
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: clamp(20px, 2.2vw, 26px);
  line-height: 1.6;
  color: var(--lore-ink, #e6edf3);
}
.lore-event-narrative p { margin: 0 0 1em; }
.lore-event-narrative p:last-child { margin-bottom: 0; }
.lore-event-narrative em { font-style: italic; color: var(--lore-teal-bright, #51e9b6); }
.lore-em-amber { color: var(--lore-amber-bright, #ffc858); font-style: italic; }
.lore-event-narrative .lore-event-tag {
  display: inline-block;
  margin-top: 24px;
  font-family: var(--lore-mono, monospace);
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--tone-accent);
  opacity: 0.8;
  border: 1px solid var(--tone-soft);
  padding: 6px 14px;
}

/* figure — image + caption + optional body */
.lore-event-figure {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(20px, 3vw, 40px);
  align-items: center;
  max-width: 1000px;
  width: 100%;
}
@media (min-width: 800px) {
  .lore-event-figure { grid-template-columns: minmax(280px, 1fr) 1.2fr; }
}
.lore-event-figure-imgwrap {
  position: relative;
  aspect-ratio: 1 / 1;
  background: transparent;
  filter: drop-shadow(0 0 60px var(--tone-soft));
}
.lore-event-figure-img {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
}
.lore-event-figure-img.is-placeholder {
  /* used when the image 404s; CSS renders a dying-world stand-in */
  background: radial-gradient(circle at 35% 35%,
    rgba(255,200,120,0.4) 0%,
    rgba(180,80,40,0.25) 30%,
    rgba(60,20,20,0.6) 70%,
    transparent 100%);
}
.lore-event-figure-caption {
  font-family: var(--lore-mono, monospace);
  font-size: 10px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--tone-accent);
  opacity: 0.6;
  margin: 8px 0 0;
}
.lore-event-figure-text {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: clamp(16px, 1.6vw, 20px);
  line-height: 1.6;
  color: var(--lore-ink-2, #b8d4cc);
}
.lore-event-figure-text p { margin: 0 0 1em; }

/* data — HUD scan readout */
.lore-event-data {
  width: 100%;
  max-width: 820px;
  padding: clamp(20px, 3vw, 32px) clamp(8px, 2vw, 24px);
  position: relative;
  font-family: var(--lore-mono, monospace);
}

.lore-event-data-title {
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: 18px;
  letter-spacing: 0.2em;
  color: var(--tone-accent);
  text-transform: uppercase;
  margin: 0 0 18px;
}

.lore-event-data-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px 24px;
}
.lore-event-data-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.lore-event-data-label {
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  /* Brightened from --lore-ink-4 (#5a6e68) which read as illegible
     dark grey on the dark background. */
  color: var(--lore-ink-3, #b8d4cc);
}
.lore-event-data-value {
  /* Was Audiowide; that face lacks several scan glyphs (°, ², subscripts,
     +/<) which fell back to a different metric and gave the values a
     mismatched-first-letter look. Mono renders the full mixed string
     consistently. */
  font-family: var(--lore-mono, 'Share Tech Mono', ui-monospace, monospace);
  font-size: 18px;
  letter-spacing: 0.04em;
  font-weight: 600;
  color: var(--lore-ink, #e6edf3);
  font-variant-numeric: tabular-nums;
}
.lore-event-data-value.is-critical { color: var(--lore-amber-bright, #ffc858); text-shadow: 0 0 12px rgba(232,160,32,0.5); }

.lore-event-data-countdown-wrap { text-align: center; }
.lore-event-data-countdown {
  margin-top: 8px;
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: clamp(20px, 3vw, 28px);
  letter-spacing: 0.14em;
  color: var(--lore-amber-bright, #ffc858);
  text-shadow: 0 0 18px rgba(232,160,32,0.5);
  font-variant-numeric: tabular-nums;
  transition: color 0.4s, text-shadow 0.4s;
}
.lore-event-data-countdown.is-critical {
  color: #ff6a5a;
  text-shadow: 0 0 24px rgba(255,90,80,0.7);
  animation: lore-pulse-critical 1.2s ease-in-out infinite;
}
.lore-event-data-countdown.is-fired {
  color: var(--lore-teal-bright, #51e9b6);
  text-shadow: 0 0 26px rgba(81,233,182,0.7);
  letter-spacing: 0.24em;
}
@keyframes lore-pulse-critical {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}
.lore-event-data-action-wrap { text-align: center; }

.lore-event-data-redacted {
  margin-top: 18px;
  display: grid;
  gap: 10px;
}
.lore-event-data-redacted-row {
  display: flex;
  gap: 18px;
  font-size: 12px;
  letter-spacing: 0.12em;
  align-items: baseline;
}
/* Use direct-child selectors so the label/value styling does NOT
   bleed into the per-character spans that decryptReveal creates inside
   data-redacted-text. Before this change, the first cell of the
   revealed string inherited the label's grey/uppercase styling. */
.lore-event-data-redacted-row > span:first-child {
  flex: 0 0 auto;
  color: var(--lore-ink-4, #5a6e68);
  text-transform: uppercase;
  font-size: 10px;
  letter-spacing: 0.3em;
  min-width: 140px;
}
.lore-event-data-redacted-row > span:last-child,
.lore-event-data-redacted-row > span:last-child > span {
  color: var(--tone-accent);
  font-family: var(--lore-mono, monospace);
}
.lore-event-data-redacted-row.is-revealed > span:last-child,
.lore-event-data-redacted-row.is-revealed > span:last-child > span {
  letter-spacing: 0.12em;
  font-weight: 600;
}

.lore-event-data-action {
  margin-top: 24px;
  appearance: none;
  background: transparent;
  border: 1px solid var(--tone-accent);
  color: var(--tone-accent);
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: 12px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  padding: 12px 22px;
  cursor: pointer;
  transition: background 0.25s, transform 0.25s, box-shadow 0.25s;
}
.lore-event-data-action:hover,
.lore-event-data-action:focus-visible {
  outline: none;
  background: rgba(0,200,180,0.08);
}

/* cards — flexible card grid (ark cards, revelations, contenders) */
.lore-event-cards {
  width: 100%;
  display: grid;
  gap: clamp(14px, 2.4vw, 28px);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  max-width: 1000px;
}
/* When the cards kind opens an artefact detail it sets the grid
   [hidden]; without this, the display:grid above overrides the
   attribute's display:none, so the collapsed grid still reserves space
   and pushes the detail down (the empty gap under the title). */
.lore-event-cards[hidden] { display: none; }
/* The manifest intro (e.g. "Click any sigil to read the public
   fragment.") is a selection-page instruction; hide it once an artefact
   detail is open so it doesn't linger on the sub-page. */
.lore-event-cards-wrap[data-view="detail"] .lore-event-cards-intro { display: none; }
.lore-event-card {
  /* Seamless: no card-shaped background, no soft-glow halo. The card
     sits on the chapter background directly, separated from its
     neighbours by a single thin divider only on hover/focus so the
     grid still reads as discrete clickable units. */
  position: relative;
  appearance: none;
  background: transparent;
  padding: 14px 14px 16px;
  text-align: center;
  color: var(--lore-ink-2, #b8d4cc);
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: 13px;
  letter-spacing: 0.02em;
  cursor: pointer;
  border: 0;
  transition: transform 0.25s, color 0.25s;
  --card-accent: var(--tone-accent);
}
.lore-event-card:hover {
  /* No drop-shadow glow. Just a tiny lift + accent-coloured headings. */
  transform: translateY(-2px);
  filter: none;
}
.lore-event-card:focus-visible {
  outline: 1px solid var(--card-accent);
  outline-offset: 6px;
  filter: none;
}
.lore-event-card-num {
  /* Cleaner type: monospace, modest weight, no glow text-shadow.
     The accent colour alone signals which ark/contender it is. */
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--card-accent);
  text-shadow: none;
  margin: 0 0 10px;
  line-height: 1;
  opacity: 0.95;
}
.lore-event-card-img {
  /* Retained for non-ark uses (contender/revelation may set image). */
  width: 64px; height: 36px;
  margin: 0 auto 12px;
  object-fit: contain;
  display: block;
  filter: none;
}
/* <img> sigil: width follows the sigil's own aspect (no letterboxing), and JS
   sets an equal-AREA height per sigil (see loadSigilImg → normalizeArea) so they
   all read the same visual size. max-height caps the tallest (narrow) ones. */
/* Fixed-height slot so every contender sigil reserves the SAME vertical space,
   regardless of its own (equal-AREA, hence per-sigil-variable) height. Without
   it a taller sigil pushed its hint text lower than its neighbours' — the
   "sigil labels not aligned" variance. The image is centred in the slot; JS
   still sets each sigil's equal-area height (capped to the slot height). */
.lore-event-card-sigil-box {
  height: 128px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 14px;
  position: relative;   /* anchors the glitch ghost layers + overlays */
}
.lore-event-card-sigil {
  display: block;
  margin: 0 auto;
  max-height: 128px;
  max-width: 100%;
  width: auto; height: auto;
  object-fit: contain;
  filter: drop-shadow(0 0 18px var(--tone-soft));
}
/* ── Sigil IV idle reveal (easter egg), logo-focused glitch ──────────────
   After 20 idle seconds on the contender manifest (only action clicks
   reset), JS adds .is-glitching to the sigil and .is-glitch-stage to its
   box; ~5s of corruption later a burst (~0.64s) decodes the art into
   <sigil>_real.png (src swap at the 50% flash frame), then ALL glitch
   layers drop and the real sigil holds clean for ~20s before a second
   burst takes it back and the cycle restarts.
   Every layer is shaped by the logo itself — no box-wide rectangles:
   base <img> (tear slices + palette flash) · two JS-built ghost copies
   tinted amber / teal (site palette, .lore-sigil-ghost--amber/--teal) ·
   a scanline div MASKED by the sigil silhouette (.lore-sigil-scanmask).
   steps(1, end) everywhere: hold, then snap — choppy, digital. */
.lore-event-card-sigil.is-glitching {
  animation: lore-sigil-glitch-ambient 2.7s steps(1, end) infinite;
}
.lore-event-card-sigil.is-glitch-burst {
  animation: lore-sigil-glitch-burst 0.64s steps(1, end) both;
}
/* Chromatic ghost layers: same art, palette-tinted via sepia→hue-rotate,
   flashed in as offset slices. Hidden (opacity 0) outside their keyframes. */
.lore-sigil-ghost {
  position: absolute;
  inset: 0;
  margin: auto;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  opacity: 0;
  pointer-events: none;
  mix-blend-mode: screen;
}
.lore-sigil-ghost--amber { filter: brightness(1.2) sepia(1) saturate(9) hue-rotate(-12deg); }  /* lore amber #e8a020 */
.lore-sigil-ghost--teal  { filter: brightness(1.15) sepia(1) saturate(9) hue-rotate(125deg); } /* lore teal #00c8b4 */
.is-glitch-stage .lore-sigil-ghost--amber { animation: lore-sigil-ghost-amber 2.7s steps(1, end) infinite; }
.is-glitch-stage .lore-sigil-ghost--teal  { animation: lore-sigil-ghost-teal 2.7s steps(1, end) infinite; }
.is-glitch-stage.is-glitch-burst .lore-sigil-ghost--amber { animation: lore-sigil-ghost-amber-burst 0.64s steps(1, end) both; }
.is-glitch-stage.is-glitch-burst .lore-sigil-ghost--teal  { animation: lore-sigil-ghost-teal-burst 0.64s steps(1, end) both; }
/* Scanline layer clipped to the logo: JS sizes it to the rendered sigil and
   masks it with the sigil art itself (mask-image set inline), so the holo
   flicker lives strictly inside the silhouette. */
.lore-sigil-scanmask {
  position: absolute;
  inset: 0;
  margin: auto;
  opacity: 0;
  pointer-events: none;
  mix-blend-mode: screen;
  background: repeating-linear-gradient(to bottom,
    var(--tone-glow, rgba(0, 200, 180, 0.5)) 0 1px, transparent 1px 3px);
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}
.is-glitch-stage .lore-sigil-scanmask {
  animation: lore-sigil-scanmask 2.7s steps(1, end) infinite;
}
.is-glitch-stage.is-glitch-burst .lore-sigil-scanmask {
  animation: lore-sigil-scanmask-burst 0.64s steps(1, end) both;
}
@keyframes lore-sigil-glitch-ambient {
  0%, 100% { transform: none; opacity: 1; clip-path: none; }
  12% { transform: translateX(2px); opacity: 0.85; }
  13% { transform: none; opacity: 1; }
  41% { clip-path: inset(54% 0 16% 0); transform: translateX(-3px); }
  44% { clip-path: none; transform: none; }
  58% { opacity: 0.6; transform: translateX(1px) scaleY(0.96); }
  59% { opacity: 1; transform: none; }
  72% { clip-path: inset(28% 0 46% 0); transform: translateX(4px) skewX(3deg); }
  74% { clip-path: none; transform: none; }
}
@keyframes lore-sigil-glitch-burst {
  0%   { clip-path: none; transform: none; opacity: 1; }
  10%  { clip-path: inset(8% -4% 70% -4%); transform: translate(-7px, 2px); }
  22%  { clip-path: inset(48% -4% 20% -4%); transform: translate(6px, -2px) skewX(-8deg); }
  34%  { clip-path: inset(20% -4% 48% -4%); transform: translate(-5px, 0) scaleX(1.3); opacity: 0.8; }
  44%  { clip-path: none; transform: scaleX(1.6) scaleY(0.7); opacity: 0.55;
         filter: brightness(1.7) sepia(1) saturate(7) hue-rotate(125deg); }   /* teal decode flash */
  /* ← the src swap lands at 50%, under the flash */
  56%  { clip-path: none; transform: scaleX(1.25) scaleY(0.85); opacity: 0.75;
         filter: brightness(1.6) sepia(1) saturate(7) hue-rotate(-12deg); }   /* amber settle flash */
  64%  { clip-path: inset(38% -4% 30% -4%); transform: translate(7px, 1px); opacity: 1; }
  78%  { clip-path: inset(8% -4% 74% -4%); transform: translate(-5px, -1px) skewX(6deg); }
  90%  { clip-path: none; transform: translateX(2px); }
  100% { clip-path: none; transform: none; opacity: 1; }
}
@keyframes lore-sigil-ghost-amber {
  0%, 100% { opacity: 0; transform: none; clip-path: inset(0); }
  12% { opacity: 0.8; transform: translateX(-4px); clip-path: inset(12% 0 70% 0); }
  14% { opacity: 0; }
  41% { opacity: 0.65; transform: translateX(-3px) skewX(-4deg); clip-path: inset(56% 0 18% 0); }
  44% { opacity: 0; }
  72% { opacity: 0.75; transform: translateX(-5px); clip-path: inset(30% 0 44% 0); }
  74% { opacity: 0; }
}
@keyframes lore-sigil-ghost-teal {
  0%, 100% { opacity: 0; transform: none; clip-path: inset(0); }
  12% { opacity: 0.8; transform: translateX(4px); clip-path: inset(64% 0 14% 0); }
  14% { opacity: 0; }
  47% { opacity: 0.7; transform: translateX(3px) skewX(4deg); clip-path: inset(8% 0 76% 0); }
  50% { opacity: 0; }
  72% { opacity: 0.75; transform: translateX(5px); clip-path: inset(46% 0 28% 0); }
  74% { opacity: 0; }
}
@keyframes lore-sigil-ghost-amber-burst {
  0%   { opacity: 0.9; transform: translateX(-3px); clip-path: inset(0); }
  18%  { opacity: 0.9; transform: translate(-10px, 2px); clip-path: inset(34% 0 38% 0); }
  36%  { opacity: 0.8; transform: translate(-7px, -2px) skewX(-6deg); clip-path: inset(6% 0 72% 0); }
  50%  { opacity: 0.3; transform: translateX(-14px); clip-path: inset(0); }
  68%  { opacity: 0.85; transform: translate(-6px, 1px); clip-path: inset(58% 0 12% 0); }
  86%  { opacity: 0.5; transform: translateX(-2px); clip-path: inset(20% 0 52% 0); }
  100% { opacity: 0; transform: none; clip-path: inset(0); }
}
@keyframes lore-sigil-ghost-teal-burst {
  0%   { opacity: 0.9; transform: translateX(3px); clip-path: inset(0); }
  18%  { opacity: 0.9; transform: translate(10px, -2px); clip-path: inset(40% 0 32% 0); }
  36%  { opacity: 0.8; transform: translate(7px, 2px) skewX(6deg); clip-path: inset(68% 0 8% 0); }
  50%  { opacity: 0.3; transform: translateX(14px); clip-path: inset(0); }
  68%  { opacity: 0.85; transform: translate(6px, -1px); clip-path: inset(10% 0 60% 0); }
  86%  { opacity: 0.5; transform: translateX(2px); clip-path: inset(48% 0 24% 0); }
  100% { opacity: 0; transform: none; clip-path: inset(0); }
}
@keyframes lore-sigil-scanmask {
  0%, 100% { opacity: 0.16; background-position: 0 0; }
  12% { opacity: 0.5; }
  14% { opacity: 0.2; }
  41% { opacity: 0.55; background-position: 0 2px; }
  44% { opacity: 0.18; background-position: 0 0; }
  58% { opacity: 0.05; }
  60% { opacity: 0.2; }
  72% { opacity: 0.5; background-position: 0 -2px; }
  74% { opacity: 0.18; background-position: 0 0; }
}
@keyframes lore-sigil-scanmask-burst {
  0%   { opacity: 0.7; background-position: 0 0; }
  30%  { opacity: 0.85; background-position: 0 2px; }
  50%  { opacity: 0.95; background-position: 0 -2px; }
  75%  { opacity: 0.6; background-position: 0 1px; }
  100% { opacity: 0.2; background-position: 0 0; }
}
@media (prefers-reduced-motion: reduce) {
  .lore-event-card-sigil.is-glitching,
  .lore-event-card-sigil.is-glitch-burst,
  .is-glitch-stage .lore-sigil-ghost--amber,
  .is-glitch-stage .lore-sigil-ghost--teal,
  .is-glitch-stage .lore-sigil-scanmask { animation: none; }
}
/* Big sigil shown in the contender DETAIL view (left column), aspect-preserved. */
.lore-ark-detail-sigil {
  display: block;
  margin: 0 auto;
  max-width: 100%;
  max-height: 300px;
  width: auto; height: auto;
  object-fit: contain;
  filter: drop-shadow(0 0 26px var(--tone-soft));
}
.lore-event-card-glyph {
  width: 144px; height: 144px;
  margin: 0 auto 14px;
  color: var(--card-accent);
}
.lore-event-card-glyph svg,
.lore-event-card-glyph img { width: 100%; height: 100%; object-fit: contain; }
.lore-event-card-name {
  /* Heading is the visual anchor. Larger serif (matches body prose
     family) instead of the noisy Audiowide display font; subtle
     weight and minimal letter-spacing. No glow / shadow. */
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: clamp(18px, 3.2vw, 22px);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--lore-ink, #e6edf3);
  margin: 0 0 10px;
  text-transform: none;
  line-height: 1.15;
}
.lore-event-card-hint {
  /* The italic flavour line stays italic but quieter so the name
     dominates the card. */
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: 13px;
  color: var(--lore-ink-3, #8b9d96);
  margin: 0;
  letter-spacing: normal;
  text-transform: none;
  line-height: 1.5;
  max-width: 26ch;
  margin-left: auto;
  margin-right: auto;
}
.lore-event-card-cargo {
  /* Cargo line: small monospace caption, no uppercase shouting. */
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid color-mix(in srgb, var(--card-accent) 18%, transparent);
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--lore-ink-4, #5a6e68);
  text-transform: none;
}
.lore-event-card[data-variant="wait"] { --card-accent: var(--lore-amber, #e8a020); }

/* ─── Card details overlay (ark / contender / revelation click) ─── */
.lore-card-details {
  position: fixed;
  inset: 0;
  z-index: 55;
  display: grid;
  place-items: center;
  background: rgba(2,8,11,0.86);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s;
  padding: 16px;
}
.lore-card-details[data-open="true"] { opacity: 1; pointer-events: auto; }
.lore-card-details-card {
  width: min(95vw, 720px);
  max-height: 88vh;
  overflow-y: auto;
  padding: clamp(28px, 4vw, 48px);
  position: relative;
  background:
    radial-gradient(ellipse 80% 60% at 50% 0%, var(--card-accent, var(--tone-accent)), transparent 70%),
    radial-gradient(ellipse 100% 80% at 50% 100%, rgba(0,0,0,0.6), transparent 65%);
  --card-accent-soft: color-mix(in srgb, var(--card-accent, var(--tone-accent)) 18%, transparent);
  text-align: left;
}
.lore-card-details-close {
  position: absolute;
  top: 8px; right: 12px;
  background: transparent;
  border: 0;
  color: var(--lore-ink-3, #8b9d96);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  padding: 8px;
}
.lore-card-details-close:hover { color: var(--card-accent, var(--tone-accent)); }
.lore-card-details-eyebrow {
  font-family: var(--lore-mono, monospace);
  font-size: 11px;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--card-accent, var(--tone-accent));
  margin: 0 0 8px;
}
.lore-card-details-name {
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: clamp(28px, 4vw, 40px);
  letter-spacing: 0.06em;
  color: var(--lore-ink, #e6edf3);
  margin: 0 0 4px;
  text-shadow: 0 0 22px var(--card-accent-soft);
}
.lore-card-details-cargo {
  font-family: var(--lore-mono, monospace);
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--lore-ink-3, #8b9d96);
  margin: 0 0 24px;
}
.lore-card-details-hint {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: 16px;
  color: var(--lore-ink-2, #b8d4cc);
  margin: 0 0 24px;
}
.lore-card-details-section {
  margin: 18px 0 0;
}
.lore-card-details-section h4 {
  font-family: var(--lore-mono, monospace);
  font-size: 10px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--card-accent, var(--tone-accent));
  margin: 0 0 6px;
  font-weight: 400;
}
.lore-card-details-section p,
.lore-card-details-section li {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: 15px;
  color: var(--lore-ink-2, #b8d4cc);
  line-height: 1.55;
  margin: 0 0 4px;
}
.lore-card-details-section em { color: var(--lore-ink, #e6edf3); }
.lore-card-details-section ul { list-style: none; padding: 0; margin: 0; }
.lore-card-details-section ul li::before {
  content: "· ";
  color: var(--card-accent, var(--tone-accent));
  font-weight: bold;
}
.lore-card-details-drift {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--card-accent-soft);
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: 14px;
  color: var(--lore-ink-3, #8b9d96);
}

/* Tech sheet spec grid (ark / planet details). */
.lore-card-details-specs dl {
  display: grid;
  grid-template-columns: 1fr;
  gap: 6px 0;
  margin: 4px 0 0;
}
@media (min-width: 520px) {
  .lore-card-details-specs dl { grid-template-columns: 1fr 1fr; gap: 6px 18px; }
}
.lore-card-details-spec-row {
  display: grid;
  grid-template-columns: minmax(120px, 0.9fr) 1.2fr;
  gap: 12px;
  align-items: baseline;
  padding: 4px 0;
  border-bottom: 1px dashed var(--card-accent-soft);
}
.lore-card-details-spec-row:last-child { border-bottom: 0; }
.lore-card-details-spec-row dt {
  font-family: var(--lore-mono, monospace);
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--lore-ink-4, #5a6e68);
  margin: 0;
}
.lore-card-details-spec-row dd {
  font-family: var(--lore-mono, monospace);
  font-size: 13px;
  letter-spacing: 0.02em;
  color: var(--lore-ink, #e6edf3);
  margin: 0;
  font-variant-numeric: tabular-nums;
}

.lore-event-cards-intro {
  width: 100%;
  text-align: center;
  font-family: var(--lore-mono, monospace);
  font-size: 12px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--lore-ink-3, #8b9d96);
  margin: 0 0 22px;
}

/* ─── Inline ark detail (replaces the popup overlay) ──────── */
.lore-event-cards-wrap { width: 100%; max-width: 1100px; position: relative; }
.lore-event-cards-detail-host { width: 100%; }

.lore-ark-detail {
  position: relative;
  width: 100%;
  padding-top: 56px;
}
/* Top-of-detail back button. The renderer's footer Back is also
   wired to closeDetail (via setSubView), so the user has both:
     - a visible back chip at the top of the long detail page
     - the familiar Back slot at the bottom (relabelled to "Back to
       manifest")
   Either click returns to the manifest. */
.lore-ark-detail-top-back {
  position: absolute;
  top: 0;
  left: 0;
  appearance: none;
  background:
    radial-gradient(ellipse 110% 80% at 50% 50%, color-mix(in srgb, var(--card-accent) 18%, transparent), transparent 70%),
    rgba(2, 8, 11, 0.55);
  border: 1px solid color-mix(in srgb, var(--card-accent) 50%, transparent);
  color: var(--card-accent, var(--tone-accent));
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: 12px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  padding: 10px 18px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  transition: background 0.2s, transform 0.2s, box-shadow 0.2s;
  z-index: 2;
}
.lore-ark-detail-top-back:hover,
.lore-ark-detail-top-back:focus-visible {
  outline: none;
  background:
    radial-gradient(ellipse 110% 80% at 50% 50%, color-mix(in srgb, var(--card-accent) 30%, transparent), transparent 70%),
    rgba(2, 8, 11, 0.7);
  box-shadow: 0 0 22px color-mix(in srgb, var(--card-accent) 35%, transparent);
  transform: translateY(-1px);
}
.lore-ark-detail-top-back > span { font-size: 16px; line-height: 1; }

.lore-ark-detail-grid {
  /* Single-column stack: blueprint on top (full-width so the dense
     technical drawing is actually readable), text below. The old
     side-by-side layout shrank a 1536×1024 blueprint into a ~450 px
     column where all the spec-panel text became illegible. */
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(20px, 3vw, 36px);
}

/* Blueprint image area */
.lore-ark-detail-image {
  position: relative;
  border: 0;
  background: transparent;
  padding: 0;
  cursor: zoom-in;
}
.lore-ark-detail-blueprint {
  display: block;
  width: 100%;
  max-width: 1100px;
  height: auto;
  margin: 0 auto;
  border-radius: 4px;
  /* No drop-shadow halo — the blueprint art has its own dark
     background and any outer glow muddies the technical drawing. */
  filter: none;
}
.lore-ark-detail-blueprint.is-fallback {
  filter: drop-shadow(0 0 18px color-mix(in srgb, var(--card-accent) 28%, transparent));
}

/* ─── Blueprint lightbox (click-to-enlarge) ───────────────────────
   Mounted at body level by kinds/cards.js. Opens via dialog.showModal
   so it inherits proper focus trap + escape-to-close. */
.lore-blueprint-lightbox {
  border: 0;
  padding: 0;
  background: transparent;
  width: 96vw;
  max-width: 96vw;
  height: 96vh;
  max-height: 96vh;
  color: var(--lore-ink, #e6edf3);
}
.lore-blueprint-lightbox::backdrop {
  background: rgba(2, 8, 11, 0.92);
  backdrop-filter: blur(6px);
}
.lore-blueprint-lightbox[open] {
  display: grid;
  place-items: center;
}
.lore-blueprint-lightbox-scroll {
  width: 100%;
  height: 100%;
  overflow: auto;
  display: grid;
  place-items: center;
  -webkit-overflow-scrolling: touch;
}
.lore-blueprint-lightbox-img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  /* `image-rendering: pixelated` keeps the blueprint's thin lines +
     small text crisp on hi-DPI / Chromium upscales; without it the
     1536-wide PNG looks blurry when fit to a 1600+ viewport. */
  image-rendering: -webkit-optimize-contrast;
}
.lore-blueprint-lightbox-close {
  position: fixed;
  top: 16px;
  right: 20px;
  z-index: 2;
  width: 40px;
  height: 40px;
  background: rgba(2, 8, 11, 0.85);
  color: var(--lore-ink, #ffffff);
  border: 1px solid color-mix(in srgb, var(--tone-accent) 50%, transparent);
  border-radius: 4px;
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.lore-blueprint-lightbox-close:hover {
  border-color: var(--tone-accent);
  color: var(--tone-accent);
}
.lore-ark-detail-image-caption {
  margin-top: 14px;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.lore-ark-detail-image-caption > span {
  font-family: var(--lore-mono, monospace);
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--card-accent, var(--tone-accent));
}
.lore-ark-detail-image-caption > strong {
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: clamp(24px, 3.4vw, 32px);
  letter-spacing: 0.06em;
  color: var(--lore-ink, #ffffff);
  font-weight: 400;
  text-shadow: 0 0 22px color-mix(in srgb, var(--card-accent) 40%, transparent);
}
.lore-ark-detail-image-caption > em {
  font-family: var(--lore-mono, monospace);
  font-style: normal;
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--lore-ink-3, #b8d4cc);
}

/* Text area */
.lore-ark-detail-text {
  display: flex;
  flex-direction: column;
  gap: 22px;
}
.lore-ark-detail-hint {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: clamp(17px, 2vw, 20px);
  line-height: 1.5;
  color: var(--lore-ink, #e6edf3);
  margin: 0;
}
.lore-ark-detail-cargo {
  font-family: var(--lore-mono, monospace);
  font-size: 12px;
  letter-spacing: 0.18em;
  color: var(--card-accent, var(--tone-accent));
  margin: -10px 0 0;
  text-transform: uppercase;
}
.lore-ark-detail-section { margin: 0; }
.lore-ark-detail-section-title {
  font-family: var(--lore-mono, monospace);
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--card-accent, var(--tone-accent));
  margin: 0 0 8px;
  padding-bottom: 6px;
  border-bottom: 1px dashed color-mix(in srgb, var(--card-accent) 26%, transparent);
  font-weight: 400;
}
.lore-ark-detail-specs {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  margin: 0;
}
@media (min-width: 520px) {
  .lore-ark-detail-specs { grid-template-columns: 1fr 1fr; gap: 0 24px; }
}
.lore-ark-detail-spec {
  display: grid;
  grid-template-columns: minmax(130px, 0.85fr) 1.2fr;
  gap: 12px;
  padding: 6px 0;
  border-bottom: 1px dashed color-mix(in srgb, var(--card-accent) 14%, transparent);
}
.lore-ark-detail-spec dt {
  font-family: var(--lore-mono, monospace);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  /* Brighter than the previous --lore-ink-4 so labels are actually readable. */
  color: var(--lore-ink-3, #b8d4cc);
  margin: 0;
  line-height: 1.5;
}
.lore-ark-detail-spec dd {
  font-family: var(--lore-mono, monospace);
  font-size: 13px;
  color: var(--lore-ink, #ffffff);
  margin: 0;
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
}
.lore-ark-detail-faction {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: 15px;
  line-height: 1.55;
  color: var(--lore-ink, #e6edf3);
  margin: 0;
}
.lore-ark-detail-faction em {
  color: var(--card-accent, var(--tone-accent));
  font-style: italic;
}

/* Simple detail (contenders, revelations) */
.lore-ark-detail--simple { max-width: 720px; margin: 0 auto; }
.lore-ark-detail-simple-body { display: flex; flex-direction: column; gap: 16px; text-align: center; }
/* Sigil contenders: logo LEFT, text RIGHT — uses the lateral space on desktop
   instead of a narrow centred column. Stacks on narrow screens. */
.lore-ark-detail-simple-body.has-sigil {
  display: grid;
  grid-template-columns: minmax(150px, 230px) 1fr;
  gap: clamp(24px, 4vw, 52px);
  align-items: center;
  text-align: left;
}
.lore-ark-detail-simple-text { display: flex; flex-direction: column; gap: 14px; }
/* The sigil's numeral becomes a big, CENTRED title on the text side (instead of
   a tiny eyebrow). */
.lore-ark-detail-simple-body.has-sigil .lore-ark-detail-eyebrow {
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: clamp(44px, 7vw, 76px);
  line-height: 1;
  letter-spacing: 0.06em;
  text-align: center;
  color: var(--card-accent, var(--tone-accent));
  text-shadow: 0 0 30px color-mix(in srgb, var(--card-accent) 45%, transparent);
  margin: 0 0 6px;
}
@media (max-width: 640px) {
  .lore-ark-detail-simple-body.has-sigil { grid-template-columns: 1fr; text-align: center; }
}
.lore-ark-detail-eyebrow {
  font-family: var(--lore-mono, monospace);
  font-size: 12px;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--card-accent, var(--tone-accent));
  margin: 0;
}
.lore-ark-detail-name {
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: clamp(30px, 4vw, 44px);
  letter-spacing: 0.06em;
  color: var(--lore-ink, #ffffff);
  margin: 0;
  font-weight: 400;
  text-shadow: 0 0 24px color-mix(in srgb, var(--card-accent) 40%, transparent);
}
.lore-ark-detail-line {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: 16px;
  line-height: 1.6;
  color: var(--lore-ink, #e6edf3);
  margin: 0;
}
.lore-ark-detail-list {
  list-style: none;
  padding: 0;
  margin: 0;
  font-family: var(--lore-mono, monospace);
  font-size: 12px;
  letter-spacing: 0.18em;
  color: var(--lore-ink-2, #b8d4cc);
}
.lore-ark-detail-list li { padding: 4px 0; }
.lore-ark-detail-list li::before { content: "· "; color: var(--card-accent, var(--tone-accent)); }
.lore-ark-detail-drift {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: 14px;
  color: var(--lore-ink-3, #b8d4cc);
  margin-top: 12px;
  padding-top: 16px;
  border-top: 1px dashed color-mix(in srgb, var(--card-accent) 20%, transparent);
}

/* Disable the old popup overlay (kept in CSS but never opened). */
.lore-card-details { display: none !important; }

/* scene - full-stage container. No frame, no overflow clipping
   (tooltips and labels positioned outside the canvas must remain
   visible). The scene module owns its visuals; we just give it room. */
.lore-event-scene {
  position: relative;
  width: 100%;
  max-width: 1280px;
  aspect-ratio: 16 / 9;
  background: transparent;
  overflow: visible;
}
/* Act V scene placeholder. The actual canvas is in a separate fixed
   layer (`.lore-galaxy-layer`) at #lore-app level, NOT inside this
   container - that's how we kill the rectangular "frame" the user
   was seeing. This element just holds layout space for the stage
   header / footer to position around. */
.lore-event-scene[data-scene="reach"] {
  position: relative;
  width: 100%;
  height: clamp(380px, 60vh, 640px);
  max-width: 1100px;
  margin: 0 auto;
  background: transparent;
  pointer-events: none;        /* clicks pass through to the galaxy layer */
}
.lore-reach-placeholder { width: 100%; height: 100%; }

/* The galaxy layer itself. Fixed full-viewport, behind the screen UI
   but above the page bg + stars canvas. Pointer-events default off so
   the stage header/footer/HUD above remain interactive; we re-enable
   pointer-events on the canvas and the floating chrome elements. */
.lore-galaxy-layer,
.lore-voyage-layer {
  position: fixed;
  /* Full viewport: the galaxy IS the page. The vertical mask gradient
     below dissolves the top/bottom under the navbar/footer rather than
     hard-cropping them, which the user described as a "cropped block".
     Numbers: nav is ~80px, footer is ~120px; we fade across a slightly
     wider band so the join reads as gradient haze rather than a line. */
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: visible;
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0,
    rgba(0,0,0,0.06) 24px,
    #000 96px,
    #000 calc(100% - 140px),
    rgba(0,0,0,0.06) calc(100% - 40px),
    transparent 100%
  );
          mask-image: linear-gradient(
    to bottom,
    transparent 0,
    rgba(0,0,0,0.06) 24px,
    #000 96px,
    #000 calc(100% - 140px),
    rgba(0,0,0,0.06) calc(100% - 40px),
    transparent 100%
  );
}
.lore-galaxy-layer[hidden], .lore-voyage-layer[hidden] { display: none; }

/* The galaxy canvas must CLAIM touch gestures (touch-action:none) so a finger
   drag orbits the system instead of the browser trying to pan/zoom the page —
   that default gesture fired pointercancel and aborted the orbit, which is why
   planets couldn't be moved on a phone (right-drag worked on desktop because it
   never triggers touch scrolling). pointer-events:auto re-enables the canvas
   over the pointer-events:none layer. */
.lore-reach-canvas { pointer-events: auto; touch-action: none; }

/* While the galaxy is the active event we lock the page to the
   viewport (no vertical scroll) and collapse the chapter body so the
   only visual is the WebGL bed. The placeholder div the kind handler
   inserts (`<div class="lore-reach-placeholder">`) takes zero layout
   space; the actual interactive surface is .lore-galaxy-layer above.
   overflow-x:hidden everywhere kills any rogue horizontal scrollbar
   from the fixed pills / blur containers extending fractionally past
   the viewport edge during sub-pixel tween states. */
html:has(#lore-app[data-galaxy-active="true"]),
body:has(#lore-app[data-galaxy-active="true"]) {
  overflow: hidden;
  height: 100dvh;   /* dvh tracks the mobile URL-bar show/hide so the scene doesn't "slide" when you swipe */
}
html, body { overflow-x: hidden; }
#lore-app[data-galaxy-active="true"] .lore-event-scene[data-scene="reach"],
#lore-app[data-galaxy-active="true"] .lore-event-scene[data-scene="voyage"] {
  height: 0;
  min-height: 0;
  padding: 0;
  margin: 0;
  pointer-events: none;
}
#lore-app[data-galaxy-active="true"] .lore-stage-body {
  min-height: 0;
  flex: 0 0 auto;
}
#lore-app[data-galaxy-active="true"] .lore-stage {
  min-height: 0;
  height: auto;
  padding-top: clamp(24px, 4vh, 56px);
  padding-bottom: 0;          /* nav is fixed-positioned now, doesn't need padding */
}

/* Continue / Back: anchored at 5% from the bottom of the viewport, on
   the RIGHT side so the bottom-LEFT caption pill has the left side to
   itself with no overlap risk on any resolution. */
#lore-app[data-galaxy-active="true"] .lore-stage-nav {
  position: fixed;
  bottom: 5vh;
  right: clamp(16px, 3vw, 32px);
  left: auto;
  transform: none;
  margin: 0;
  gap: 14px;
  z-index: 12;
  pointer-events: auto;
}
/* While inside a solar system, the chapter Back/Continue nav goes
   away and the in-canvas "Back to Reach" pill takes its slot — same
   bottom-right anchor, single source of "where you can leave from". */
#lore-app[data-reach-phase="system"] .lore-stage-nav,
#lore-app[data-reach-phase="transitioning"] .lore-stage-nav {
  display: none !important;
}
/* Hide the ENTIRE chapter stage (header eyebrow + act title + body +
   nav) when zoomed into a solar system — the user wants the screen
   to belong to the system preview, not the chapter chrome. The HUD
   (Index / Cover / Mute) at #lore-app level is a sibling of .lore-
   stage so it stays visible. */
#lore-app[data-reach-phase="system"] .lore-stage,
#lore-app[data-reach-phase="transitioning"] .lore-stage {
  display: none !important;
}
/* Back-to-Reach is re-parented to #lore-app by the v2 wrapper so it
   sits OUTSIDE .lore-galaxy-layer (whose mask-image gradient was
   fading the button down to nearly invisible). Selector targets it
   at the app level now. */
#lore-app[data-galaxy-active="true"] > .lore-reach-back,
#lore-app[data-galaxy-active="true"] .lore-reach-back {
  position: fixed !important;
  top: auto !important;
  left: auto !important;
  bottom: 5vh !important;
  right: clamp(16px, 3vw, 32px) !important;
  transform: none !important;
  z-index: 50 !important;
  pointer-events: auto !important;
  /* Flat, fully-opaque primary button. No box-shadow, no glow, no
     inset highlights. The mask issue was the real culprit — now
     that the button is outside the masked layer, the bright teal
     reads at full saturation. */
  background: #51e9b6 !important;
  border: 0 !important;
  color: #021414 !important;
  padding: 12px 26px !important;
  font-weight: 800 !important;
  font-size: 12px !important;
  letter-spacing: 0.32em !important;
  text-transform: uppercase !important;
  border-radius: 4px !important;
  box-shadow: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  opacity: 1 !important;
}
#lore-app[data-galaxy-active="true"] > .lore-reach-back:hover,
#lore-app[data-galaxy-active="true"] .lore-reach-back:hover {
  background: #6cf3c4 !important;
  box-shadow: none !important;
}

/* Orbits toggle — compact icon-only square pinned BELOW the chapter
   header block. CRITICAL: visibility is gated on data-reach-phase
   ="system" (set by scene-reach.js ONLY while the camera is inside a
   solar system), NOT on data-galaxy-active (which is true for the
   whole Reach chapter, galaxy view included). The previous selector
   used data-galaxy-active + `display: inline-flex !important`, and
   that !important display beat the element's `hidden` attribute, so
   the button leaked into the galaxy overview. Phase-scoping plus the
   explicit [hidden] guard below keeps it strictly system-only.
   Anchored at top:130px to clear HUD + stage padding + glow halo. */
#lore-app[data-reach-phase="system"] > .lore-reach-orbits-toggle,
#lore-app[data-reach-phase="system"] .lore-reach-orbits-toggle {
  position: fixed !important;
  bottom: auto !important;
  right: auto !important;
  top: 54px !important;                          /* just under the top HUD row (was 130 when the 64px nav still pushed everything down) */
  left: clamp(14px, 3vw, 28px) !important;
  transform: none !important;
  z-index: 50 !important;
  pointer-events: auto !important;
  background: rgba(2, 20, 20, 0.55) !important;
  border: 1px solid rgba(81, 233, 182, 0.55) !important;
  color: #51e9b6 !important;
  padding: 0 !important;
  width: 32px !important;
  height: 32px !important;
  font-size: 0 !important;        /* belt-and-suspenders: no text leaks */
  letter-spacing: 0 !important;
  text-transform: none !important;
  border-radius: 6px !important;
  cursor: pointer !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 0 !important;
  backdrop-filter: blur(4px) !important;
  transition: background 0.15s, color 0.15s, border-color 0.15s !important;
}
/* Hard guard: whenever the button carries the `hidden` attribute
   (galaxy view, transitions, pre-mount) it stays gone regardless of
   the phase rule's !important display. Highest-specificity + source
   order after the show rule so it always wins when hidden is set. */
#lore-app .lore-reach-orbits-toggle[hidden],
#lore-app[data-reach-phase="galaxy"] .lore-reach-orbits-toggle,
#lore-app[data-reach-phase="transitioning"] .lore-reach-orbits-toggle {
  display: none !important;
}
#lore-app[data-reach-phase="system"] .lore-reach-orbits-toggle:hover {
  background: rgba(81, 233, 182, 0.15) !important;
  border-color: #6cf3c4 !important;
  color: #6cf3c4 !important;
}
#lore-app[data-reach-phase="system"] .lore-reach-orbits-toggle[data-active="true"] {
  background: #51e9b6 !important;
  color: #021414 !important;
  border-color: #51e9b6 !important;
}
/* Icon: planet-on-orbit glyph. Outer circle is the orbit, a single
   small dot sits on its right edge to read clearly as "a body
   tracing an orbital path" rather than a generic target reticle. */
#lore-app[data-reach-phase="system"] .lore-reach-orbits-toggle .lore-reach-orbits-icon {
  display: inline-block;
  width: 18px;
  height: 18px;
  border: 1.4px solid currentColor;
  border-radius: 50%;
  position: relative;
}
#lore-app[data-reach-phase="system"] .lore-reach-orbits-toggle .lore-reach-orbits-icon::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -2.5px;
  width: 5px; height: 5px;
  background: currentColor;
  border-radius: 50%;
  transform: translateY(-50%);
  box-shadow: 0 0 4px currentColor;
}

/* Zoom slider — vertical, sits directly under the orbits toggle, only
   while a system is open. Drag up = closer to the sun, down = pulled
   back (keeps the 3/4 top-down angle, just scales the distance). */
#lore-app .lore-reach-zoom { display: none; }
#lore-app[data-reach-phase="system"] > .lore-reach-zoom,
#lore-app[data-reach-phase="system"] .lore-reach-zoom {
  display: flex !important;
  position: fixed !important;
  top: 98px !important;                        /* below the 32px orbits toggle at 54 */
  left: clamp(14px, 3vw, 28px) !important;
  z-index: 50 !important;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 32px;
  padding: 8px 0;
  background: rgba(2, 20, 20, 0.55);
  border: 1px solid rgba(81, 233, 182, 0.4);
  border-radius: 6px;
  backdrop-filter: blur(4px);
  pointer-events: auto !important;
}
#lore-app[data-reach-phase="system"] .lore-reach-zoom-icon {
  color: #51e9b6;
  font-size: 13px;
  line-height: 1;
  font-weight: 700;
  user-select: none;
}
/* Vertical slider via a rotated horizontal range input — renders the
   same in every browser (vertical <input type=range> support is a
   moving target). The track box reserves the upright footprint; the
   input is laid out horizontally then rotated -90deg so its MAX end
   points up (= closer, matching the "+" label above). */
#lore-app[data-reach-phase="system"] .lore-reach-zoom-track {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 104px;
}
#lore-app[data-reach-phase="system"] .lore-reach-zoom-slider {
  width: 104px;
  height: 18px;
  transform: rotate(-90deg);
  accent-color: #51e9b6;
  cursor: pointer;
  background: transparent;
}
.lore-galaxy-layer .lore-reach-canvas,
.lore-galaxy-layer .lore-ark-label,
.lore-galaxy-layer .lore-reach-back,
.lore-galaxy-layer .lore-reach-card,
.lore-galaxy-layer .lore-reach-card-close { pointer-events: auto; }
.lore-galaxy-layer .lore-reach-stage {
  position: relative;
  width: 100%;
  height: 100%;
}

/* On dark tone, the page bg is fully transparent so the galaxy
   blends into the star canvas + nebula behind it. The stage chrome
   stays visible (chapter header at top, Continue/Back at bottom) but
   pointer events pass through the middle so the user interacts
   directly with the galaxy beneath. */
#lore-app[data-tone="dark"] .lore-bg { background: transparent; }
#lore-app[data-tone="dark"] .lore-stage {
  padding-top: 48px;
  padding-bottom: 48px;
  pointer-events: none;
}
#lore-app[data-tone="dark"] .lore-stage button,
#lore-app[data-tone="dark"] .lore-stage a {
  pointer-events: auto;
}
.lore-event-scene[data-scene="voyage"] {
  aspect-ratio: auto;
  height: clamp(460px, 80vh, 940px);   /* tall, near-fullscreen visual area */
}

/* Chapter I — dying paradise planet. Sized like a portrait/figure
   that reads alongside the chapter text rather than taking over the
   screen the way the Reach galaxy does. */
.lore-event-scene[data-scene="dying-planet"] {
  aspect-ratio: 16 / 9;
  max-width: 920px;
  margin: 0 auto;
  background:
    radial-gradient(ellipse 60% 50% at 50% 50%, rgba(0, 8, 14, 0.55), transparent 75%);
}
.lore-dying-planet-stage {
  position: absolute;
  inset: 0;
}
.lore-dying-planet-canvas {
  display: block;
  width: 100%;
  height: 100%;
  background: transparent;
}

/* Optional narrative paragraphs that sit BELOW the canvas — same
   typography as the narrative event kind, but tightened to read as
   caption-prose to the scene above. */
.lore-event-scene-body {
  max-width: 720px;
  margin: 10px auto 0 auto;
  text-align: center;
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: clamp(15px, 1.6vw, 18px);
  line-height: 1.55;
  color: var(--lore-ink-2, #b8d4cc);
}
.lore-event-scene-body p { margin: 0 0 10px 0; }
.lore-event-scene-body p:last-child { margin-bottom: 0; }

/* ─── Legacy scene overrides: strip the corner-bracket frames so the
   Three.js galaxy + system view blends seamlessly with the v2 background.
   Tooltips and floating cards are also unframed; visibility comes from
   soft glows, not hard outlines. ─── */
#lore-app .lore-reach-stage {
  background: transparent;
  border: 0;
  box-shadow: none;
  overflow: visible;
  max-width: 100%;
  aspect-ratio: auto;
  height: 100%;
}
#lore-app .lore-reach-stage::before,
#lore-app .lore-reach-stage::after { content: none; }

#lore-app .lore-reach-canvas {
  background: transparent;
}

/* Hide the REACH / GALAXY corner breadcrumb + zoom label (the labels and the
   solar-system back button still appear inside the canvas as needed). */
#lore-app .lore-reach-chrome { display: none; }

/* Make sure ARK labels actually receive clicks even with other stacking
   contexts in the lore-app tree above them. */
#lore-app .lore-ark-labels { z-index: 100; pointer-events: none; }
#lore-app .lore-ark-label  { z-index: 101; pointer-events: auto; cursor: pointer; }

/* Planet / sun / moon info card. Anchored to the RIGHT side of the
   viewport. The LEFT column now holds the orbits toggle AND the zoom
   slider, so the card must not live there. On the right the only
   chrome is the HUD action buttons (Index/Cover/Mute) which sit at
   the very top (~y 78-108); anchoring the card at top:130 clears
   them, and the bottom-right Back-to-Reach pill is cleared by the
   max-height. So the card overlaps nothing on either side. */
#lore-app .lore-reach-card {
  position: fixed;
  top: 130px;
  right: clamp(16px, 3vw, 36px);
  left: auto;                                       /* never anchor left */
  bottom: auto;
  /* Width reserves a 140px gutter so the card can NEVER reach the left
     control column (orbits toggle + zoom slider live there). */
  width: min(380px, calc(100vw - 140px));
  max-height: calc(100vh - 130px - 120px - 16px);  /* nav+HUD + footer + breath */
  margin: 0;
  overflow-y: auto;
  z-index: 60;                              /* above orbits toggle (z:50) + HUD (z:10) */
  background:
    radial-gradient(ellipse 100% 60% at 50% 0%, color-mix(in srgb, var(--lore-ark-c, var(--lore-teal)) 26%, transparent), transparent 70%),
    rgba(2, 8, 11, 0.92);
  border: 1px solid color-mix(in srgb, var(--lore-ark-c, var(--lore-teal)) 35%, transparent);
  border-radius: 6px;
  backdrop-filter: blur(12px) saturate(140%);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  box-shadow:
    0 18px 48px rgba(0, 0, 0, 0.55),
    0 0 36px color-mix(in srgb, var(--lore-ark-c, var(--lore-teal)) 30%, transparent);
}
@media (max-width: 700px), (orientation: landscape) and (max-height: 540px) {
  /* On phones — narrow portrait OR a short landscape phone (e.g. iPhone 12 Pro
     844x390, which the max-width test alone misses) — fall back to a bottom
     drawer, clear of the top chrome + hub. */
  #lore-app .lore-reach-card {
    top: auto;
    right: 10px; left: 10px;
    bottom: 12px;
    width: auto;
    max-height: 64vh;
  }
  /* The card lives inside the low-z .lore-galaxy-layer, while the Back / nav
     buttons are re-parented to #lore-app (a higher stacking context) and so
     render ON TOP of the card on phones. While a card is open, hide them —
     the card is a modal with its own close (×) button. */
  #lore-app:has(.lore-reach-card.is-open) .lore-reach-back,
  #lore-app:has(.lore-reach-card.is-open) .lore-hub,
  #lore-app:has(.lore-reach-card.is-open) .lore-stage-nav { display: none !important; }
}
#lore-app .lore-reach-card::before,
#lore-app .lore-reach-card::after { content: none; }
/* Hide the planet PNG glyph in the card - the texture is the
   equirectangular map (a wide, square pixel painting) which looks
   wrong in a tiny preview slot. Collapse the inner grid to a single
   column so the text gets the full card width. */
#lore-app .lore-reach-card-glyph { display: none; }
#lore-app .lore-reach-card-inner { grid-template-columns: 1fr; gap: 0; }

/* Target-world ark image inside the planet card (baked PNG of the ark). */
#lore-app .lore-reach-card-ark {
  margin: 12px 0 4px;
  padding: 10px 10px 8px;
  border: 1px solid color-mix(in srgb, var(--lore-ark-c, var(--lore-teal)) 26%, transparent);
  border-radius: 6px;
  background: color-mix(in srgb, var(--lore-ark-c, var(--lore-teal)) 7%, transparent);
  text-align: center;
}
#lore-app .lore-reach-card-ark[hidden] { display: none; }
#lore-app .lore-reach-card-ark-img {
  display: block;
  width: 100%;
  max-height: 160px;
  object-fit: contain;
  margin: 0 auto 6px;
}
#lore-app .lore-reach-card-ark figcaption {
  font-family: var(--lore-mono);
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--lore-ark-c, var(--lore-teal));
  opacity: 0.85;
}

#lore-app .lore-reach-card-specs {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  margin: 10px 0 12px;
  padding: 0;
}
@media (min-width: 520px) {
  #lore-app .lore-reach-card-specs { grid-template-columns: 1fr 1fr; gap: 0 18px; }
}
#lore-app .lore-reach-card-spec {
  display: grid;
  grid-template-columns: minmax(90px, 0.9fr) 1.3fr;
  gap: 10px;
  padding: 3px 0;
  border-bottom: 1px dashed color-mix(in srgb, var(--lore-ark-c, var(--lore-teal)) 22%, transparent);
}
#lore-app .lore-reach-card-spec:last-child,
#lore-app .lore-reach-card-spec:nth-last-child(2) { border-bottom: 0; }
#lore-app .lore-reach-card-spec dt {
  font-family: var(--lore-mono);
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--lore-ink-4, #5a6e68);
  margin: 0;
  line-height: 1.6;
}
#lore-app .lore-reach-card-spec dd {
  font-family: var(--lore-mono);
  font-size: 11px;
  color: var(--lore-ink-2, #b8d4cc);
  margin: 0;
  font-variant-numeric: tabular-nums;
  line-height: 1.6;
}
/* Long / "·"-listed values (composition, atmosphere, …): span the FULL card
   width and stack the value UNDER the label, instead of cramming it into the
   narrow value column (which caused multi-line wrap + a too-tall card).
   Short values keep the inline label | value layout above. */
#lore-app .lore-reach-card-spec.is-long {
  grid-column: 1 / -1;
  grid-template-columns: 1fr;
  gap: 1px;
}
#lore-app .lore-reach-card-spec.is-long dd { margin-top: 1px; }

#lore-app .lore-reach-tooltip {
  background:
    radial-gradient(ellipse 90% 70% at 50% 0%, rgba(0,200,180,0.18), transparent 70%),
    rgba(2, 10, 14, 0.82);
  border: 0;
  box-shadow: 0 8px 28px rgba(0,0,0,0.55), 0 0 22px rgba(0, 200, 180, 0.22);
  /* Clamp the tooltip inside the stage so it never overflows when a
     planet sits near a screen edge. The JS positions it via transform;
     we add a safety margin via max-width and pointer-events: none stays. */
  max-width: min(280px, calc(100vw - 32px));
}

/* Ark labels: lift contrast so they read clearly over any backdrop. */
#lore-app .lore-ark-label-card {
  background:
    radial-gradient(ellipse 100% 70% at 50% 0%, rgba(0,200,180,0.22), transparent 70%),
    rgba(2, 8, 11, 0.92);
  border: 1px solid color-mix(in srgb, var(--lore-teal) 70%, transparent);
  box-shadow:
    0 4px 18px rgba(0, 0, 0, 0.7),
    0 0 24px rgba(0, 200, 180, 0.45);
}
#lore-app .lore-ark-label-eyebrow { color: var(--lore-teal-bright, #51e9b6); }
#lore-app .lore-ark-label-name {
  color: #ffffff;
  text-shadow: 0 1px 4px rgba(0,0,0,0.8), 0 0 12px rgba(0,200,180,0.4);
}

/* Voyage scene: also drop legacy frames. */
#lore-app .lore-voyage-stage {
  background: transparent;
  border: 0;
  box-shadow: none;
  max-width: 100%;
}
#lore-app .lore-voyage-stage::before,
#lore-app .lore-voyage-stage::after { content: none; }
#lore-app .lore-voyage-viewport {
  background:
    radial-gradient(ellipse 90% 70% at 50% 50%, rgba(0,30,36,0.4), transparent 75%);
  border: 0;
}

/* Procedural ark sprite. Painted in inline SVG so it renders even
   when /images/lore/ark-ship.png is missing. The currentColor token
   is picked up from --lore-ark-c on the parent so each ark wears its
   own engine glow. */
#lore-app .lore-voyage-ark-img.is-placeholder,
#lore-app .lore-ark-icon-img.is-placeholder,
#lore-app .lore-event-card .lore-event-card-img.is-placeholder {
  background-color: transparent;
  border: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 36'%3E%3Cdefs%3E%3ClinearGradient id='h' x1='0' x2='1'%3E%3Cstop offset='0' stop-color='%23ffffff' stop-opacity='0.6'/%3E%3Cstop offset='0.4' stop-color='%23dde7ec' stop-opacity='0.95'/%3E%3Cstop offset='1' stop-color='%23ffffff' stop-opacity='0.85'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cg%3E%3Cpath d='M6 16 L60 14 L84 18 L60 22 L6 20 Z' fill='url(%23h)' stroke='rgba(255,255,255,0.85)' stroke-width='0.4'/%3E%3Cpath d='M60 14 L86 18 L60 22 Z' fill='%23ffffff' opacity='0.9'/%3E%3Cline x1='18' y1='14.5' x2='18' y2='21.5' stroke='rgba(0,0,0,0.35)' stroke-width='0.4'/%3E%3Cline x1='32' y1='14.4' x2='32' y2='21.6' stroke='rgba(0,0,0,0.35)' stroke-width='0.4'/%3E%3Cline x1='46' y1='14.3' x2='46' y2='21.7' stroke='rgba(0,0,0,0.35)' stroke-width='0.4'/%3E%3Cline x1='40' y1='14' x2='40' y2='8' stroke='rgba(255,255,255,0.7)' stroke-width='0.3'/%3E%3Ccircle cx='40' cy='8' r='0.9' fill='%23ffffff'/%3E%3Cellipse cx='5' cy='18' rx='5' ry='3' fill='%2351e9b6' opacity='0.9'/%3E%3Cellipse cx='3' cy='18' rx='8' ry='1.5' fill='%2351e9b6' opacity='0.5'/%3E%3C/g%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  width: 56px !important;
  height: 22px;
  filter: drop-shadow(0 0 6px var(--lore-ark-c, var(--lore-teal)));
}
/* (Per-ark placeholder PNG overrides removed — cards no longer render
   the side-on ark sprite, see kinds/cards.js. The dimension/filter
   rules above for .lore-event-card-img remain only for contender or
   future card kinds that might use the slot.) */

/* ═══ Chapter II — Covenants (structured tabbed blueprint) ═════════
   Tab strip across the top (Ark I / II / III), structured semantic
   content below — eyebrow, ark name, section label, overview, ship
   specs table, key-systems list with per-system spec rows, footer
   stats. Image slots are explicit labeled PLACEHOLDERS — the user
   will drop transparent element PNGs into them later. No stretched
   blueprint backgrounds, no decorative drop-shadow glow blobs.
   Uses the existing lore design tokens. */

.lore-covenants-wrap {
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
}
.lore-covenants-intro {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: 14px;
  font-style: italic;
  text-align: center;
  color: var(--lore-ink-3, #8b9d96);
  margin: 0 0 22px;
  letter-spacing: 0.02em;
}

/* ─── Tab strip ─────────────────────────────────────────────────── */
.lore-covenants-tabs {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 0;
  margin: 0 0 28px;
  border-bottom: 1px solid color-mix(in srgb, var(--lore-ink-2, #b8d4cc) 18%, transparent);
}
.lore-covenants-tab {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: 12px 16px 14px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  text-align: left;
  cursor: pointer;
  color: var(--lore-ink-3, #8b9d96);
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  transition: color 0.2s, border-color 0.2s;
  --panel-accent: var(--tone-accent);
}
.lore-covenants-tab-num {
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 10px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--panel-accent);
}
.lore-covenants-tab-name {
  font-size: 18px;
  font-weight: 500;
  color: inherit;
  letter-spacing: 0.02em;
}
.lore-covenants-tab-section {
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--lore-ink-4, #5a6e68);
}
.lore-covenants-tab:hover {
  color: var(--lore-ink-2, #b8d4cc);
}
.lore-covenants-tab[data-active="true"] {
  color: var(--lore-ink, #e6edf3);
  border-bottom-color: var(--panel-accent);
}
.lore-covenants-tab[data-active="true"] .lore-covenants-tab-section {
  color: var(--lore-ink-3, #b8d4cc);
}
.lore-covenants-tab:focus-visible {
  outline: none;
  background: color-mix(in srgb, var(--panel-accent) 8%, transparent);
}

/* ─── Body (active panel content) ───────────────────────────────── */
.lore-covenants-body {
  --panel-accent: var(--tone-accent);
}
.lore-covenant-panel {
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 3vh, 32px);
}

/* Header */
.lore-covenant-head {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-bottom: 18px;
  border-bottom: 1px solid color-mix(in srgb, var(--panel-accent) 22%, transparent);
}
.lore-covenant-eyebrow {
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--lore-ink-4, #5a6e68);
  margin: 0;
}
.lore-covenant-id {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
}
.lore-covenant-num {
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--panel-accent);
}
.lore-covenant-name {
  font-family: var(--lore-display, 'Audiowide', 'Exo 2', sans-serif);
  font-size: clamp(26px, 3.2vw, 36px);
  font-weight: 400;
  color: var(--lore-ink, #e6edf3);
  margin: 0;
  line-height: 1.05;
  letter-spacing: 0.04em;
}
.lore-covenant-section {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: 13px;
  color: var(--lore-ink-3, #8b9d96);
  margin: 0;
}

/* Overview (text + hero placeholder side by side) */
.lore-covenant-overview {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(16px, 2.4vw, 28px);
}
@media (min-width: 820px) {
  .lore-covenant-overview { grid-template-columns: 1fr 1fr; align-items: start; }
}
.lore-covenant-overview-text {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.lore-covenant-drift {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: 14px;
  line-height: 1.55;
  color: var(--lore-ink-2, #b8d4cc);
  margin: 0;
  padding-left: 14px;
  border-left: 2px solid var(--panel-accent);
}
.lore-covenant-drift em {
  font-style: normal;
  color: var(--panel-accent);
  font-weight: 500;
}
.lore-covenant-overview-body {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: 14px;
  line-height: 1.6;
  color: var(--lore-ink-2, #b8d4cc);
  margin: 0;
}

/* Section blocks (specs / systems / stats) */
.lore-covenant-block {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.lore-covenant-block-title {
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--panel-accent);
  margin: 0 0 4px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.lore-covenant-block-title::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--panel-accent) 35%, transparent), transparent);
}

/* Two-column spec list (used by specs + stats + system specs) */
.lore-covenant-specs,
.lore-covenant-system-specs {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  margin: 0;
  padding: 0;
}
@media (min-width: 520px) {
  .lore-covenant-specs { grid-template-columns: 1fr 1fr; column-gap: 28px; }
}
.lore-covenant-spec {
  display: grid;
  grid-template-columns: minmax(140px, 0.85fr) 1.15fr;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px dashed color-mix(in srgb, var(--lore-ink-2, #b8d4cc) 14%, transparent);
}
.lore-covenant-spec dt {
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--lore-ink-4, #5a6e68);
  margin: 0;
  line-height: 1.55;
}
.lore-covenant-spec dd {
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 12px;
  color: var(--lore-ink-2, #b8d4cc);
  margin: 0;
  line-height: 1.55;
  font-variant-numeric: tabular-nums;
}

/* Key-systems list — each system gets a small placeholder + spec rows */
.lore-covenant-systems {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
}
@media (min-width: 760px) {
  .lore-covenant-systems { grid-template-columns: 1fr 1fr; }
}
.lore-covenant-system {
  display: grid;
  grid-template-columns: 140px 1fr;
  /* figure | body on row 1; specs sit under the body (col 2) on desktop —
     same as before. The mobile block (≤540) re-flows specs to span the full
     module width (image left / text right, specs full-width below). */
  grid-template-areas: "figure body" ".      specs";
  gap: 16px;
  padding: 14px 14px 14px 0;
  border-top: 1px solid color-mix(in srgb, var(--panel-accent) 14%, transparent);
}
.lore-covenant-system-figure { grid-area: figure; min-width: 0; }
.lore-covenant-system-body  { grid-area: body; min-width: 0; }
.lore-covenant-system-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 6px;
}
.lore-covenant-system-index {
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 10px;
  letter-spacing: 0.2em;
  color: var(--panel-accent);
}
.lore-covenant-system-title {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: 14px;
  font-weight: 500;
  color: var(--lore-ink, #e6edf3);
  margin: 0;
  line-height: 1.25;
  letter-spacing: 0.01em;
}
.lore-covenant-system-summary {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: 12px;
  color: var(--lore-ink-3, #8b9d96);
  margin: 0 0 8px;
  line-height: 1.5;
}
.lore-covenant-system-specs { grid-area: specs; grid-template-columns: 1fr !important; column-gap: 0; }
.lore-covenant-system-specs .lore-covenant-spec {
  grid-template-columns: minmax(110px, 0.85fr) 1fr;
  padding: 3px 0;
}
.lore-covenant-system-specs .lore-covenant-spec dt {
  font-size: 9px;
  letter-spacing: 0.16em;
}
.lore-covenant-system-specs .lore-covenant-spec dd { font-size: 11px; }

/* Stats footer styling — same dl rows but slightly elevated treatment */
.lore-covenant-block-stats .lore-covenant-specs {
  background: color-mix(in srgb, var(--panel-accent) 4%, transparent);
  border-top: 1px solid color-mix(in srgb, var(--panel-accent) 22%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--panel-accent) 22%, transparent);
  padding: 8px 14px;
}
.lore-covenant-block-stats .lore-covenant-spec {
  border-bottom: 1px dashed color-mix(in srgb, var(--lore-ink-2, #b8d4cc) 10%, transparent);
}

/* ─── Image placeholders ───────────────────────────────────────────
   Labeled rectangular slots awaiting the user-supplied transparent
   element PNGs. Clean blueprint-style framing: corner brackets, dotted
   border, accent text. */
.lore-covenant-placeholder {
  position: relative;
  width: 100%;
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--panel-accent) 5%, transparent) 0%,
      transparent 60%),
    repeating-linear-gradient(135deg,
      transparent 0 7px,
      color-mix(in srgb, var(--panel-accent) 6%, transparent) 7px 8px);
  border: 1px dashed color-mix(in srgb, var(--panel-accent) 35%, transparent);
  border-radius: 3px;
}
.lore-covenant-placeholder[data-placeholder-kind="hero"]   { aspect-ratio: 16 / 9; }
.lore-covenant-placeholder[data-placeholder-kind="system"] { aspect-ratio: 4 / 3; }
.lore-covenant-placeholder[data-placeholder-kind="wide"]   { aspect-ratio: 6 / 1; }
.lore-covenant-placeholder-frame {
  position: absolute;
  inset: 8px;
  display: grid;
  place-items: center;
}
.lore-covenant-placeholder-corner {
  position: absolute;
  width: 14px;
  height: 14px;
  border: 1px solid var(--panel-accent);
  pointer-events: none;
}
.lore-covenant-placeholder-corner[data-corner="tl"] { top: 0;    left: 0;    border-right: 0; border-bottom: 0; }
.lore-covenant-placeholder-corner[data-corner="tr"] { top: 0;    right: 0;   border-left:  0; border-bottom: 0; }
.lore-covenant-placeholder-corner[data-corner="bl"] { bottom: 0; left: 0;    border-right: 0; border-top:    0; }
.lore-covenant-placeholder-corner[data-corner="br"] { bottom: 0; right: 0;   border-left:  0; border-top:    0; }
.lore-covenant-placeholder-label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: center;
  padding: 14px 18px;
  text-align: center;
  max-width: 92%;
}
.lore-covenant-placeholder-label > span {
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 9px;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--panel-accent);
  opacity: 0.7;
}
.lore-covenant-placeholder-label > strong {
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.06em;
  color: var(--lore-ink-3, #8b9d96);
  line-height: 1.45;
}

/* Wired covenant imagery: the real transparent PNG sits inside the blueprint
   frame (corner brackets kept as a sci-fi border); the dashed slot border +
   hatch backdrop soften once an image is present, and a small mono caption
   names the part. */
.lore-covenant-placeholder[data-has-image="true"] {
  background:
    radial-gradient(ellipse 80% 70% at 50% 45%,
      color-mix(in srgb, var(--panel-accent) 10%, transparent) 0%,
      transparent 72%);
  border-color: color-mix(in srgb, var(--panel-accent) 20%, transparent);
}
.lore-covenant-img {
  position: absolute;    /* fill the fixed framed box (grid auto-row would otherwise size to the image and overflow) */
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;   /* fit the part render inside the framed aspect box, letterboxed, no overflow */
  filter: drop-shadow(0 4px 18px rgba(0, 0, 0, 0.45));
}
.lore-covenant-caption {
  position: absolute;
  left: 8px;
  bottom: 6px;
  max-width: calc(100% - 16px);
  font-family: var(--lore-mono, 'Share Tech Mono', monospace);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--panel-accent);
  opacity: 0.55;
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Ark SECTION hero: the part render fills the whole frame edge-to-edge, scaled
   to COVER (the renders sit in transparent padding, so `contain` made them look
   small + letterboxed). Bleeding off the connecting edge makes the sections
   feel continuous — the hull carries on into the neighbouring section off-frame.
   System images keep the framed, letterboxed `contain` look above. */
.lore-covenant-placeholder[data-placeholder-kind="hero"][data-has-image="true"] {
  overflow: hidden;
}
.lore-covenant-placeholder[data-placeholder-kind="hero"][data-has-image="true"] .lore-covenant-placeholder-frame {
  inset: 0;
}
.lore-covenant-placeholder[data-placeholder-kind="hero"][data-has-image="true"] .lore-covenant-img {
  object-fit: cover;
  filter: none;
}

.lore-event-scene-caption {
  margin-top: clamp(20px, 4vh, 36px);
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: 14px;
  color: var(--lore-ink-3, #8b9d96);
  text-align: center;
}

/* Caption pinned tight to the bottom-LEFT corner of the viewport.
   Two selectors so we land the rule regardless of timing: the
   first targets the `data-galaxy-active` flag we set when the
   scene mounts; the second uses :has() to match whenever the
   stage body contains a reach scene — which is true the moment the
   chapter renders, before the wrapper async-imports scene-reach.js
   and sets the flag. `!important` on every positioning declaration
   because we're competing with the base `.lore-event-scene-caption`
   rule's centered text-align + flex-centered parent layout. */
/* Scoped to a reach-scene caption (not just the galaxy-active flag): the
   flag can linger past the reach scene, and an unscoped match leaked this
   fixed + backdrop-blur pill onto OTHER scene captions (e.g. The
   Crossing's voyage caption) - where backdrop-filter blurred the scene
   behind the text, and the text only "revealed" when the scene faded on
   continue. Requiring a reach-scene sibling keeps the pill on reach only. */
#lore-app[data-galaxy-active="true"] .lore-event-scene[data-scene="reach"] ~ .lore-event-scene-caption,
.lore-stage-body:has(> .lore-event-scene[data-scene="reach"]) > .lore-event-scene-caption {
  position: fixed !important;
  left: 24px !important;
  right: auto !important;
  bottom: 24px !important;
  top: auto !important;
  transform: none !important;
  margin: 0 !important;
  width: clamp(220px, 36vw, 380px) !important;
  padding: 10px 14px !important;
  background: rgba(2, 8, 11, 0.72) !important;
  border: 1px solid rgba(184, 212, 204, 0.22) !important;
  border-radius: 8px !important;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--lore-ink-2, #b8d4cc) !important;
  font-size: 12px !important;
  line-height: 1.45 !important;
  text-align: left !important;
  z-index: 11 !important;
  pointer-events: none;
}
/* Inside a system view the caption is more verbose ("Hover an ark
   to inspect…" still reads odd) and the user has the Back to Reach
   pill in the same vertical slot — hide the caption entirely. */
#lore-app[data-reach-phase="system"] .lore-event-scene-caption,
#lore-app[data-reach-phase="transitioning"] .lore-event-scene-caption {
  display: none;
}

/* ─── Stage footer (Back / Continue) ──────────────────────── */
.lore-stage-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  margin-top: clamp(28px, 4vh, 48px);
}
.lore-stage-nav-btn {
  appearance: none;
  background: transparent;
  border: 1px solid rgba(184,212,204,0.25);
  color: var(--lore-ink-2, #b8d4cc);
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: 13px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  padding: 14px 28px;
  cursor: pointer;
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  transition: border-color 0.25s, color 0.25s, background 0.25s, transform 0.25s;
}
.lore-stage-nav-btn[data-dir="continue"] {
  border-color: var(--tone-accent);
  color: var(--tone-accent);
  margin-left: auto;   /* always hug the RIGHT edge, even when Back is hidden (prologue) — keeps the design consistent */
}
.lore-stage-nav-btn:hover,
.lore-stage-nav-btn:focus-visible {
  outline: none;
  background: rgba(0,200,180,0.06);
  transform: translateY(-1px);
}
.lore-stage-nav-btn:disabled,
.lore-stage-nav-btn[hidden] { display: none; }
.lore-stage-nav-btn-arrow {
  display: inline-block;
  font-size: 16px;
  line-height: 1;
}

/* ─── Hub overlay (Acts library) ──────────────────────────── */
.lore-hub {
  position: fixed;
  inset: 0;
  /* Above the fixed navbar (z-index:100) so the index modal sits over
     EVERYTHING and covers it — nothing can slide under the navbar. */
  z-index: 200;
  display: grid;
  place-items: center;
  background: rgba(2,8,11,0.88);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  /* Padding gives the card breathing room and prevents 95vw from
     fighting with the scrollbar (which was causing the horizontal
     scroll). overflow-x:hidden is the belt-and-braces guard. */
  padding: 24px 16px;
  box-sizing: border-box;
  overflow-x: hidden;
  overflow-y: auto;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s;
}
.lore-hub[data-open="true"] {
  opacity: 1;
  pointer-events: auto;
}
.lore-hub-card {
  width: 100%;
  max-width: 960px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  background:
    radial-gradient(ellipse 80% 60% at 50% 0%, rgba(0,200,180,0.08), transparent 65%),
    radial-gradient(ellipse 90% 70% at 50% 100%, rgba(0,200,180,0.04), transparent 70%);
  padding: clamp(24px, 4vw, 48px);
  position: relative;
  box-sizing: border-box;
}
.lore-hub-close {
  position: absolute;
  top: 8px; right: 12px;
  background: transparent;
  border: 0;
  color: var(--lore-ink-3, #8b9d96);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  padding: 8px;
}
.lore-hub-close:hover, .lore-hub-close:focus-visible { color: var(--tone-accent); outline: none; }
/* Hub header bar: back arrow | title | close */
.lore-hub-head {
  display: grid;
  grid-template-columns: 100px 1fr 32px;
  align-items: center;
  gap: 12px;
  margin: 0 0 4px;
}
.lore-hub-back {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--lore-ink-3, #b8d4cc);
  font-family: var(--lore-mono, monospace);
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  padding: 6px 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  justify-self: start;
  transition: color 0.2s;
}
.lore-hub-back:hover, .lore-hub-back:focus-visible {
  outline: none; color: var(--tone-accent, #00c8b4);
}
.lore-hub-back > span { font-size: 14px; line-height: 1; }
.lore-hub-back[hidden] { display: none; }
.lore-hub-title {
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: 22px;
  letter-spacing: 0.2em;
  color: var(--lore-ink, #e6edf3);
  margin: 0;
  text-align: center;
  justify-self: center;
}
.lore-hub-close {
  justify-self: end;
  position: static;
  top: auto; right: auto;
}
.lore-hub-sub {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: 14px;
  color: var(--lore-ink-3, #b8d4cc);
  margin: 0 0 24px;
  text-align: center;
}

/* ─── Acts grid ─── */
.lore-hub-acts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
}
.lore-hub-act-card {
  appearance: none;
  background:
    radial-gradient(ellipse 100% 60% at 50% 0%, var(--tone-soft), transparent 70%),
    rgba(2, 8, 11, 0.4);
  border: 0;
  padding: 24px 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-align: left;
  cursor: pointer;
  position: relative;
  color: var(--lore-ink-2, #b8d4cc);
  transition: transform 0.25s, filter 0.25s;
  min-height: 170px;
}
.lore-hub-act-card:hover,
.lore-hub-act-card:focus-visible {
  outline: none;
  transform: translateY(-3px);
  filter: drop-shadow(0 0 22px var(--tone-soft));
}
.lore-hub-act-card[data-state="locked"] {
  cursor: not-allowed;
  /* Mute the act content but keep enough contrast that the title is
     still readable. Drop the harsh grayscale — colour helps the reader
     anticipate the locked content's tone (amber/teal/violet). */
  filter: brightness(0.65) saturate(0.55);
  opacity: 0.85;
}
.lore-hub-act-card[data-state="locked"]:hover {
  transform: none;
  filter: brightness(0.75) saturate(0.65);
}
.lore-hub-act-card-foot-locked {
  color: #ffb24a !important;
  letter-spacing: 0.28em;
}
/* Padlock badge: amber pill in the top-right corner of locked acts. */
.lore-hub-act-card-lock-badge {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  background: rgba(20, 12, 4, 0.92);
  border: 1px solid #ffb24a;
  border-radius: 6px;
  color: #ffb24a;
  box-shadow: 0 0 12px rgba(255, 178, 74, 0.35);
  z-index: 2;
  /* Counter-rotate the parent's `filter: brightness/saturate` so the
     padlock stays at full amber visibility — without this, the locked
     card's brightness-reduce dims the badge along with everything else. */
  filter: brightness(1.4) saturate(1.6);
}
.lore-hub-act-card[data-state="current"] {
  filter: drop-shadow(0 0 24px var(--tone-soft));
}
.lore-hub-act-card[data-state="current"]::after {
  content: "";
  position: absolute;
  left: 18px; right: 18px; top: -1px;
  height: 2px;
  background: var(--tone-accent);
  box-shadow: 0 0 14px var(--tone-accent);
}
.lore-hub-act-card-num {
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: 22px;
  letter-spacing: 0.18em;
  color: var(--tone-accent);
  text-shadow: 0 0 14px var(--tone-soft);
}
.lore-hub-act-card-title {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: 18px;
  color: var(--lore-ink, #ffffff);
  font-weight: 600;
}
.lore-hub-act-card-sub {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-style: italic;
  font-size: 13px;
  color: var(--lore-ink-3, #b8d4cc);
  line-height: 1.4;
  flex: 1 1 auto;
}
.lore-hub-act-card-foot {
  font-family: var(--lore-mono, monospace);
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--tone-accent);
  margin-top: 10px;
}
.lore-hub-act-card-lock {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: #ffb24a;
  filter: drop-shadow(0 0 14px rgba(255,178,74,0.55));
  pointer-events: none;
}
.lore-hub-act-card-lock svg { width: 36px; height: 36px; }

/* ─── Chapters list (per-act view) ─── */
.lore-hub-chapters-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.lore-hub-chapter-row {
  appearance: none;
  background: transparent;
  border: 0;
  border-left: 2px solid color-mix(in srgb, var(--tone-accent) 35%, transparent);
  text-align: left;
  display: grid;
  grid-template-columns: 56px 1fr 24px;
  align-items: center;
  gap: 16px;
  padding: 14px 18px;
  color: var(--lore-ink-2, #b8d4cc);
  cursor: pointer;
  transition: background 0.2s, border-left-color 0.2s, color 0.2s, transform 0.2s;
}
.lore-hub-chapter-row:hover,
.lore-hub-chapter-row:focus-visible {
  outline: none;
  background: color-mix(in srgb, var(--tone-accent) 10%, transparent);
  border-left-color: var(--tone-accent);
  color: var(--lore-ink, #ffffff);
  transform: translateX(4px);
}
.lore-hub-chapter-row[data-state="current"] {
  border-left-color: var(--tone-accent);
  background: color-mix(in srgb, var(--tone-accent) 12%, transparent);
}
.lore-hub-chapter-row-index {
  font-family: var(--lore-display, 'Audiowide', monospace);
  font-size: 18px;
  color: var(--tone-accent);
  letter-spacing: 0.08em;
  text-shadow: 0 0 10px var(--tone-soft);
}
.lore-hub-chapter-row-body { display: flex; flex-direction: column; gap: 4px; }
.lore-hub-chapter-row-title {
  font-family: var(--lore-serif, 'Exo 2', sans-serif);
  font-size: 16px;
  color: var(--lore-ink, #e6edf3);
}
.lore-hub-chapter-row-meta {
  font-family: var(--lore-mono, monospace);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--lore-ink-3, #b8d4cc);
}
.lore-hub-chapter-row-cue {
  color: var(--tone-accent);
  font-size: 18px;
  text-align: right;
}

/* ─── Rotate-to-landscape gate (mobile portrait) ──────────────────── */
.lore-rotate-gate {
  position: fixed;
  inset: 0;
  z-index: 9999;                 /* hard takeover: above HUD, cards, and the global navbar */
  display: none;                 /* shown ONLY by the portrait media query below */
  align-items: center;
  justify-content: center;
  padding: 32px;
  background: radial-gradient(circle at 50% 38%, #08171d 0%, #02080b 72%);
  color: var(--lore-ink, #e6edf3);
  text-align: center;
  touch-action: none;            /* swallow touches so the lore behind can't scroll/tap through */
}
.lore-rotate-gate-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(14px, 3vh, 22px);
  max-width: 360px;
}
.lore-rotate-gate-icon {
  color: var(--tone-accent, #51e9b6);
  filter: drop-shadow(0 0 14px rgba(81,233,182,0.35));
  animation: lore-rotate-hint 2.8s ease-in-out infinite;
}
.lore-rotate-gate-title {
  font-family: var(--lore-display, 'Orbitron', sans-serif);
  font-size: clamp(20px, 6vw, 30px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.lore-rotate-gate-text {
  font-family: var(--lore-mono, monospace);
  font-size: clamp(12px, 3.4vw, 14px);
  line-height: 1.55;
  letter-spacing: 0.03em;
  color: var(--lore-ink-2, #b8d4cc);
}
@keyframes lore-rotate-hint {
  0%, 12%   { transform: rotate(0deg); }
  44%, 56%  { transform: rotate(-90deg); }
  88%, 100% { transform: rotate(0deg); }
}
/* THE GATE: touch phones/tablets in portrait see only this overlay. The
   (pointer: coarse) guard keeps narrow desktop windows unaffected. */
@media (orientation: portrait) and (max-width: 900px) and (pointer: coarse) {
  .lore-rotate-gate { display: flex; }
}

/* ─── Transitions (GSAP toggles these classes) ────────────── */
.lore-stage[data-transition="entering"] .lore-stage-body { opacity: 0; transform: translateY(12px); }
.lore-stage[data-transition="exiting"]  .lore-stage-body { opacity: 0; transform: translateY(-12px); }
.lore-stage-body {
  transition: opacity 0.4s ease, transform 0.4s ease;
}

@media (prefers-reduced-motion: reduce) {
  .lore-stage-body,
  .lore-hub,
  .lore-cover-cta,
  .lore-event-card {
    transition: none !important;
  }
  .lore-rotate-gate-icon { animation: none !important; }
  .lore-event-card:hover { transform: none; }
  .lore-stage-nav-btn:hover { transform: none; }
}

/* ─── Mobile tightening ───────────────────────────────────── */
@media (max-width: 640px) {
  .lore-stage { padding: 80px 16px 90px; }
  .lore-stage-nav { gap: 8px; }
  .lore-stage-nav-btn { padding: 11px 18px; font-size: 11px; letter-spacing: 0.24em; }
  .lore-event-data-grid { grid-template-columns: 1fr 1fr; }
  .lore-event-cards { grid-template-columns: 1fr 1fr; }
  .lore-cover-cta { padding: 14px 24px; letter-spacing: 0.24em; font-size: 13px; }
  .lore-hud { font-size: 10px; letter-spacing: 0.16em; padding: 10px 14px; }
  .lore-hud-progress-act { font-size: 13px; }
}

/* ─── Short landscape (phones held sideways: wide but <= ~540px tall) ─
   Landscape phones are wide yet SHORT, so the width-based rules above
   miss them. Mobile is landscape-only now (portrait shows the rotate
   gate), so this is effectively THE phone layout: trim the tall vertical
   paddings, scale headings to the available HEIGHT (vh), fill the short
   viewport with the WebGL scene, and pull the fixed-px Reach controls up
   under a slimmed HUD so they never fall off a ~360px-tall screen.
   svh keeps it stable across the mobile URL-bar show/hide. */
@media (orientation: landscape) and (max-height: 540px) {
  /* Cover splash fits a short screen WITHOUT scroll: the L.R mark circle (a fixed
     120-180px on desktop) is the main culprit, so scale it + the gaps + title to
     the available HEIGHT. */
  .lore-cover { min-height: 100svh; padding: 12px clamp(20px, 5vw, 56px); }
  .lore-cover-inner { gap: clamp(8px, 2.2svh, 22px); }
  .lore-cover-mark { width: clamp(64px, 17svh, 120px); height: clamp(64px, 17svh, 120px); }
  .lore-cover-mark::before { inset: -8px; }
  .lore-cover-mark::after  { inset: -16px; }
  .lore-cover-mark-glyph { font-size: clamp(20px, 5svh, 38px); }
  .lore-cover-title { font-size: clamp(22px, 7.5svh, 46px); }
  .lore-cover-subtitle { font-size: clamp(11px, 2.6svh, 15px); }

  /* Reading chapters: trim padding, scale the act head to height. */
  /* min-height fits the space BELOW the fixed nav, not the whole viewport:
     .page-content pads the lore app down by --nav-height (64px), so a plain
     100svh stage runs 64px past a short screen and drops Back/Continue off. */
  /* Fill the .lore-screen scroller (100dvh) EXACTLY, not 100svh: the svh/dvh
     mismatch left the nav floating above the true bottom on a real phone (URL
     bar shown). 100% tracks the scroller so Back/Continue sit at the bottom. */
  .lore-stage { min-height: 100%; padding: 40px clamp(16px, 4vw, 40px) 16px; }
  .lore-stage-body { min-height: 0; }

  /* Pin Back/Continue to the viewport bottom on mobile so they're ALWAYS there.
     A medium narrative (e.g. "The Disc" V-1-1, "The Contenders" VI-1-1) overflows
     a short phone just enough to push the in-grid nav below the fold; a fixed bar
     fixes that for every chapter (and tall ones too). The content gets bottom
     room so its last line clears the bar. Fullscreen scenes keep their own fixed
     nav, so they're excluded. */
  #lore-app:not([data-galaxy-active="true"]) .lore-stage { padding-bottom: 58px; }
  #lore-app:not([data-galaxy-active="true"]) .lore-stage-nav {
    position: fixed; left: 0; right: 0; bottom: 0; margin: 0; z-index: 12;
    gap: 10px;
    padding: 8px clamp(12px, 4vw, 24px) calc(8px + env(safe-area-inset-bottom, 0px));
    background: linear-gradient(to top, rgba(2, 8, 11, 0.92) 55%, rgba(2, 8, 11, 0));
    pointer-events: none;
  }
  #lore-app:not([data-galaxy-active="true"]) .lore-stage-nav-btn { pointer-events: auto; }
  .lore-stage-act { font-size: clamp(20px, 6vh, 32px); }
  .lore-stage-nav { margin-top: 16px; }

  /* Slim top HUD + compact actions so Index / Cover / Mute / EXIT ALL fit a
     narrow landscape phone — EXIT was overflowing off the right edge (the last
     icon the user saw was Mute). The chapter label shrinks/ellipsizes rather
     than pushing the actions off-screen. */
  .lore-hud { padding: 7px clamp(8px, 2.5vw, 16px); gap: 8px; }
  .lore-hud-progress { min-width: 0; flex: 0 1 auto; overflow: hidden; }
  .lore-hud-progress-act { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .lore-hud-actions { gap: 4px; flex: 0 0 auto; }
  .lore-hud-btn { padding: 4px 7px; font-size: 9.5px; letter-spacing: 0.06em; }

  /* Inline WebGL scenes fill the short viewport instead of a tall
     aspect-ratio box (which would overflow a 360px-tall screen). */
  .lore-event-scene { height: 78svh; min-height: 0; max-width: none; aspect-ratio: auto; }

  /* Reach overlay controls: stack just under the slim HUD. */
  #lore-app .lore-reach-orbits-toggle { top: 50px; }
  #lore-app .lore-reach-zoom { top: 90px; }

  /* Info card: a right rail filling the short height. A wide-but-short
     landscape phone slips past the width-based bottom-drawer rule, so it
     would otherwise stay pinned at top:130px with almost no height. */
  #lore-app .lore-reach-card {
    top: 48px; right: 10px; left: auto; bottom: auto;   /* content-height, not stretched top->bottom */
    width: min(340px, 46vw); max-height: calc(100dvh - 60px); overflow-y: auto;
  }

  /* Galaxy "back" button up off the very bottom edge. */
  #lore-app[data-galaxy-active="true"] > .lore-reach-back { bottom: 12px; }
}

/* ─── Landscape phones (cont.): the block above trims padding + the act title,
   but the BODY text (narrative 20–26px/1.6, telemetry 18px + big padding) stayed
   full-size, so BACK/CONTINUE fell below a ~360–420px-tall fold. Shrink the
   reading body, sigils, Reach labels, end-cover + nav for the short height.
   Everything here is height-gated, so desktop is untouched. (Loads only on
   /lore, so the #nav rule can't reach other pages.) ─────────────────────── */
@media (orientation: landscape) and (max-height: 540px) {
  /* Slim the global nav so it isn't cramped at landscape width (shots 6/12/15). */
  #nav { padding-left: 14px; padding-right: 14px; }
  .nav-links { gap: clamp(12px, 2.4vw, 22px); }
  .nav-links a { font-size: 12px; letter-spacing: 0.02em; }

  /* THE mono-page fix: the fixed 64px nav pushes #lore-app down (via
     .page-content padding-top), yet #lore-app is min-height:100vh — so it
     extends 64px past a short viewport and Back/Continue fall below the fold.
     Fit it (and the stage, above) to the height that's actually visible. */
  #lore-app { min-height: 100svh; }

  /* Tighter act head + nav. */
  .lore-stage { padding-top: 42px; padding-bottom: 12px; }
  .lore-stage-head { margin-bottom: 8px; gap: 3px; }
  .lore-stage-eyebrow { font-size: 9.5px; letter-spacing: 0.3em; gap: 8px; }
  .lore-stage-eyebrow::before, .lore-stage-eyebrow::after { width: 14px; }
  .lore-stage-act { font-size: clamp(18px, 5vh, 27px); }
  .lore-stage-chapter { font-size: 12px; }
  .lore-stage-nav { margin-top: 10px; }
  .lore-stage-nav-btn { padding: 9px 16px; font-size: 11px; letter-spacing: 0.2em; }

  /* Narrative prose — the main culprit for the below-fold nav. Scale to
     height, tighten the leading. */
  .lore-event-narrative { font-size: clamp(13.5px, 3.9vh, 18px); line-height: 1.4; max-width: 680px; }
  .lore-event-narrative .lore-event-tag { margin-top: 12px; }

  /* Telemetry / data block: trim padding, force a tidy even 3-col grid. */
  .lore-event-data { padding: 8px clamp(8px, 2vw, 18px); }
  .lore-event-data-title { font-size: 13px; margin: 0 0 8px; }
  .lore-event-data-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px 18px; }
  .lore-event-data-value { font-size: 14px; }
  .lore-event-data-label { font-size: 9.5px; letter-spacing: 0.2em; }

  /* Cards (3 artefacts / 4 sigils): one shrink-to-fit row, no horizontal
     overflow. flex + min-width:0 lets a wide sigil's column drop below its
     content width instead of pushing the row past the edge (shot 14). */
  .lore-event-cards { display: flex; flex-wrap: nowrap; justify-content: center; gap: clamp(6px, 1.6vw, 18px); }
  .lore-event-card { flex: 1 1 0; min-width: 0; padding: 4px 6px 6px; }
  /* Cap above the JS equal-area max (76px) so it doesn't flatten the optical
     balance — the loader sets each sigil's height by area (REF/√aspect). */
  /* Sigils (contenders) + glyphs (revelations): scale to viewport HEIGHT so they
     SHRINK on a short phone instead of forcing a slim scroll on those pages. The
     !important caps the JS-set inline height (the loader sets each sigil's height
     by equal-area) to the slot so the image never spills past it. */
  .lore-event-card-sigil-box { height: clamp(52px, 16svh, 84px); margin-bottom: 6px; }
  .lore-event-card-sigil { max-height: clamp(52px, 16svh, 84px) !important; margin-bottom: 0; }
  .lore-event-card-img { margin-bottom: 6px; }
  .lore-event-card-glyph { width: clamp(38px, 11.5svh, 48px); height: clamp(38px, 11.5svh, 48px); margin-bottom: 6px; }
  .lore-event-card-num { margin-bottom: 4px; font-size: 10px; }
  .lore-event-card-name { font-size: clamp(14px, 2.8vh, 18px); margin-bottom: 4px; }
  .lore-event-card-hint { font-size: 10.5px; line-height: 1.3; }
  .lore-event-card-cargo { margin-top: 7px; padding-top: 6px; }

  /* Reach destination labels (Moravec / Alephward / Ouranion): shrink so the
     three stop overlapping when the galaxy is small (shot 12). */
  .lore-ark-label-card { width: 92px; padding: 4px 8px 5px; }
  .lore-ark-label-eyebrow { font-size: 8px; letter-spacing: 0.2em; margin-bottom: 2px; }
  .lore-ark-label-name { font-size: 12px; }
  .lore-ark-label-leader { height: 12px; }
  .lore-ark-label-dot { width: 7px; height: 7px; }

  /* End cover: the keyboard-hint foot ("Press → … H for index") overlaps the
     CTAs on a short screen (shot 16) and is useless on touch — drop it. */
  .lore-cover-foot { display: none; }
  .lore-cover-ctas { gap: 10px; }

  /* Index menu (item 11): smaller frames + text so the act cards aren't
     oversized on a 390px screen (the 170px min-height card ate ~45% of it). */
  .lore-hub-title { font-size: 18px; letter-spacing: 0.16em; }
  .lore-hub-sub { font-size: 12px; margin: 0 0 12px; }
  .lore-hub-head { margin-bottom: 2px; }
  .lore-hub-acts-grid { gap: 12px; }
  .lore-hub-act-card { min-height: 0; padding: 12px 14px; gap: 4px; }
  .lore-hub-act-card-num { font-size: 17px; }
  .lore-hub-act-card-title { font-size: 14px; }
  .lore-hub-act-card-sub { font-size: 11px; line-height: 1.3; }
  .lore-hub-act-card-foot { font-size: 9px; margin-top: 6px; }
  .lore-hub-chapter-row { padding: 10px 12px; }

  /* Contender sigil detail (item 10): shrink the big logo + numeral so the
     two-column logo-left / text-right detail fits the short height. */
  .lore-ark-detail-sigil { max-height: clamp(86px, 30vh, 150px); }
  .lore-ark-detail--simple .has-sigil .lore-ark-detail-eyebrow,
  .has-sigil .lore-ark-detail-eyebrow { font-size: clamp(26px, 6vh, 44px); }
  .lore-ark-detail-simple-body.has-sigil { gap: clamp(16px, 3vw, 36px); align-items: center; }
  .lore-ark-detail-hint { font-size: 12.5px; line-height: 1.4; }

  /* Index menu (item 4): ONE scroll container — the card had its own
     overflow-y:auto on TOP of the hub's, making a double scrollbar. The hub
     now covers the navbar via z-index (base rule), so no nav-clearance padding
     is needed; just top-align so tall content scrolls cleanly from the top. */
  .lore-hub { align-items: start; }
  .lore-hub-card { max-height: none; overflow-y: visible; }

  /* Covenant key-systems (item 1): specs span the FULL module width, starting
     at the left under the image — not cramped in the right column. */
  .lore-covenant-system { grid-template-areas: "figure body" "specs specs"; }
}

/* sr-only utility (matches site convention) */
.lore-stage h1.sr-only,
#lore-app .sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}
