/* ============================================================================
   Cinematic timeline — themeable.
   Every colour is a CSS variable (see :root). A dataset picks a theme with
   `theme: "<name>"` in data.js; the engine sets <html data-theme="…"> and the
   matching block in the THEMES section at the bottom overrides the palette.
   ============================================================================ */
:root {
  /* ===== THEME PALETTE (default = "midnight") ============================== */
  --bg-0: #05070d;          /* outer background / vignette edge */
  --bg-1: #0b1020;          /* mid background */
  --bg-haze: #11192f;       /* soft glow at the centre of the backdrop */
  --line: #2b3a5c;          /* axis base */
  --line-glow: #4f7cff;     /* axis / connector highlight */
  --glow-rgb: 79, 124, 255; /* rgb of the axis/connector glow (used in shadows) */
  --accent: #5b9bff;        /* focus glow, primary buttons, active scrubber dot */
  --accent-rgb: 91, 155, 255;  /* rgb of --accent (used in soft fills/glows) */
  --accent-deep: #3f7fe8;   /* bottom stop of the primary-button gradient */
  --accent-soft: #7db4ff;   /* eyebrow text, links, arrows */
  --on-accent: #06122b;     /* text on the accent fill: >=4.5:1 on --accent AND --accent-deep */
  --text: #e8eefc;          /* body text */
  --text-dim: #8b9bc4;      /* secondary text, minor/tick/band labels */
  --ink: #ffffff;           /* strong headings (card + panel titles) */
  --card-bg: rgba(16, 23, 43, 0.92);
  --card-border: rgba(90, 130, 220, 0.35);
  --ctrl-bg: rgba(20, 30, 58, 0.8);        /* buttons / chips / scrubber bg */
  --ctrl-bg-hover: rgba(40, 60, 110, 0.9);
  --soft-fill: rgba(255, 255, 255, 0.08);  /* faint inset fills (kbd, code, ✕) */
  --soft-fill-2: rgba(255, 255, 255, 0.12);
  --hairline: rgba(255, 255, 255, 0.10);   /* faint dashed band edges */
  --panel-top: #0e1630;     /* top of the details-panel gradient */
  /* Status colours (quiz right/wrong, streaks, study traits). The light
     themes darken them so they still read on a pale page. */
  --good: #7ed957;          --good-rgb: 126, 217, 87;   /* correct / clean */
  --good-alt: #3ddc84;      --good-alt-rgb: 61, 220, 132; /* study: good traits, disciples */
  --bad: #ff6b6b;           --bad-rgb: 255, 107, 107;   /* wrong / enemy / removed */
  --warn: #f5b942;          --warn-rgb: 245, 185, 66;   /* streak / bonus / broken */
  --on-status: #0b0b14;     /* text on a solid status fill */

  /* Home-made base map (engines/map.js draws land/sea/lake/river/coast + a grayscale
     relief image tinted via mix-blend-mode; flat relief = white). */
  --map-land: #525766;
  --map-sea: #0d1318;
  --map-lake: var(--map-sea);
  --map-river: #2e404f;
  --map-coast: #7d818c;
  --map-relief-opacity: 0.80;
  --map-relief-blend: multiply;
  /* Map labels (engines/map.js "Map labels"): water/land/region/note text colours and the
     halo (text-shadow) colour = the land colour; sea labels halo in --map-sea. */
  --map-label-water: #a4b7ca;
  --map-label-land: #cbc4b8;
  --map-label-region: #dce2f0;
  --map-label-note: #a8b4d3;
  --map-label-halo: #525766;

  --font: "Segoe UI", system-ui, -apple-system, Roboto, sans-serif;
  /* Display face: site header, shelf headings, card titles. */
  --font-display: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia,
                  "Times New Roman", serif;
  /* Base text size of the "More info" details panel (NOT themed). Everything
     inside it (headings, lists, code…) scales from this. Fluid: 36px on wide
     screens down to 17px on a phone (engines/timeline.js reads the computed size). */
  --detail-font: clamp(17px, 1.6vw + 10px, 36px);
  --detail-title: clamp(19px, 1.5vw + 12px, 34px);   /* the panel's heading (the event title) */
}

* { box-sizing: border-box; }

/* Keyboard focus ring for everything interactive. :where() keeps it at zero
   specificity, so an engine's own :focus-visible style still wins. Where a
   design shows focus another way it uses `outline: 2px solid transparent`,
   which stays invisible normally but is painted in Windows forced-colours. */
:where(a, button, [tabindex], summary, input, select, textarea):focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 2px;
}

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--bg-0);
  color: var(--text);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
}

/* ---- Camera viewport -------------------------------------------------- */
#stage {
  position: fixed;
  inset: 0;
  overflow: hidden;
  cursor: grab;          /* the whole surface is pannable */
  background:
    radial-gradient(1200px 800px at 50% 40%, var(--bg-haze) 0%, var(--bg-1) 45%, var(--bg-0) 100%);
}

/* faint starfield vignette so empty space doesn't look flat */
#stage::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(1px 1px at 20% 30%, rgba(255,255,255,.5), transparent),
    radial-gradient(1px 1px at 75% 60%, rgba(255,255,255,.35), transparent),
    radial-gradient(1px 1px at 40% 80%, rgba(255,255,255,.3), transparent),
    radial-gradient(1px 1px at 90% 20%, rgba(255,255,255,.4), transparent),
    radial-gradient(1px 1px at 60% 15%, rgba(255,255,255,.25), transparent);
  opacity: .6;
}

/* ---- World (the layer the camera moves) ------------------------------- */
#world {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  /* NOTE: deliberately no `will-change: transform` here. It promotes the layer
     to a cached bitmap that gets scaled like an image (blurry text on zoom).
     Without it the browser re-rasterises vector text crisply at the new scale. */
}

/* ---- Axis ------------------------------------------------------------- */
#axis {
  position: absolute;
  height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg,
    transparent 0%, var(--line) 6%, var(--line-glow) 50%, var(--line) 94%, transparent 100%);
  box-shadow: 0 0 12px rgba(var(--glow-rgb), 0.55), 0 0 40px rgba(var(--glow-rgb), 0.25);
}

/* ---- Markers (minor events only) -------------------------------------- */
/* Major events leave NO resting dot on the axis — their connector + card mark
   the spot and the focus ping does the glowing. The only dots that stay on the
   line are the labelled ticks and these small minor markers, which need a dot
   because they have no card. */
.marker.minor {
  position: absolute;
  width: 7px;
  height: 7px;
  margin-left: -3.5px;
  margin-top: -3.5px;
  border-radius: 50%;
  background: var(--accent);
  opacity: .6;
}
/* ---- Ticks (custom time dots, defined in data.js `ticks[]`) ----------- */
/* A short vertical mark on the axis carrying the only time text. */
.tick {
  position: absolute;
  z-index: 0;            /* below everything else on the axis */
  width: 2px;
  height: 16px;
  margin-left: -1px;
  margin-top: -8px;
  border-radius: 2px;
  background: linear-gradient(var(--line-glow), rgba(var(--glow-rgb), 0.1));
  opacity: .55;
}

/* ---- Events (connector + card) ---------------------------------------- */
/* The node is a ZERO-WIDTH anchor sitting exactly at the event's x. The card is
   centred on it (left: -140 in JS) and the connector/ping ride x directly, so
   the card, its stem, and any tick at the same `at` all line up. */
.event {
  position: absolute;
  width: 0;
  transition: opacity .45s ease;
}
/* Stacking order: connectors (1) run BEHIND cards (4) so a higher lane's stem
   passes behind the cards it crosses and appears to emerge from behind them.
   Markers (3) sit above connectors but below cards. */
.marker { z-index: 3; }
.event .connector {
  position: absolute;
  z-index: 1;
  left: 0;
  width: 2px;
  margin-left: -1px;
  background: linear-gradient(var(--line-glow), rgba(var(--glow-rgb), .15));
  box-shadow: 0 0 8px rgba(var(--glow-rgb), 0.5);
}
.event .card {
  position: absolute;
  z-index: 4;
  left: 0;
  width: 280px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  overflow: hidden;        /* clip the media banner to the rounded corners */
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.55), inset 0 0 0 1px rgba(255,255,255,0.02);
  /* no backdrop-filter: it forces the card to rasterise and blurs when zoomed */
}
.event .card h3 {
  margin: 0 0 6px;
  font-size: 16px;
  color: var(--ink);
  letter-spacing: .01em;
}
.event .card .date {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--accent-soft);
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.event .card p {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-dim);
}

/* up = card sits above the axis; down = below. JS sets connector/card offsets. */
.event.dimmed { opacity: 0.18; }
.event.focused .card {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 0 28px rgba(var(--accent-rgb), 0.5), 0 10px 40px rgba(0,0,0,.6);
}
@keyframes ping {
  0%   { transform: scale(.4); opacity: .9; }
  100% { transform: scale(1.8); opacity: 0; }
}

/* ---- HUD / controls --------------------------------------------------- */
#hud {
  position: fixed;
  top: 22px;
  left: 0; right: 0;
  text-align: center;
  pointer-events: none;
  z-index: 10;
  transition: opacity .4s ease;
}
/* While focused on an event, the whole top bar (title, subtitle, legend) fades
   away for an immersive, chrome-free journey. */
#hud.gone { opacity: 0; }
#hud.gone #legend { pointer-events: none; }
#title {
  font-size: 26px;
  font-weight: 700;
  letter-spacing: .03em;
  text-shadow: 0 2px 18px rgba(0,0,0,.7);
}
#era {
  font-size: 13px;
  letter-spacing: .35em;
  text-transform: uppercase;
  color: var(--accent-soft);
  margin-top: 4px;
}

/* ---- Scrubber: clickable dot-rail + position counter ------------------ */
/* Sits bottom-centre; only shown while focused (where Start has stepped aside). */
#scrubber {
  position: fixed;
  bottom: 34px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: min(72vw, 760px);
  padding: 8px 16px;
  background: var(--ctrl-bg);
  border: 1px solid var(--card-border);
  border-radius: 999px;
  backdrop-filter: blur(4px);
}
#scrubDots {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
  justify-content: center;
}
#progress {
  font-size: 12px;
  letter-spacing: .1em;
  color: var(--text-dim);
  white-space: nowrap;
}

.ctrl {
  position: fixed;
  z-index: 20;
  font-family: var(--font);
  font-size: 14px;
  color: var(--text);
  background: var(--ctrl-bg);
  border: 1px solid var(--card-border);
  border-radius: 999px;
  padding: 11px 22px;
  cursor: pointer;
  backdrop-filter: blur(4px);
  transition: transform .15s ease, background .2s ease, box-shadow .2s ease;
}
.ctrl:hover { background: var(--ctrl-bg-hover); transform: translateY(-1px); }
.ctrl.primary {
  left: 50%;
  bottom: 36px;
  transform: translateX(-50%);
  color: var(--on-accent);
  background: linear-gradient(180deg, var(--accent), var(--accent-deep));
  border-color: var(--accent-soft);
  box-shadow: 0 0 22px rgba(var(--accent-rgb), .5);
  font-weight: 600;
}
.ctrl.primary:hover { transform: translateX(-50%) translateY(-1px); }
#resetBtn { top: 66px; left: 24px; }   /* sits below the ⌂ Charts link */
/* A smaller secondary pill right-aligned under ⛶, clear of ▶ Play (which
   widens to ❚❚ Pause). It steps aside while the tour plays. */
#exportPdf {
  top: 76px; right: 24px;
  font-size: 13px; padding: 7px 14px;
  color: var(--text-dim);
  transition: transform .15s ease, background .2s ease, opacity .3s ease, visibility 0s linear 0s;
}
#exportPdf:hover { color: var(--text); }

/* ---- Prev / Next ------------------------------------------------------ */
.nav {
  position: fixed;
  bottom: 32px;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font);
  font-size: 15px;
  color: var(--text);
  background: var(--ctrl-bg);
  border: 1px solid var(--card-border);
  border-radius: 999px;
  padding: 12px 20px;
  cursor: pointer;
  backdrop-filter: blur(4px);
  transition: transform .15s ease, background .2s ease, opacity .2s ease;
}
.nav:hover { background: var(--ctrl-bg-hover); }
.nav-prev { left: 28px; }
.nav-next { right: 28px; }
.nav-prev:hover { transform: translateX(-3px); }
.nav-next:hover { transform: translateX(3px); }
.nav-arrow { font-size: 22px; line-height: 1; color: var(--accent-soft); }
.nav:disabled { opacity: .3; cursor: default; transform: none; }

#hint {
  position: fixed;
  left: 50%;
  bottom: 92px;
  transform: translateX(-50%);
  font-size: 12px;
  color: var(--text-dim);
  letter-spacing: .04em;
  z-index: 10;
  pointer-events: none;
  transition: opacity .4s ease;
}
#hint kbd {
  background: var(--soft-fill);
  border: 1px solid var(--card-border);
  border-radius: 5px;
  padding: 1px 6px;
  font-family: var(--font);
}

/* ---- Zoom: magnifier slider pill, right edge, vertically centred ------- */
/* Plain scroll pans the timeline sideways; this (and Ctrl + scroll / pinch)
   zooms. engines/timeline.js keeps the thumb in sync with every zoom path. */
#zoomCtl {
  position: fixed;
  right: 24px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 6px;
  background: var(--ctrl-bg);
  border: 1px solid var(--card-border);
  border-radius: 999px;
  backdrop-filter: blur(4px);
}
.zoom-btn {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--accent-soft);
  cursor: pointer;
  transition: background .2s ease;
}
.zoom-btn:hover { background: var(--ctrl-bg-hover); }
.zoom-btn svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
}
/* Vertical range: top = zoomed in (direction: rtl flips the vertical axis). */
#zoomRange {
  writing-mode: vertical-lr;
  direction: rtl;
  width: 18px;
  height: 140px;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}

[hidden] { display: none !important; }

/* The PDF booklet (core/print.js) is print-only: core/print.css loads with media="print"
   and reveals it there, so the screen only needs to keep it out of the way. */
#printRoot { display: none; }

/* ---- Era / period bands (data.js `bands[]`) --------------------------- */
/* A translucent vertical column behind the timeline marking a span of `at`. */
.band {
  position: absolute;
  z-index: -1;                 /* behind the axis, ticks, and cards */
  background: var(--band, rgba(var(--accent-rgb), 0.06));
  border-left: 1px dashed var(--hairline);
  border-right: 1px dashed var(--hairline);
  pointer-events: none;
}

/* ---- Category legend / filter chips (data.js `categories`) ------------ */
#legend {
  margin-top: 12px;
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
  pointer-events: auto;        /* HUD is pointer-events:none; re-enable here */
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font);
  font-size: 12px;
  color: var(--text);
  background: var(--ctrl-bg);
  border: 1px solid var(--card-border);
  border-radius: 999px;
  padding: 4px 12px;
  cursor: pointer;
  transition: opacity .2s ease, transform .1s ease;
}
.chip:hover { transform: translateY(-1px); }
.chip .chip-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--cat, var(--accent));
}
.chip.off { opacity: .4; }
.chip.off .chip-dot { background: var(--text-dim); }



/* Events filtered out by the legend fade back and stop catching clicks. */
.event.filtered, .marker.filtered { opacity: .06; pointer-events: none; }

/* ---- Play / tour button ----------------------------------------------- */
#playBtn { top: 22px; right: 78px; }   /* left of ⛶ */
#playBtn.playing {
  color: var(--on-accent);
  background: linear-gradient(180deg, var(--accent), var(--accent-deep));
  border-color: var(--accent-soft);
}

/* When dragging, show a grabbing cursor over the whole stage. */
#stage.grabbing { cursor: grabbing; }

/* ---- Immersive mode (engines/timeline.js, HL.immersive; shared fade rules under
   "IMMERSIVE MODE") -------------------------------------------------------
   ⛶ sits top-right, ▶ Play to its left. While on, the overview fills the
   screen and the chrome floats over it, with soft top / bottom scrims under
   the title and the buttons that fade with them (below the HUD, above the
   world; the details panel is above both). */
#fsBtn { top: 22px; right: 24px; padding: 11px 15px; }
body.chart-mode.hl-immersive #timelineRoot::before,
body.chart-mode.hl-immersive #timelineRoot::after {
  content: ""; position: fixed; left: 0; right: 0; z-index: 0; pointer-events: none;
  transition: opacity 0.5s ease;
}
body.chart-mode.hl-immersive #timelineRoot::before { top: 0; height: 130px; background: linear-gradient(rgba(0, 0, 0, 0.5), transparent); }
body.chart-mode.hl-immersive #timelineRoot::after { bottom: 0; height: 120px; background: linear-gradient(transparent, rgba(0, 0, 0, 0.5)); }
body.chart-mode.hl-idle #timelineRoot::before,
body.chart-mode.hl-idle #timelineRoot::after { opacity: 0; }

/* ---- Details panel (data: `details`, markdown) ------------------------ */
#detail {
  position: fixed;
  inset: 0;
  z-index: 100;            /* above all controls and the camera */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;          /* tighter so the panel can grow taller */
}
#detailBackdrop {
  position: absolute;
  inset: 0;
  background: rgba(2, 5, 12, 0.72);
  backdrop-filter: blur(2px);
  animation: fadeIn .25s ease;
}
#detailPanel {
  position: relative;
  display: flex;
  flex-direction: column;     /* title | body (flex) | page-nav */
  width: min(96vw, 1440px);   /* 50% wider than the previous 960px cap */
  max-height: 96vh;           /* taller box -> more fits per page */
  overflow: hidden;           /* the body scrolls/paginates, not the panel */
  background: linear-gradient(180deg, var(--panel-top), var(--bg-1));
  border: 1px solid var(--card-border);
  border-radius: 16px;
  box-shadow: 0 20px 70px rgba(0, 0, 0, 0.65), 0 0 0 1px rgba(255,255,255,0.03);
  padding: 18px 38px 16px;    /* slimmer top/bottom chrome */
  animation: panelIn .26s cubic-bezier(.2,.7,.3,1);
}
#detailClose {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 32px; height: 32px;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px;
  color: var(--text-dim);
  background: var(--soft-fill);
  border: 1px solid var(--card-border);
  border-radius: 50%;
  cursor: pointer;
  transition: color .2s ease, background .2s ease;
}
#detailClose:hover { color: var(--text); background: var(--soft-fill-2); }
#detailTitle {
  flex: 0 0 auto;
  /* padding-right (not margin) clears the ✕ button so the divider below still
     spans the full width — mirroring the Prev/Next bar's top border. */
  margin: 0 0 12px;
  padding: 0 44px 9px 0;
  border-bottom: 1px solid var(--card-border);
  font-size: var(--detail-title);
  line-height: 1.2;
  letter-spacing: .01em;
  color: var(--ink);
}

/* Rendered markdown body — flexes to fill the panel; one page shows at a time.
   Everything below sizes in `em`, so it all scales from --detail-font. */
#detailBody {
  flex: 1 1 auto;
  min-height: 0;              /* allow it to shrink inside the flex column */
  overflow-y: auto;          /* fallback scroll if a single block is huge */
  font-size: var(--detail-font);
  line-height: 1.65;
  color: var(--text);
}
#detailBody h1, #detailBody h2, #detailBody h3,
#detailBody h4, #detailBody h5, #detailBody h6 {
  margin: 1.2em 0 .5em;
  line-height: 1.3;
  color: var(--ink);
}
#detailBody h1 { font-size: 1.45em; }
#detailBody h2 { font-size: 1.25em; }
#detailBody h3 { font-size: 1.1em; }
#detailBody p { margin: 0 0 .85em; }
#detailBody ul, #detailBody ol { margin: 0 0 .85em; padding-left: 1.4em; }
#detailBody li { margin: .25em 0; }
#detailBody a { color: var(--accent-soft); }
#detailBody strong { color: var(--ink); }
#detailBody code {
  font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
  font-size: .9em;
  padding: 1px 5px;
  border-radius: 5px;
  background: var(--soft-fill);
}
#detailBody pre {
  margin: 0 0 .85em;
  padding: 12px 14px;
  border-radius: 10px;
  background: rgba(0,0,0,0.35);
  border: 1px solid var(--card-border);
  overflow-x: auto;
}
#detailBody pre code { padding: 0; background: none; }
#detailBody blockquote {
  margin: 0 0 .85em;
  padding: 2px 14px;
  border-left: 3px solid var(--accent);
  color: var(--text-dim);
}
#detailBody hr {
  border: none;
  border-top: 1px solid var(--card-border);
  margin: 1.2em 0;
}

/* Page navigation (only shown when the content spans more than one page) */
#detailNav {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 8px;            /* shorter footer */
  padding-top: 9px;
  border-top: 1px solid var(--card-border);
}
#detailNav button {
  font-family: var(--font);
  font-size: 14px;
  color: var(--text);
  background: rgba(var(--accent-rgb), 0.14);
  border: 1px solid var(--card-border);
  border-radius: 999px;
  padding: 6px 18px;
  cursor: pointer;
  transition: background .2s ease, border-color .2s ease;
}
#detailNav button:hover:not(:disabled) {
  background: rgba(var(--accent-rgb), 0.28);
  border-color: var(--accent-soft);
}
#detailNav button:disabled { opacity: .3; cursor: default; }
#detailPage { font-size: 14px; letter-spacing: .12em; color: var(--text-dim); }

@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes panelIn {
  from { opacity: 0; transform: translateY(14px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

/* Reduced motion: every animation plays once, instantly, and transitions snap.
   0.001ms (not 0) so transitionend still fires — Leaflet's zoom listens for it;
   no site script waits on transitionend/animationend. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================================
   LOW-POWER MODE  (body.low-power — set by core/app.js for weak hardware, or via the
   gallery "Lite mode" toggle). The two worst drains on old integrated GPUs are
   live backdrop-blur (re-blurs everything behind the element on every frame the
   content moves) and infinite ambient animation (keeps the compositor running
   nonstop). Drop both here; the engines separately collapse their GSAP motion
   to instant by folding window.HL_LOWPOWER into their reduced-motion guard.
   ============================================================================ */
body.low-power *,
body.low-power *::before,
body.low-power *::after {
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}
body.low-power .event.focused .marker-ping,
body.low-power .map-highlight-ring {
  animation: none !important;
}
/* Flatten expensive decorative paint: wide multi-layer blur shadows (on the
   focused card, gallery cards, tiles, choice boxes…) repaint as those elements
   move/hover, and big title glows are pure paint cost. Drop box-shadows wholesale
   but restore the focused-card accent ring — it's a cheap 1px and the key focus
   cue. Text-shadows are kept (they aid legibility over imagery); only the purely
   decorative landing glow is dropped. */
body.low-power * { box-shadow: none !important; }
body.low-power .event.focused .card { box-shadow: 0 0 0 1px var(--accent) !important; }
body.low-power .landing-title { text-shadow: none !important; }

/* ============================================================================
   MAP ENGINE  (engines/map.js)  —  shown in body.map-mode. Uses the same CSS-variable
   palette as the timeline, so every theme recolours it for free — including the
   home-made base map, which engines/map.js draws from the --map-* variables.
   ============================================================================ */
#mapRoot { position: fixed; inset: 0; }

/* The Leaflet surface fills the screen and owns its own stacking context
   (z-index:0 on a positioned element) so its internal panes/controls stay
   beneath the fixed UI layer (HUD, controls, panel) which sit at z-index >= 10. */
#map {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--map-sea);   /* the sea: the base map draws only land/lakes/rivers */
}
.leaflet-container {
  background: var(--map-sea);
  font-family: var(--font);
}
/* No ring for a mouse click on the map, a visible one for keyboard focus. */
.leaflet-container:focus:not(:focus-visible) { outline: none; }
.leaflet-container:focus-visible { outline: 2px solid var(--accent-soft); outline-offset: -3px; }
/* Attribution + any Leaflet controls, themed to blend in. */
.leaflet-control-attribution {
  background: var(--ctrl-bg) !important;
  color: var(--text-dim) !important;
  border-radius: 8px 0 0 0;
  padding: 2px 8px;
  font-size: 10px;
}
.leaflet-control-attribution a { color: var(--accent-soft) !important; }




@keyframes mapPing {
  0%   { transform: scale(.35); opacity: .9; }
  100% { transform: scale(1.05); opacity: 0; }
}

   /* drop the little arrow */































/* ---- Phones: one compact row of corner pills, the HUD below it ---------- */
/* At 390px the two-line title used to run under ⌂ Library / ▶ Play / Export PDF
   / ⚙ / ⤓. On phones every top control shrinks and sits in ONE row at the top
   (Export PDF and ⤓ go icon-only; both keep their aria-label), and the title
   block starts under that row with its own fluid size. Widths assumed:
   ⌂ Library ≈ 78px, Reset ≈ 60px, ▶ Play / ❚❚ Pause ≤ 88px, icon pills ≈ 42px. */
@media (max-width: 640px) {
  #playBtn, #resetBtn, #exportPdf, #fsBtn,
  #mapPlayBtn, #mapResetBtn, #mapSettingsBtn, #mapDownloadBtn {
    top: 12px; font-size: 13px; line-height: 1.2; padding: 6px 12px;
  }
  #resetBtn, #mapResetBtn { left: 96px; }
  #playBtn { right: 58px; }
  #fsBtn { right: 12px; font-size: 15px; padding: 6px 11px; }
  #exportPdf { right: 152px; font-size: 0; }
  #exportPdf::before { content: "⎙"; font-size: 15px; }

  #hud, #mapHud { top: 54px; padding: 0 12px; }
  #title, #mapTitle { font-size: clamp(18px, 5.2vw, 24px); line-height: 1.2; letter-spacing: .01em; }
  #era, #mapSubtitle { font-size: 11px; letter-spacing: .22em; }
  #legend, #mapLegend { margin-top: 10px; gap: 6px; }

  /* Focused: the dot grid sits ABOVE Previous / Next instead of between them. */
  #scrubber { bottom: 86px; left: 12px; right: 12px; transform: none; max-width: none; justify-content: center; padding: 6px 12px; gap: 10px; }
  #scrubDots { gap: 6px; }
  #zoomCtl { right: 12px; padding: 4px; gap: 4px; }
  .zoom-btn { width: 28px; height: 28px; }
  #zoomRange { height: 96px; }
  .hint-zoom { display: none; }   /* no Ctrl on a phone: pinch or the slider zooms */
  .nav { bottom: 28px; padding: 10px 16px; font-size: 14px; }
  .nav-prev { left: 12px; }
  .nav-next { right: 12px; }
}

/* ============================================================================
   THEMES  —  selected by `theme: "<name>"` in data.js.
   "midnight" is the default above; the ten names are:
     midnight · ember · forest · royal · crimson ·
     ocean · slate · sunset · parchment(light) · arctic(light)
   Each block only redefines palette variables; nothing else changes.
   ============================================================================ */

/* 2) EMBER — warm charcoal, amber glow */
[data-theme="ember"] {
  --bg-0: #0c0805; --bg-1: #171008; --bg-haze: #2a1c0c;
  --line: #5c4327; --line-glow: #ff9f1c; --glow-rgb: 255, 159, 28;
  --accent: #ff8c42; --accent-rgb: 255, 140, 66; --accent-deep: #e0621e;
  --accent-soft: #ffb27a; --on-accent: #2a1404;
  --text: #f6ece0; --text-dim: #c2a98f; --ink: #fff7ee;
  --card-bg: rgba(34, 22, 12, 0.92); --card-border: rgba(220, 150, 80, 0.32);
  --ctrl-bg: rgba(46, 30, 16, 0.82); --ctrl-bg-hover: rgba(82, 52, 24, 0.9);
  --panel-top: #1b1207;
  --map-land: #5e564d; --map-sea: #101415; --map-lake: #1c252a;
  --map-river: #31414b; --map-coast: #867e75;
  --map-relief-opacity: 0.80; --map-relief-blend: multiply;
  --map-label-water: #a5b7c9; --map-label-land: #cfc2af; --map-label-region: #eae0d4;
  --map-label-note: #d1bfab; --map-label-halo: #5e564d;
}

/* 3) FOREST — deep green, emerald */
[data-theme="forest"] {
  --bg-0: #040a07; --bg-1: #08140d; --bg-haze: #0f2418;
  --line: #2c5c41; --line-glow: #3ddc84; --glow-rgb: 61, 220, 132;
  --accent: #10b981; --accent-rgb: 16, 185, 129; --accent-deep: #0b9468;
  --accent-soft: #5eead4; --on-accent: #04140d;
  --text: #e6f5ec; --text-dim: #93b8a4; --ink: #f1fff8;
  --card-bg: rgba(12, 30, 21, 0.92); --card-border: rgba(80, 200, 140, 0.3);
  --ctrl-bg: rgba(16, 38, 27, 0.82); --ctrl-bg-hover: rgba(28, 64, 46, 0.9);
  --panel-top: #0a1b12;
  --map-land: #4f5c54; --map-sea: #0d1416; --map-lake: #19252b;
  --map-river: #2d424c; --map-coast: #78857d;
  --map-relief-opacity: 0.80; --map-relief-blend: multiply;
  --map-label-water: #a3b8c9; --map-label-land: #c9c5b2; --map-label-region: #dae9e0;
  --map-label-note: #aecabb; --map-label-halo: #4f5c54;
}

/* 4) ROYAL — indigo + violet axis, gold buttons */
[data-theme="royal"] {
  --bg-0: #0a0612; --bg-1: #150d24; --bg-haze: #241640;
  --line: #473a6b; --line-glow: #a78bfa; --glow-rgb: 167, 139, 250;
  --accent: #f4c95d; --accent-rgb: 244, 201, 93; --accent-deep: #b8860b;
  --accent-soft: #ffe6a8; --on-accent: #2a1c08;
  --text: #efe9fb; --text-dim: #a99bc9; --ink: #ffffff;
  --card-bg: rgba(28, 18, 46, 0.92); --card-border: rgba(167, 139, 250, 0.32);
  --ctrl-bg: rgba(34, 22, 56, 0.82); --ctrl-bg-hover: rgba(58, 40, 92, 0.9);
  --panel-top: #171029;
  --map-land: #5b5369; --map-sea: #10131a; --map-lake: #1c242e;
  --map-river: #304052; --map-coast: #847e8f;
  --map-relief-opacity: 0.80; --map-relief-blend: multiply;
  --map-label-water: #a4b7cb; --map-label-land: #cec2ba; --map-label-region: #e3ddef;
  --map-label-note: #bfb4d7; --map-label-halo: #5b5369;
}

/* 5) CRIMSON — dark maroon, rose-red */
[data-theme="crimson"] {
  --bg-0: #0d0405; --bg-1: #1a080b; --bg-haze: #2e0f14;
  --line: #5c2730; --line-glow: #ff5c7a; --glow-rgb: 255, 92, 122;
  --accent: #ff4d6d; --accent-rgb: 255, 77, 109; --accent-deep: #e8385a;
  --accent-soft: #ff8fa3; --on-accent: #2a0509;
  --text: #f7e6e9; --text-dim: #c79aa2; --ink: #fff2f4;
  --card-bg: rgba(40, 14, 18, 0.92); --card-border: rgba(220, 90, 110, 0.32);
  --ctrl-bg: rgba(52, 18, 24, 0.82); --ctrl-bg-hover: rgba(92, 32, 42, 0.9);
  --panel-top: #1d0a0d;
  --map-land: #614f52; --map-sea: #111215; --map-lake: #1d232a;
  --map-river: #323f4b; --map-coast: #89787b;
  --map-relief-opacity: 0.85; --map-relief-blend: multiply;
  --map-label-water: #a5b7c9; --map-label-land: #d0c0b2; --map-label-region: #ebdadd;
  --map-label-note: #d5b3b9; --map-label-halo: #614f52;
}

/* 6) OCEAN — teal/cyan deep water */
[data-theme="ocean"] {
  --bg-0: #03090d; --bg-1: #06141c; --bg-haze: #0c2630;
  --line: #235c66; --line-glow: #22d3ee; --glow-rgb: 34, 211, 238;
  --accent: #14b8a6; --accent-rgb: 20, 184, 166; --accent-deep: #0e8fa0;
  --accent-soft: #5eead4; --on-accent: #03161a;
  --text: #e2f4f7; --text-dim: #8fb4bd; --ink: #f1fdff;
  --card-bg: rgba(10, 30, 38, 0.92); --card-border: rgba(70, 190, 200, 0.3);
  --ctrl-bg: rgba(14, 38, 48, 0.82); --ctrl-bg-hover: rgba(24, 64, 78, 0.9);
  --panel-top: #081a22;
  --map-land: #4c5c62; --map-sea: #0c1418; --map-lake: #18252c;
  --map-river: #2c414f; --map-coast: #758489;
  --map-relief-opacity: 0.80; --map-relief-blend: multiply;
  --map-label-water: #a3b7ca; --map-label-land: #c8c5b7; --map-label-region: #d6e8eb;
  --map-label-note: #abc7ce; --map-label-halo: #4c5c62;
}

/* 7) SLATE — neutral monochrome */
[data-theme="slate"] {
  --bg-0: #0a0c10; --bg-1: #12151c; --bg-haze: #1d222c;
  --line: #3a4250; --line-glow: #8aa0c0; --glow-rgb: 138, 160, 192;
  --accent: #64748b; --accent-rgb: 100, 116, 139; --accent-deep: #334155;
  --accent-soft: #cbd5e1; --on-accent: #ffffff;
  --text: #e8ecf2; --text-dim: #9aa6b8; --ink: #ffffff;
  --card-bg: rgba(22, 26, 34, 0.92); --card-border: rgba(148, 163, 184, 0.3);
  --ctrl-bg: rgba(28, 33, 42, 0.82); --ctrl-bg-hover: rgba(48, 56, 70, 0.9);
  --panel-top: #161a22;
  --map-land: #565a60; --map-sea: #101419; --map-lake: #1c252d;
  --map-river: #304351; --map-coast: #808388;
  --map-relief-opacity: 0.78; --map-relief-blend: multiply;
  --map-label-water: #a4b8cb; --map-label-land: #ccc4b7; --map-label-region: #dce0e6;
  --map-label-note: #b3bcca; --map-label-halo: #565a60;
}

/* 8) SUNSET — plum sky, coral & pink */
[data-theme="sunset"] {
  --bg-0: #0d0710; --bg-1: #1a0c1a; --bg-haze: #301630;
  --line: #5c2f52; --line-glow: #ff7eb3; --glow-rgb: 255, 126, 179;
  --accent: #ff8e72; --accent-rgb: 255, 142, 114; --accent-deep: #e64a80;
  --accent-soft: #ffc1ad; --on-accent: #2a0c10;
  --text: #f9e8f0; --text-dim: #cfa3bd; --ink: #fff1f7;
  --card-bg: rgba(38, 16, 36, 0.92); --card-border: rgba(255, 140, 160, 0.3);
  --ctrl-bg: rgba(48, 22, 46, 0.82); --ctrl-bg-hover: rgba(82, 38, 76, 0.9);
  --panel-top: #1c0e1c;
  --map-land: #61525e; --map-sea: #111319; --map-lake: #1d242d;
  --map-river: #324051; --map-coast: #897a84;
  --map-relief-opacity: 0.85; --map-relief-blend: multiply;
  --map-label-water: #a5b7cb; --map-label-land: #d0c1b5; --map-label-region: #eddce4;
  --map-label-note: #dbbace; --map-label-halo: #61525e;
}

/* 9) PARCHMENT — LIGHT warm cream + sepia */
[data-theme="parchment"] {
  --bg-0: #e7dcc6; --bg-1: #f3ead8; --bg-haze: #fbf6ea;
  --line: #c9b896; --line-glow: #b07d3c; --glow-rgb: 176, 125, 60;
  --accent: #a4671f; --accent-rgb: 164, 103, 31; --accent-deep: #7c4a12;
  --accent-soft: #8a5a1c; --on-accent: #ffffff;
  --text: #3a2f23; --text-dim: #6f6151; --ink: #241c12;
  --card-bg: rgba(255, 251, 243, 0.96); --card-border: rgba(150, 120, 80, 0.4);
  --ctrl-bg: rgba(255, 250, 240, 0.88); --ctrl-bg-hover: rgba(235, 224, 203, 0.96);
  --soft-fill: rgba(0, 0, 0, 0.06); --soft-fill-2: rgba(0, 0, 0, 0.1);
  --hairline: rgba(0, 0, 0, 0.12); --panel-top: #fbf6ea;
  --map-land: #f3ead8; --map-sea: #aeb8b6; --map-lake: #a6b3b4;
  --map-river: #8aa2ac; --map-coast: #b1a899;
  --map-relief-opacity: 0.75; --map-relief-blend: multiply;
  --map-label-water: #2b475b; --map-label-land: #534533; --map-label-region: #4c4135;
  --map-label-note: #6f6151; --map-label-halo: #f3ead8;
  --good: #2f6f16; --good-rgb: 47, 111, 22; --good-alt: #16703c; --good-alt-rgb: 22, 112, 60;
  --bad: #b3261e; --bad-rgb: 179, 38, 30; --warn: #8a5a00; --warn-rgb: 138, 90, 0; --on-status: #ffffff;
}

/* 10) ARCTIC — LIGHT cool ice + blue */
[data-theme="arctic"] {
  --bg-0: #dbe6f0; --bg-1: #eaf1f8; --bg-haze: #f6fafe;
  --line: #a8c0d8; --line-glow: #2f6fb0; --glow-rgb: 47, 111, 176;
  --accent: #2f7fd0; --accent-rgb: 47, 127, 208; --accent-deep: #3584d4;
  --accent-soft: #1f6098; --on-accent: #020a14;
  --text: #1c2a38; --text-dim: #566576; --ink: #0f1c29;
  --card-bg: rgba(255, 255, 255, 0.96); --card-border: rgba(90, 130, 180, 0.4);
  --ctrl-bg: rgba(255, 255, 255, 0.88); --ctrl-bg-hover: rgba(224, 234, 247, 0.96);
  --soft-fill: rgba(0, 0, 0, 0.05); --soft-fill-2: rgba(0, 0, 0, 0.09);
  --hairline: rgba(0, 0, 0, 0.1); --panel-top: #f6fafe;
  --map-land: #eaf1f8; --map-sea: #a7bece; --map-lake: #a0b8c9;
  --map-river: #87a5b9; --map-coast: #a4adb6;
  --map-relief-opacity: 0.65; --map-relief-blend: multiply;
  --map-label-water: #2a485f; --map-label-land: #4f463c; --map-label-region: #303e4b;
  --map-label-note: #566576; --map-label-halo: #eaf1f8;
  --good: #2f6f16; --good-rgb: 47, 111, 22; --good-alt: #16703c; --good-alt-rgb: 22, 112, 60;
  --bad: #b3261e; --bad-rgb: 179, 38, 30; --warn: #8a5a00; --warn-rgb: 138, 90, 0; --on-status: #ffffff;
}

/* ============================================================================
   LIGHT AND DARK  —  every palette in both modes.
   <html data-mode="light|dark"> is the reader's choice, the same one the site's
   light/dark button makes (tools/lamp-shell/head.html sets it before paint:
   the saved choice, else the device's). A chart's `theme` picks the palette;
   the mode picks which version of it. The blocks above are each palette's own
   version (eight dark, parchment and arctic light); these are the others.
   Engine rules for light pages key on [data-mode="light"], never on a palette.
   ============================================================================ */

/* What every light version shares with parchment and arctic: faint fills and
   hairlines in black, the deeper status colours, and the base map's water. */
:root[data-mode="light"]:not([data-theme="parchment"], [data-theme="arctic"]) {
  --soft-fill: rgba(0, 0, 0, 0.06); --soft-fill-2: rgba(0, 0, 0, 0.1);
  --hairline: rgba(0, 0, 0, 0.12);
  --map-sea: #abbac0; --map-lake: #a3b5bd; --map-river: #88a3b2; --map-coast: #aba9a6;
  --map-relief-opacity: 0.7; --map-relief-blend: multiply;
  --map-label-water: #2a475d; --map-label-land: #51473a; --map-label-region: #3e3f44;
  --good: #2f6f16; --good-rgb: 47, 111, 22; --good-alt: #16703c; --good-alt-rgb: 22, 112, 60;
  --bad: #b3261e; --bad-rgb: 179, 38, 30; --warn: #8a5a00; --warn-rgb: 138, 90, 0; --on-status: #ffffff;
}

/* MIDNIGHT, light — pale blue-grey paper, cobalt */
:root[data-mode="light"]:not([data-theme]),
:root[data-mode="light"][data-theme=""],
:root[data-mode="light"][data-theme="midnight"] {
  --bg-0: #dfe5f2; --bg-1: #edf1f9; --bg-haze: #f8faff;
  --line: #b3c0dc; --line-glow: #3d63c9; --glow-rgb: 61, 99, 201;
  --accent: #3b6fd8; --accent-rgb: 59, 111, 216; --accent-deep: #2c58b8;
  --accent-soft: #2a52a8; --on-accent: #ffffff;
  --text: #1a2238; --text-dim: #4f5b78; --ink: #0e1528;
  --card-bg: rgba(248, 250, 255, 0.96); --card-border: rgba(61, 99, 201, 0.38);
  --ctrl-bg: rgba(248, 250, 255, 0.88); --ctrl-bg-hover: rgba(223, 229, 242, 0.96);
  --panel-top: #f8faff;
  --map-land: #edf1f9; --map-label-halo: #edf1f9; --map-label-note: #4f5b78;
}

/* EMBER, light — warm peach paper, burnt orange */
:root[data-mode="light"][data-theme="ember"] {
  --bg-0: #efdfd0; --bg-1: #f8eee4; --bg-haze: #fdf7f0;
  --line: #d9bea4; --line-glow: #c2561a; --glow-rgb: 194, 86, 26;
  --accent: #c4540f; --accent-rgb: 196, 84, 15; --accent-deep: #9c3f08;
  --accent-soft: #963c08; --on-accent: #ffffff;
  --text: #33231a; --text-dim: #6e5646; --ink: #20140c;
  --card-bg: rgba(253, 247, 240, 0.96); --card-border: rgba(194, 86, 26, 0.38);
  --ctrl-bg: rgba(253, 247, 240, 0.88); --ctrl-bg-hover: rgba(239, 223, 208, 0.96);
  --panel-top: #fdf7f0;
  --map-land: #f8eee4; --map-label-halo: #f8eee4; --map-label-note: #6e5646;
}

/* FOREST, light — pale sage, deep green */
:root[data-mode="light"][data-theme="forest"] {
  --bg-0: #d9e7de; --bg-1: #eaf3ed; --bg-haze: #f6fbf8;
  --line: #a9c7b5; --line-glow: #178a58; --glow-rgb: 23, 138, 88;
  --accent: #13845a; --accent-rgb: 19, 132, 90; --accent-deep: #0d6845;
  --accent-soft: #0d6542; --on-accent: #ffffff;
  --text: #16271d; --text-dim: #4c6457; --ink: #0b1a12;
  --card-bg: rgba(246, 251, 248, 0.96); --card-border: rgba(23, 138, 88, 0.38);
  --ctrl-bg: rgba(246, 251, 248, 0.88); --ctrl-bg-hover: rgba(217, 231, 222, 0.96);
  --panel-top: #f6fbf8;
  --map-land: #eaf3ed; --map-label-halo: #eaf3ed; --map-label-note: #4c6457;
}

/* ROYAL, light — lilac paper, violet line, gold buttons */
:root[data-mode="light"][data-theme="royal"] {
  --bg-0: #e2dcef; --bg-1: #efeaf8; --bg-haze: #f9f6fd;
  --line: #bfb2dc; --line-glow: #6d4fc4; --glow-rgb: 109, 79, 196;
  --accent: #c9971c; --accent-rgb: 201, 151, 28; --accent-deep: #a87b10;
  --accent-soft: #5e3fb0; --on-accent: #1f1405;
  --text: #221a36; --text-dim: #5a5072; --ink: #140e24;
  --card-bg: rgba(249, 246, 253, 0.96); --card-border: rgba(109, 79, 196, 0.38);
  --ctrl-bg: rgba(249, 246, 253, 0.88); --ctrl-bg-hover: rgba(226, 220, 239, 0.96);
  --panel-top: #f9f6fd;
  --map-land: #efeaf8; --map-label-halo: #efeaf8; --map-label-note: #5a5072;
}

/* CRIMSON, light — pale rose, deep red */
:root[data-mode="light"][data-theme="crimson"] {
  --bg-0: #eedadd; --bg-1: #f8ebed; --bg-haze: #fdf6f7;
  --line: #dbb2b9; --line-glow: #c43a56; --glow-rgb: 196, 58, 86;
  --accent: #c8284a; --accent-rgb: 200, 40, 74; --accent-deep: #a51d3b;
  --accent-soft: #9e1c38; --on-accent: #ffffff;
  --text: #2e1a1e; --text-dim: #6b4c52; --ink: #1e0d11;
  --card-bg: rgba(253, 246, 247, 0.96); --card-border: rgba(196, 58, 86, 0.38);
  --ctrl-bg: rgba(253, 246, 247, 0.88); --ctrl-bg-hover: rgba(238, 218, 221, 0.96);
  --panel-top: #fdf6f7;
  --map-land: #f8ebed; --map-label-halo: #f8ebed; --map-label-note: #6b4c52;
}

/* OCEAN, light — pale aqua, deep teal */
:root[data-mode="light"][data-theme="ocean"] {
  --bg-0: #d6e9ea; --bg-1: #e8f4f4; --bg-haze: #f5fbfb;
  --line: #a3cbcd; --line-glow: #0f8f99; --glow-rgb: 15, 143, 153;
  --accent: #0d8378; --accent-rgb: 13, 131, 120; --accent-deep: #0a6a62;
  --accent-soft: #0a6660; --on-accent: #ffffff;
  --text: #142a2c; --text-dim: #48625f; --ink: #0a1c1e;
  --card-bg: rgba(245, 251, 251, 0.96); --card-border: rgba(15, 143, 153, 0.38);
  --ctrl-bg: rgba(245, 251, 251, 0.88); --ctrl-bg-hover: rgba(214, 233, 234, 0.96);
  --panel-top: #f5fbfb;
  --map-land: #e8f4f4; --map-label-halo: #e8f4f4; --map-label-note: #48625f;
}

/* SLATE, light — neutral grey paper */
:root[data-mode="light"][data-theme="slate"] {
  --bg-0: #dde1e7; --bg-1: #eceff3; --bg-haze: #f8f9fb;
  --line: #b5bfcc; --line-glow: #55698a; --glow-rgb: 85, 105, 138;
  --accent: #475569; --accent-rgb: 71, 85, 105; --accent-deep: #334155;
  --accent-soft: #3a4a60; --on-accent: #ffffff;
  --text: #1c232e; --text-dim: #525d6c; --ink: #0f141c;
  --card-bg: rgba(248, 249, 251, 0.96); --card-border: rgba(85, 105, 138, 0.38);
  --ctrl-bg: rgba(248, 249, 251, 0.88); --ctrl-bg-hover: rgba(221, 225, 231, 0.96);
  --panel-top: #f8f9fb;
  --map-land: #eceff3; --map-label-halo: #eceff3; --map-label-note: #525d6c;
}

/* SUNSET, light — blush paper, coral and plum */
:root[data-mode="light"][data-theme="sunset"] {
  --bg-0: #efdde6; --bg-1: #f8ecf2; --bg-haze: #fdf7fa;
  --line: #dcb5c8; --line-glow: #c8457e; --glow-rgb: 200, 69, 126;
  --accent: #d9603f; --accent-rgb: 217, 96, 63; --accent-deep: #d35a3a;
  --accent-soft: #a03a62; --on-accent: #2a0c10;
  --text: #2e1a24; --text-dim: #6c4e5e; --ink: #1f0d16;
  --card-bg: rgba(253, 247, 250, 0.96); --card-border: rgba(200, 69, 126, 0.38);
  --ctrl-bg: rgba(253, 247, 250, 0.88); --ctrl-bg-hover: rgba(239, 221, 230, 0.96);
  --panel-top: #fdf7fa;
  --map-land: #f8ecf2; --map-label-halo: #f8ecf2; --map-label-note: #6c4e5e;
}

/* What the dark versions of parchment and arctic put back: the dark faint
   fills, hairlines and status colours of :root. */
:root[data-mode="dark"]:is([data-theme="parchment"], [data-theme="arctic"]) {
  --soft-fill: rgba(255, 255, 255, 0.08); --soft-fill-2: rgba(255, 255, 255, 0.12);
  --hairline: rgba(255, 255, 255, 0.10);
  --map-relief-opacity: 0.80; --map-relief-blend: multiply;
  --good: #7ed957; --good-rgb: 126, 217, 87; --good-alt: #3ddc84; --good-alt-rgb: 61, 220, 132;
  --bad: #ff6b6b; --bad-rgb: 255, 107, 107; --warn: #f5b942; --warn-rgb: 245, 185, 66; --on-status: #0b0b14;
}

/* PARCHMENT, dark — lamplit sepia, tan gold */
:root[data-mode="dark"][data-theme="parchment"] {
  --bg-0: #0e0a06; --bg-1: #19130c; --bg-haze: #2a2115;
  --line: #5a4a33; --line-glow: #d1a15c; --glow-rgb: 209, 161, 92;
  --accent: #d9a55a; --accent-rgb: 217, 165, 90; --accent-deep: #b8843c;
  --accent-soft: #ebc78f; --on-accent: #241809;
  --text: #f2e9d9; --text-dim: #bcab90; --ink: #fff8ec;
  --card-bg: rgba(25, 19, 12, 0.92); --card-border: rgba(209, 161, 92, 0.32);
  --ctrl-bg: rgba(42, 33, 21, 0.82); --ctrl-bg-hover: rgba(90, 74, 51, 0.9);
  --panel-top: #19130c;
  --map-land: #5f574b; --map-sea: #101315; --map-lake: #1c2429;
  --map-river: #31404a; --map-coast: #877f73;
  --map-label-water: #a5b7c8; --map-label-land: #d0c3ad; --map-label-region: #ece2d2;
  --map-label-note: #cbbb9f; --map-label-halo: #5f574b;
}

/* ARCTIC, dark — polar night, ice blue */
:root[data-mode="dark"][data-theme="arctic"] {
  --bg-0: #050a10; --bg-1: #0b1622; --bg-haze: #132538;
  --line: #2d4a66; --line-glow: #6fb4f0; --glow-rgb: 111, 180, 240;
  --accent: #4fa3e8; --accent-rgb: 79, 163, 232; --accent-deep: #2f86d0;
  --accent-soft: #9ccbf4; --on-accent: #031220;
  --text: #e6f0f9; --text-dim: #8fa9c2; --ink: #ffffff;
  --card-bg: rgba(11, 22, 34, 0.92); --card-border: rgba(111, 180, 240, 0.32);
  --ctrl-bg: rgba(19, 37, 56, 0.82); --ctrl-bg-hover: rgba(45, 74, 102, 0.9);
  --panel-top: #0b1622;
  --map-land: #525a66; --map-sea: #0c1319; --map-lake: #18242e;
  --map-river: #2d4152; --map-coast: #7b828c;
  --map-label-water: #a4b8cc; --map-label-land: #c9c5bb; --map-label-region: #dbe6f1;
  --map-label-note: #a9bdd2; --map-label-halo: #525a66;
}

/* ============================================================================
   LANDING / GALLERY
   Shown when there is no ?chart= in the URL. core/app.js toggles body.chart-mode to
   swap between this and the timeline. Uses the default (midnight) palette.
   ============================================================================ */

/* The landing page scrolls; the timeline / map do not. */
body:not(.chart-mode):not(.map-mode) { overflow-y: auto; }

#timelineRoot { display: none; }
body.chart-mode #timelineRoot { display: block; }
body.chart-mode #landing { display: none; }

/* Map mode: reveal #mapRoot, hide the gallery (mirrors chart-mode). */
#mapRoot { display: none; }
body.map-mode #landing { display: none; }

/* ============================================================================
   CHARACTER STUDY  (#profileRoot, body.study-mode)
   A full-screen dashboard of panels for a Person / Event / Place / Concept.
   Built by engines/profile.js from the global PROFILE. Uses the shared theme palette.
   ============================================================================ */
#profileRoot { display: none; }
body.study-mode #landing { display: none; }



/* ---- The dashboard board ---------------------------------------------- */
/* A top region (Overview + Character/Themes) over a tabbed panel. Everything
   takes its natural height; #profileRoot scrolls the page as a whole. */
#studyBoard {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: clamp(12px, 1.4vw, 20px);
}
















.study-ref {
  font-size: 12.5px;
  letter-spacing: 0.01em;
  padding: 3px 9px;
  border-radius: 7px;
  border: 1px solid var(--hairline);
  background: var(--soft-fill);
  color: var(--text-dim);
  white-space: nowrap;
}















/* ---- Gallery badge tint ----------------------------------------------- */
.chart-type-badge.type-study { color: var(--accent-soft); }

/* ---- Theme switcher (🎨, fixed top-right) ----------------------------- */
.study-theme-btn {
  position: fixed;
  z-index: 21;
  top: 22px;
  right: 24px;
  font-size: 16px;
  line-height: 1;
  color: var(--text);
  background: var(--ctrl-bg);
  border: 1px solid var(--card-border);
  border-radius: 999px;
  padding: 9px 13px;
  cursor: pointer;
  backdrop-filter: blur(4px);
  transition: transform .15s ease, background .2s ease;
}
.study-theme-btn:hover { background: var(--ctrl-bg-hover); transform: translateY(-1px); }


/* Phones: the name starts under the corner pills instead of beside them. */
@media (max-width: 640px) {
  .study-theme-btn { top: 12px; right: 12px; font-size: 15px; padding: 6px 11px; }
}

#landing {
  position: relative;
  min-height: 100%;
  background:
    radial-gradient(1200px 800px at 50% -10%, var(--bg-haze) 0%, var(--bg-1) 45%, var(--bg-0) 100%);
}
/* Faint grain, so the page reads as a surface the cards rest on rather than as
   empty space. Two static repeating gradients — painted once, never animated,
   no blur — and switched off wholesale in low-power mode. */
#landing::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: .5;
  background:
    repeating-linear-gradient(115deg,
      rgba(255, 250, 238, 0.014) 0 2px, transparent 2px 5px),
    repeating-linear-gradient(20deg,
      rgba(255, 250, 238, 0.010) 0 3px, transparent 3px 7px);
}
.landing-inner {
  position: relative;
  max-width: 1240px;            /* wide enough for 4 cards/row; header stays centered */
  margin: 0 auto;
  padding: clamp(48px, 9vh, 120px) 24px 28px;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
.landing-head { text-align: center; margin-bottom: 28px; }
.landing-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(36px, 6.2vw, 62px);
  font-weight: 600;
  letter-spacing: 0.005em;
  color: var(--ink);
  text-shadow: 0 0 40px rgba(var(--accent-rgb), 0.35);
}
.landing-verse {
  margin: 16px auto 0;
  max-width: 560px;
  font-size: clamp(14px, 1.9vw, 16px);   /* the epigraph: quieter than the tagline */
  font-style: italic;
  line-height: 1.55;
  color: var(--text-dim);
}
.landing-verse cite {
  display: block;
  margin-top: 8px;
  font-style: normal;
  font-size: 0.72em;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.landing-tagline {
  margin: 18px auto 0;
  max-width: 560px;
  font-size: clamp(15px, 2.2vw, 19px);
  line-height: 1.6;
  color: var(--text);      /* says what the site is: the louder of the two */
}
/* The site's other two sections (sermons/, studies/) — quiet links under the tagline. */
.landing-sections {
  display: flex; justify-content: center; flex-wrap: wrap; gap: 4px 10px;
  margin: 14px auto 0;
  font-size: 15px;
}
.landing-sections a {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px;
  color: var(--text); text-decoration: none;
  background: var(--ctrl-bg); border: 1px solid var(--card-border); border-radius: 999px;
  transition: background .2s ease;
}
.landing-sections a:hover { background: var(--ctrl-bg-hover); }
.landing-notice {
  margin: 18px auto 0;
  max-width: 560px;
  padding: 12px 16px;
  border-radius: 12px;
  background: rgba(var(--accent-rgb), 0.12);
  border: 1px solid var(--card-border);
  color: var(--text);
  font-size: 15px;
}

/* ---- Search box ------------------------------------------------------- */
.search-wrap {
  position: relative;
  max-width: 560px;
  margin: 0 auto 16px;
}
.search-icon {
  position: absolute;
  left: 18px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 16px;
  opacity: 0.7;
  pointer-events: none;
}
.search-input {
  width: 100%;
  padding: 15px 18px 15px 48px;
  font-family: var(--font);
  font-size: 17px;
  color: var(--text);
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 999px;
  transition: border-color .2s ease, box-shadow .2s ease;
}
.search-input::placeholder { color: var(--text-dim); }
.search-input:focus {
  outline: 2px solid transparent;   /* the glow shows focus; forced-colours paints this */
  border-color: var(--accent-soft);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.18);
}

/* ---- Filter chips ----------------------------------------------------- */
/* One row of chips under the search box. Clicking one collapses the shelves
   into a single filtered grid; clicking it again restores them. At ~15 charts
   this is enough narrowing on its own — a dropdown or a facet panel would be
   more machinery than the collection needs. */
.chart-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  max-width: 720px;
  margin: 0 auto 44px;
}
.chip {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font: inherit;
  font-size: 13.5px;
  color: var(--text-dim);
  background: var(--ctrl-bg);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 7px 14px;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}
.chip:hover { color: var(--text); border-color: var(--card-border); }
.chip.on {
  color: var(--ink);
  background: rgba(var(--accent-rgb), 0.24);
  border-color: var(--accent);
}
.chip-count {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: inherit;
  opacity: .62;
}
.chip.on .chip-count { opacity: .85; }

/* ---- Shelves ---------------------------------------------------------- */
/* Rows are tag queries now, so a shelf holds a mix of chart types and can no
   longer be tinted as a single-colour panel — the colour belongs to the card.
   Separation comes from space and a hairline instead, which is also what gives
   the page the unhurried feel a tinted-panel stack never had. */
.chart-sections { display: flex; flex-direction: column; gap: 52px; }

.chart-section { min-width: 0; }

/* The recently-viewed shelf leads the page and is marked as a distinct band so
   it doesn't read as just another subject row. */
.chart-section--lead {
  padding: 4px 18px 6px;
  border-radius: 20px;
  background: rgba(255, 252, 244, 0.06);
  border: 1px solid var(--hairline);
}

.chart-section-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 12px;
  margin: 0 6px 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--hairline);
}
.chart-section--lead .chart-section-head { border-bottom: none; padding-bottom: 4px; }

.chart-section-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(20px, 2.6vw, 26px);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--ink);
}
/* A shelf title opens that shelf as its own page (?shelf=<id>). */
.chart-section-link {
  color: inherit;
  text-decoration: none;
  border-radius: 6px;
}
.chart-section-cue {
  display: inline-block;
  margin-left: 8px;
  color: var(--text-dim);
  transition: transform .2s ease, color .2s ease;
}
.chart-section-link:hover { text-decoration: underline; text-underline-offset: 5px; text-decoration-thickness: 1px; }
.chart-section-link:hover .chart-section-cue { color: var(--ink); transform: translateX(3px); }
.chart-section-link:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* The ?shelf= page: a way back above the search box. */
.shelf-back {
  display: table;
  margin: 0 auto 14px;
  font-size: 14px;
  color: var(--text-dim);
  text-decoration: none;
}
.shelf-back:hover { color: var(--text); text-decoration: underline; }
.shelf-page .chart-chips { display: none; }
.shelf-page .search-wrap { margin-bottom: 36px; }

.chart-section-count {
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  background: var(--soft-fill);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 2px 10px;
}
/* Shelf ‹ › paging buttons, pushed to the right end of the heading. */
.chart-row-pager {
  margin-left: auto;
  align-self: center;
  display: flex;
  gap: 8px;
}
.chart-row-pager[hidden] { display: none; }
.chart-row-btn {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  padding: 0 0 3px;
  font: 600 22px/1 var(--font);
  color: var(--ink);
  background: var(--soft-fill);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  cursor: pointer;
  transition: border-color .2s ease, background-color .2s ease, opacity .2s ease;
}
.chart-row-btn:hover:not(:disabled) { border-color: var(--card-border); }
.chart-row-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.chart-row-btn:disabled { opacity: .3; cursor: default; }

/* Touch screens: close / paging buttons get a 44px hit area without growing
   the drawn circle (an invisible ::after around it). */
@media (pointer: coarse) {
  .chart-row-btn { position: relative; }
  .chart-row-pager { gap: 10px; }
  #detailClose::after, #mapPanelClose::after, .chart-row-btn::after {
    content: ""; position: absolute; inset: -7px; border-radius: 50%;
  }
  .chart-row-btn::after { inset: -5px; }
}

.chart-section-note {
  flex: 1 1 100%;
  margin: 0;
  font-size: 13.5px;
  color: var(--text-dim);
}

/* The row: a flex strip that shows --cols cards across and scrolls the rest.
   Card width is derived from --cols so exactly that many fill the visible row;
   the breakpoints below shrink --cols as the viewport (or zoom) tightens. */
.chart-row {
  --cols: 4;
  --gap: 18px;
  list-style: none;
  margin: 0;
  /* vertical padding leaves room for the cards' hover-lift + shadow, which the
     horizontal scroll container would otherwise clip */
  padding: 6px 4px 16px;
  display: flex;
  gap: var(--gap);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  scroll-padding-left: 4px;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.chart-row > li {
  flex: 0 0 calc((100% - (var(--cols) - 1) * var(--gap)) / var(--cols));
  min-width: 0;
  display: flex;
  scroll-snap-align: start;
}
/* No visible scrollbar: the heading's ‹ › buttons are the control. The row
   still scrolls, so touch drag, touchpad swipes and shift+wheel keep working. */
.chart-row::-webkit-scrollbar { display: none; }

/* The tail of the library, and every search / filter result: a plain wrapping
   grid. Nothing is hidden off the right-hand edge, which is what you want when
   the set is either unsorted leftovers or an answer you went looking for. */
.chart-grid {
  --cols: 4;
  --gap: 18px;
  list-style: none;
  margin: 0;
  padding: 6px 4px 8px;
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  gap: var(--gap);
}
.chart-grid > li { display: flex; min-width: 0; }

/* Fewer cards across as space tightens (covers both narrow screens and zooming
   in, which reduces the effective CSS width). */
@media (max-width: 1180px) { .chart-row, .chart-grid { --cols: 3; } }
@media (max-width: 860px) { .chart-row, .chart-grid { --cols: 2; } }
@media (max-width: 560px) { .chart-row, .chart-grid { --cols: 1; } }

/* ---- Cards ------------------------------------------------------------ */
/* Each card is slightly inset and softly shadowed so it reads as an object
   resting on the page rather than a region drawn on it. */
.chart-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: 0 0 18px;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  background: var(--card-bg);
  border: 1px solid var(--hairline);
  border-radius: 16px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
  transition: transform .15s ease, border-color .2s ease, box-shadow .2s ease;
}
.chart-card:hover,
.chart-card:focus-visible {
  transform: translateY(-3px);
  border-color: rgba(var(--accent-rgb), 0.55);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.42), 0 0 22px rgba(var(--accent-rgb), 0.18);
}
.chart-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Per-type accent. Each engine's own stylesheet section sets --accent* on its
   .chart-card.type-<engine> rule; these three are the originals. */
.chart-card.type-timeline {
  --accent: #5b9bff; --accent-soft: #9bc1ff; --accent-rgb: 91, 155, 255;
}
.chart-card.type-map {
  --accent: #10b981; --accent-soft: #5fd9b4; --accent-rgb: 16, 185, 129;
}
.chart-card.type-study {
  --accent: #f4c95d; --accent-soft: #f7d784; --accent-rgb: 244, 201, 93;
}

/* ---- Type illustration ------------------------------------------------ */
/* A generic line drawing of the FORMAT, filling the top of the card. Not a
   screenshot: most of these charts span several screens, so a reduced capture
   would be an unreadable smudge that also has to be regenerated every time the
   chart's content changes. The drawing is true of every chart of its type. */
.chart-card-art {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 104px;
  color: var(--accent-soft);
  background:
    radial-gradient(120% 140% at 50% 0%, rgba(var(--accent-rgb), 0.20), transparent 70%),
    rgba(var(--accent-rgb), 0.06);
  border-bottom: 1px solid var(--hairline);
}
.chart-art { width: 96px; height: auto; opacity: .92; }

/* ---- Card text -------------------------------------------------------- */
.chart-card-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 18px 20px 8px;
}
.chart-card-title {
  flex: 1;
  margin: 0;
  font-family: var(--font-display);
  font-size: 18.5px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
}
.chart-card-blurb {
  margin: 0 20px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--text-dim);
  /* Clamped to three lines, and deliberately NOT flex:1 — letting the box grow
     to fill the card defeats -webkit-line-clamp, which then cuts the text at
     the box edge mid-word with no ellipsis. The tag block below carries the
     margin-top:auto that keeps every card's footer on the same baseline.
     The blurb stays on the card face rather than hiding behind hover: hover
     text is unreachable by keyboard and screen reader, invisible on touch, and
     can't be scanned down a column by eye. */
  flex: 0 0 auto;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Content tags only — the format is already stated by the badge. */
.chart-tags {
  list-style: none;
  margin: 14px 20px 0;
  margin-top: auto;      /* pins the card's footer to the bottom edge */
  padding-top: 16px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.chart-tag {
  font-size: 11.5px;
  line-height: 1;
  color: var(--text-dim);
  background: var(--soft-fill);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 5px 10px;
}
.chart-card-cue {
  margin: 16px 20px 0;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--accent-soft);
}
/* An untagged chart has no tag block, so the cue carries the bottom-pinning
   instead. The adjacent-sibling selector matches only when nothing sits
   between the blurb and the cue. */
.chart-card-blurb + .chart-card-cue {
  margin-top: auto;
  padding-top: 16px;
}
/* Small badge naming the chart's format, beside the title. */
.chart-type-badge {
  flex: 0 0 auto;
  margin-top: 3px;
  font-size: 10.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent-soft);
  background: rgba(var(--accent-rgb), 0.12);
  border: 1px solid rgba(var(--accent-rgb), 0.28);
  border-radius: 999px;
  padding: 3px 9px;
}
.chart-empty {
  margin: 40px 0 0;
  text-align: center;
  color: var(--text-dim);
  font-size: 16px;
}

/* Low-power: drop the card lift and the art panel's gradient. The global
   `body.low-power * { box-shadow: none }` rule already removes the shadows;
   these are the remaining per-card repaint costs on hover and scroll. */
body.low-power .chart-card { transition: border-color .2s ease; }
body.low-power .chart-card:hover,
body.low-power .chart-card:focus-visible { transform: none; }
body.low-power .chart-card-art { background: var(--soft-fill); }
body.low-power #landing::before { display: none; }

/* ---- ⌂ Library link — one pill for every engine (class="lib-link") ----- */
/* Top-left in every engine. An engine overrides only what its own layout
   genuinely needs: --lib-top / --lib-left / --lib-z, or its position. */
.lib-link {
  position: fixed;
  z-index: var(--lib-z, 20);
  top: var(--lib-top, 22px);
  left: var(--lib-left, 24px);
  font-family: var(--font);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
  color: var(--text);
  background: var(--ctrl-bg);
  border: 1px solid var(--card-border);
  border-radius: 999px;
  padding: 8px 16px;
  backdrop-filter: blur(4px);
  transition: transform .15s ease, background .2s ease, border-color .2s ease;
}
.lib-link:hover { color: var(--text); background: var(--ctrl-bg-hover); border-color: var(--accent-soft); transform: translateY(-1px); }
.lib-link:focus-visible { outline: 2px solid var(--accent-soft); outline-offset: 2px; }
/* The light/dark button beside it (core/app.js modeButton places it). */
.hl-mode-btn { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.hl-mode-btn[hidden] { display: none; }
.hl-mode-btn svg { width: 15px; height: 15px; flex: none; }
/* Engines whose header bar sits closer to the top edge. */
#questBackToGallery, #triviaBackToGallery, #caseBackToGallery, #winnowBackToGallery,
#constellationBackToGallery, #lexBack, #dlgBack { --lib-top: 14px; --lib-left: 18px; }
@media (max-width: 640px) {
  .lib-link { top: 12px; left: 12px; font-size: 13px; padding: 6px 12px; }
}

/* ---- Attribution credit (shared by both modes) ----------------------- */
.credit {
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim);
  text-align: center;
}
/* Landing: in flow, pinned to the bottom of the page. */
.landing-foot {
  margin-top: auto;
  padding-top: 48px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
/* "Lite mode" toggle — lighter visuals for older / slower computers. */
.lite-toggle {
  cursor: pointer;
  font: inherit;
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  background: var(--ctrl-bg);
  border: 1px solid var(--card-border);
  border-radius: 999px;
  padding: 7px 16px;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.lite-toggle:hover { color: var(--text); border-color: var(--accent-soft); }
.lite-toggle.on { color: var(--accent); border-color: var(--accent); }
/* Timeline: fixed at the very bottom, beneath every other UI layer, and
   never intercepting pointer events so it can't interfere with panning. */
#timelineCredit {
  position: fixed;
  z-index: 1;
  left: 0;
  right: 0;
  bottom: 10px;
  opacity: 0.7;
  pointer-events: none;
}

/* ============================================================================
   STORY  (#storyRoot, body.story-mode)
   A deck of "pages" flipped through (forward/back or autoplay). Each page is a
   CSS-grid composition of reusable BLOCKS, laid out on a fixed-ratio board that
   engines/story.js scales to fit (letterboxed). Built from the global STORY. Uses the
   shared theme palette; per-page `theme` swaps <html data-theme> live.
   ============================================================================ */
#storyRoot { display: none; }
body.story-mode #landing { display: none; }

/* The scaled board host. engines/story.js sets width/height (from STORY.aspect) and the
   translate+scale transform; transform-origin is the board centre. */
#storyStage {
  position: absolute;
  transform-origin: 50% 50%;
  border-radius: 14px;
  overflow: hidden;
  background: var(--bg-1);
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.5);
  /* Base type size in BOARD space. All block `em` sizes are relative to this,
     and the fit transform scales the whole board (text included) to the
     viewport — so text stays proportional to the page, never tied to 16px. */
  font-size: 24px;
  line-height: 1.3;
  color: var(--text);
}



/* ---- Blocks (the puzzle pieces) --------------------------------------- */
/* Each type's font-size multiplies by --sb-fs-scale (the block's `size`,
   default 1) so `size` SCALES the block's natural size rather than replacing
   it. min-height:0 lets blocks shrink inside grid cells without overflowing. */
.sb { min-width: 0; min-height: 0; --sb-fs-scale: 1; }


























/* The dark glow lifts a pale title off a dark board; under the dark title of a
   light page it is a grey smudge, so light mode drops it. */
:root[data-mode="light"] { --story-title-shadow: none; }















@keyframes story-rotate-turn {
  0%, 25% { transform: rotate(0deg); }
  50%, 75% { transform: rotate(90deg); }
  100% { transform: rotate(0deg); }
}

/* ---- Gallery badge tint + per-type shelf accent (violet) ------------- */
.chart-type-badge.type-story { color: var(--accent-soft); }
.chart-card.type-story {
  --accent: #a98bff; --accent-soft: #c4b1ff; --accent-rgb: 169, 139, 255;
}

/* ============================================================================
   QUEST  (#questRoot, body.quest-mode)
   An interactive decision GAME: a briefing (the reading + how-to-play), then
   alternating narration and choice scenes (pick the true event from 2–4 boxes;
   wrong picks show a custom error and must be retried), with a progress rail +
   live score/accuracy/streak meters and a scored, ranked end screen. Built from
   the global QUEST. Uses the shared theme palette; per-scene `theme` swaps
   <html data-theme> live.
   ============================================================================ */
#questRoot { display: none; }
body.quest-mode #landing { display: none; }

/* ---- Briefing overlay ------------------------------------------------- */
#questBriefing {
  position: absolute; inset: 0; z-index: 30;
  display: grid; place-items: center;
  padding: 64px 24px; overflow-y: auto;
}
.quest-briefing-card {
  width: min(720px, 100%);
  background: var(--card-bg, rgba(255, 255, 255, 0.04));
  border: 1px solid var(--card-border);
  border-radius: 18px;
  padding: clamp(24px, 4vw, 48px);
  text-align: center;
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.45);
}
.quest-focus-emblem {
  font-size: clamp(48px, 9vw, 88px); line-height: 1;
  margin-bottom: 8px;
}
.quest-focus-name {
  font-family: var(--font-display, inherit);
  font-size: clamp(28px, 5vw, 44px); margin: 4px 0 2px;
}
.quest-focus-epithet {
  color: var(--accent-soft); font-size: clamp(14px, 2.2vw, 18px);
  letter-spacing: 0.04em; margin: 0 0 14px;
}
.quest-focus-blurb { color: var(--text-dim); margin: 0 auto 18px; max-width: 56ch; }
.quest-reading, .quest-howto {
  text-align: left; margin: 18px auto 0; max-width: 56ch;
  background: rgba(var(--accent-rgb), 0.06);
  border: 1px solid var(--card-border); border-radius: 12px;
  padding: 14px 18px;
}
.quest-reading-title, .quest-howto-title {
  font-size: 13px; text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--accent); margin: 0 0 8px;
}
.quest-reading-list { margin: 0; padding-left: 18px; }
.quest-howto-body { color: var(--text-dim); font-size: 15px; }
.quest-howto-body p { margin: 6px 0; }
.quest-begin {
  margin-top: 24px;
  font: inherit; font-weight: 700; font-size: clamp(16px, 2.4vw, 19px);
  color: var(--on-accent);
  background: var(--accent); border: none; border-radius: 999px;
  padding: 14px 32px; cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.2s ease, filter 0.2s ease;
  box-shadow: 0 10px 30px rgba(var(--accent-rgb), 0.4);
}
.quest-begin:hover { transform: translateY(-2px); filter: brightness(1.06); }

/* ---- HUD (titles + live meters) -------------------------------------- */
#questHud {
  position: absolute; top: 0; left: 0; right: 0; z-index: 20;
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; padding: 16px 22px; pointer-events: none;
  /* Leave room on the left for the ⌂ Library pill (matches the story HUD). */
  padding-left: clamp(108px, 13vw, 150px);
}
.quest-hud-titles { min-width: 0; }
#questTitle { font-family: var(--font-display, inherit); font-size: clamp(16px, 2.6vw, 22px); font-weight: 700; }
#questSubtitle { color: var(--text-dim); font-size: 13px; letter-spacing: 0.04em; }
.quest-meters { display: flex; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }

/* ---- Progress rail --------------------------------------------------- */
#questRail {
  position: absolute; top: 72px; left: 18px; bottom: 84px; z-index: 15;
  width: 220px; overflow-y: auto;
  background: rgba(0, 0, 0, 0.18);
  border: 1px solid var(--card-border); border-radius: 14px;
  padding: 14px 12px;
}
.quest-rail-title {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--accent); margin: 0 0 10px;
}
.quest-rail-list { list-style: none; margin: 0; padding: 0; }



/* ---- Scene stage ----------------------------------------------------- */
#questStage {
  position: absolute; inset: 0; z-index: 10;
  /* Leave room for the rail on the left and the HUD/controls top & bottom. */
  padding: 84px 28px 92px 260px;
  display: grid;
}






.quest-options--n1 { grid-template-columns: 1fr; }
.quest-options--n2 { grid-template-columns: 1fr 1fr; }
.quest-options--n3 { grid-template-columns: repeat(3, 1fr); }
.quest-options--n4 { grid-template-columns: 1fr 1fr; }







/* ---- Autoplay dwell bar ---------------------------------------------- */
#questDwell { position: absolute; top: 0; left: 0; right: 0; height: 3px; z-index: 25; background: rgba(255, 255, 255, 0.06); }
#questDwellFill { display: block; height: 100%; width: 0%; background: var(--accent); }

/* ---- Controls -------------------------------------------------------- */
#questControls {
  /* Start past the progress rail so the bar centers under the scene content
     (the stage has the same 260px left padding), not under the whole screen. */
  position: absolute; left: 260px; right: 0; bottom: 0; z-index: 20;
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 14px; pointer-events: none;
}
#questControls > * { pointer-events: auto; }
.quest-nav, .quest-ctrl {
  font: inherit; color: var(--text);
  background: var(--ctrl-bg, rgba(255, 255, 255, 0.06));
  border: 1px solid var(--card-border); border-radius: 10px;
  width: 44px; height: 44px; cursor: pointer; font-size: 18px;
  transition: transform 0.15s ease, background 0.2s ease, opacity 0.2s ease;
}
.quest-nav:hover:not(:disabled), .quest-ctrl:hover:not(:disabled) { background: var(--ctrl-bg-hover); transform: translateY(-1px); }
.quest-nav:disabled { opacity: 0.35; cursor: default; }
#questProgress { margin-left: 6px; font-size: 12px; letter-spacing: 0.1em; color: var(--text-dim); font-variant-numeric: tabular-nums; }

/* Scrolls away with the quest page (absolute in #questRoot), above its layers. */
#questBackToGallery { position: absolute; --lib-z: 40; }
#questCredit { position: absolute; bottom: 16px; right: 18px; z-index: 16; }
@media (max-width: 640px) { #questCredit { display: none; } }   

/* ---- Responsive: rail to the top, single-column options -------------- */
@media (max-width: 1000px) {
  #questRail {
    position: static; width: auto; height: auto; bottom: auto;
    margin: 64px 14px 0; max-height: 110px;
  }
  .quest-rail-list { display: flex; gap: 6px; overflow-x: auto; }
  .quest-rail-title { display: none; }
  #questStage { padding: 184px 16px 92px; }
  .quest-options--n2, .quest-options--n3, .quest-options--n4 { grid-template-columns: 1fr; }
  #questHud { flex-direction: column; align-items: stretch; padding: 44px 16px 8px; }
  .quest-meters { justify-content: flex-start; }
  /* Rail is at the top on mobile — center the controls on the full width again. */
  #questControls { left: 0; }
}

/* ---- Gallery badge tint + per-type shelf accent (amber) -------------- */
.chart-type-badge.type-quest { color: #f5b942; }
.chart-card.type-quest {
  --accent: #f5b942; --accent-soft: #ffd479; --accent-rgb: 245, 185, 66;
}

/* ============================================================================
   TRIVIA  (#triviaRoot, body.trivia-mode)
   A quiz BOARD: a grid of categories (columns) × point-value tiles (rows).
   Click a tile to open its clue; reveal the answer (host) or tap an option
   (self-play). No score is kept. Built from the global TRIVIA. Uses the shared
   theme palette.
   ============================================================================ */
#triviaRoot { display: none; }
body.trivia-mode #landing { display: none; }
























/* ---- Gallery badge tint + per-type shelf accent (teal) -------------- */
.chart-type-badge.type-trivia { color: #2dd4bf; }
.chart-card.type-trivia {
  --accent: #2dd4bf; --accent-soft: #7ff0e0; --accent-rgb: 45, 212, 191;
}

/* ============================================================================
   CASE  (#caseRoot, body.case-mode)
   A step-by-step ARGUMENT: a vertical "foundation stack" of premises layering
   toward a conclusion. Finished steps collapse to one-line bricks (click to
   review); the active step is the expanded card; a Bedrock rail shows its
   Scripture; a "Test structure" toggle dims a premise's dependents. Built from
   the global CASE. Uses the shared theme palette.
   ============================================================================ */
#caseRoot { display: none; }
body.case-mode #landing { display: none; }





























/* ---- Gallery badge tint + per-type shelf accent (indigo) ------------ */
.chart-type-badge.type-case { color: #8b9cff; }
.chart-card.type-case {
  --accent: #8b9cff; --accent-soft: #b6c2ff; --accent-rgb: 139, 156, 255;
}

/* ============================================================================
   WINNOW  (#winnowRoot, body.winnow-mode)
   A threshing floor for competing beliefs. Rival views sit in a left rail; the
   collected witness of Scripture sits on the board as clusters of passage
   boxes. Selecting a view repaints every box with that view's verdict —
   upheld / strains / contradicts / silent. A compare grid shows every verdict at
   once. Built from the global WINNOW. Uses the shared theme palette plus four
   fixed verdict hues (below) that must stay stable across all ten themes so a
   colour always means the same thing.
   ============================================================================ */
#winnowRoot { display: none; }
body.winnow-mode #landing { display: none; }

.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;
}

























































/* ---- Gallery badge tint + per-type shelf accent (amber) ------------- */
.chart-type-badge.type-winnow { color: #e8b64c; }
.chart-card.type-winnow {
  --accent: #e8b64c; --accent-soft: #f5d38a; --accent-rgb: 232, 182, 76;
}

/* ============================================================================
   CONSTELLATION  (#constellationRoot, body.constellation-mode)
   A subject sits in the core; passages float around it as a force-settled web
   of nodes joined by author-drawn links. Stepping the core repaints every node
   with how that passage bears on that subject — connects / strains /
   contradicts / silent — and every link renders at the strength of its WEAKER
   endpoint, so a chain that survives a subject stays lit end to end and one
   that doesn't visibly snaps. Built from the global CONSTELLATION.

   The four reading hues are the SAME four Winnow uses (only `upheld` is renamed
   to `connects`, because the question is different) and are theme-independent
   on purpose: a colour must mean the same thing in all ten themes.
   ============================================================================ */
#constellationRoot { display: none; }
body.constellation-mode #landing { display: none; }



















































/* ---- Gallery badge tint + per-type shelf accent (violet) ------------- */
.chart-type-badge.type-constellation { color: #a78bfa; }
.chart-card.type-constellation {
  --accent: #a78bfa; --accent-soft: #c9b6ff; --accent-rgb: 167, 139, 250;
}

/* ============================================================================
   LINEAGE  (#lineageRoot, body.lineage-mode)
   A reference line runs along the bottom; every body is a horizontal lane
   above it, and how high the lane sits is how far that body stands from the
   pattern the line represents. Each dated mark on a lane is an addition that
   put it there. Built from the global LINEAGE.

   Everything inside #lineageWorld is authored in WORLD units and scales with
   the camera together — there is no counter-scaled chrome inside the world.
   That is why the weight of a line against a name holds at every zoom, and why
   collisions can be solved once at build time instead of on every frame.

   Two hues carry the argument and are theme-independent on purpose: a step
   AWAY from the pattern and a step BACK toward it must mean the same thing in
   all ten themes. The reference line itself takes the theme's accent.
   ============================================================================ */
#lineageRoot { display: none; }
body.lineage-mode #landing { display: none; }


#lineageWorld { position: absolute; left: 0; top: 0; transform-origin: 0 0; }





































































/* ---- Gallery badge ---------------------------------------------------- */
.chart-type-badge.type-lineage { color: #6fd3c7; }
.chart-card.type-lineage {
  --accent: #6fd3c7; --accent-soft: #a6e8e0; --accent-rgb: 111, 211, 199;
}














/* ============================================================================
   GUESS  (#guessRoot, body.guess-mode)
   Guess that Verse: read a verse, name the book (then chapter / verse).
   Built from the global GUESS + bible/asv/. Uses the shared theme palette.
   Every button is one height (--guess-h); primary differs only in colour.
   Under 640px: icon-only Library pill, stacked HUD stats, one testament of
   the book picker at a time (.guess-tabs), full-width action buttons.
   ============================================================================ */
#guessRoot { display: none; --guess-h: 46px; }
body.guess-mode #landing { display: none; }


.guess-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin: 14px 0; }








.guess-chips li.is-good { background: rgba(var(--accent-rgb), .2); border-color: var(--accent); }
@keyframes guess-glow { from { background: rgba(var(--accent-rgb), .45); } to { background: rgba(var(--accent-rgb), .16); } }





/* ---- Phones ------------------------------------------------------------ */
@media (max-width: 640px) {
  #guessRoot { --guess-h: 50px; }
}

/* Biblical-script faces for the Lexicon engine (SIL OFL — fonts/OFL.txt).
   Self-hosted so word-study pages work offline like every chart but maps. */
@font-face { font-family: "Lex Greek"; src: url("../fonts/noto-serif-greek.woff2") format("woff2");
             font-display: swap; unicode-range: U+0370-03FF, U+1F00-1FFF, U+0300-036F; }
@font-face { font-family: "Lex Hebrew"; src: url("../fonts/noto-serif-hebrew.woff2") format("woff2");
             font-display: swap; unicode-range: U+0590-05FF, U+FB1D-FB4F; }
/* Reading faces for the same pages (SIL OFL): Literata for text, Cormorant
   Garamond for titles — a printed-lexicon voice rather than an encyclopedia's. */
@font-face { font-family: "Lex Text"; src: url("../fonts/literata.woff2") format("woff2");
             font-display: swap; font-weight: 400 700; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Lex Text"; src: url("../fonts/literata-ext.woff2") format("woff2");
             font-display: swap; font-weight: 400 700; unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "Lex Text"; src: url("../fonts/literata-italic.woff2") format("woff2");
             font-display: swap; font-weight: 400 700; font-style: italic; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Lex Text"; src: url("../fonts/literata-italic-ext.woff2") format("woff2");
             font-display: swap; font-weight: 400 700; font-style: italic; unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "Lex Display"; src: url("../fonts/cormorant-garamond-600.woff2") format("woff2");
             font-display: swap; font-weight: 600; }
@font-face { font-family: "Lex Display"; src: url("../fonts/cormorant-garamond-500-italic.woff2") format("woff2");
             font-display: swap; font-weight: 500; font-style: italic; }

/* ============================================================================
   LEXICON  (#lexiconRoot, body.lexicon-mode) — Word Studies & Biblical Sayings.
   Static reference page on parchment: header band · series strip · asymmetric
   two-column body · full-width Scripture cards. The per-kind accent marks ONLY
   the biblical-definition / what-it-meant block — never decoration.
   ============================================================================ */
#lexiconRoot { display: none; }
body.lexicon-mode #landing { display: none; }
#lexiconRoot.lex-word   { --lex-accent: #a4671f; --lex-accent-rgb: 164, 103, 31; }
#lexiconRoot.lex-saying { --lex-accent: #1f6b66; --lex-accent-rgb: 31, 107, 102; }




/* ============================================================================
   LEGAL PAGES — privacy.html, license.html, about.html.
   Plain static documents (no JS) in the landing page's default palette and
   type: display serif for headings, a ~70ch reading column, and the same
   "⌂ Library" pill and credit line. Everything is `legal-` prefixed.
   ============================================================================ */
/* The base rule locks html/body to the viewport for the chart engines; a
   document page scrolls normally instead. */
html.legal, html.legal body { height: auto; min-height: 100%; overflow: visible; }
body.legal-page {
  background:
    radial-gradient(1200px 800px at 50% -10%, var(--bg-haze) 0%, var(--bg-1) 45%, var(--bg-0) 100%)
    no-repeat var(--bg-0);
  line-height: 1.65;
  font-size: 17px;
}
.legal-inner {
  max-width: 70ch;
  margin: 0 auto;
  padding: 24px 20px 32px;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  overflow-wrap: break-word;
}
.legal-home {
  align-self: flex-start;
  font-size: 14px;
  text-decoration: none;
  color: var(--text);
  background: var(--ctrl-bg);
  border: 1px solid var(--card-border);
  border-radius: 999px;
  padding: 9px 18px;
  transition: background .2s ease;
}
.legal-home:hover { background: var(--ctrl-bg-hover); }
.legal-head { text-align: center; margin: clamp(28px, 6vh, 64px) 0 28px; }
.legal-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(34px, 6vw, 52px);
  font-weight: 600;
  color: var(--ink);
}
.legal-date {
  margin: 10px 0 0;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.legal-summary {
  margin: 0 0 12px;
  padding: 16px 20px;
  border-radius: 12px;
  background: rgba(var(--accent-rgb), 0.10);
  border: 1px solid var(--card-border);
  color: var(--text);
}
.legal-inner h2 {
  margin: 40px 0 10px;
  font-family: var(--font-display);
  font-size: 27px;
  font-weight: 600;
  color: var(--ink);
}
.legal-inner h3 {
  margin: 26px 0 6px;
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 600;
  color: var(--accent-soft);
}
.legal-inner p { margin: 0 0 14px; }
.legal-inner ul { margin: 0 0 14px; padding-left: 1.3em; }
.legal-inner li { margin-bottom: 6px; }
.legal-inner strong { color: var(--ink); }
.legal-inner a:not(.legal-home) { color: var(--accent-soft); text-underline-offset: 2px; }
.legal-inner a:not(.legal-home):hover { color: var(--ink); }
.legal-inner a:focus-visible { outline: 3px solid var(--accent-soft); outline-offset: 2px; }
.legal-inner code {
  font-family: Consolas, "SFMono-Regular", Menlo, monospace;
  font-size: 0.88em;
  padding: 1px 5px;
  border-radius: 5px;
  background: var(--soft-fill);
  color: var(--ink);
}
.legal-pre {
  margin: 4px 0 18px;
  padding: 16px 18px;
  border-radius: 12px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  font-family: Consolas, "SFMono-Regular", Menlo, monospace;
  font-size: 13px;
  line-height: 1.55;
  color: var(--text);
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
.legal-table {
  width: 100%;
  margin: 4px 0 18px;
  border-collapse: collapse;
  font-size: 15px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
}
.legal-table th, .legal-table td {
  padding: 9px 12px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--hairline);
}
.legal-table th {
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 600;
}
.legal-table tbody tr:last-child td { border-bottom: 0; }
.legal-table code { white-space: nowrap; }
/* Phones: each row becomes a small labelled block instead of three columns. */
@media (max-width: 560px) {
  .legal-table thead { display: none; }
  .legal-table tr { display: block; padding: 8px 0; border-bottom: 1px solid var(--hairline); }
  .legal-table tbody tr:last-child { border-bottom: 0; }
  .legal-table td { display: block; padding: 2px 12px; border: 0; }
  .legal-table td::before {
    content: attr(data-label) ": ";
    color: var(--text-dim);
    font-size: 13px;
  }
}
.legal-foot {
  margin-top: auto;
  padding-top: 56px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}
.legal-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 22px; font-size: 14px; }
.legal-inner .legal-links a { color: var(--text-dim); text-decoration: none; }
.legal-inner .legal-links a:hover { color: var(--text); text-decoration: underline; }
.legal-inner .legal-links a[aria-current="page"] { color: var(--text); }
/* Same nav, reused in the landing footer (index.html) next to the lite
   toggle and credit line — sized down to match that footer's small type. */
.landing-foot .legal-links { font-size: 12px; letter-spacing: 0.04em; gap: 6px 10px; color: var(--text-dim); }
.landing-foot .legal-links a { color: var(--text-dim); text-decoration: none; }
.landing-foot .legal-links a:hover { color: var(--text); text-decoration: underline; }
@media print {
  html.legal {
    --ink: #000; --text: #000; --text-dim: #444; --accent-soft: #000;
    --card-bg: #fff; --card-border: #999; --hairline: #ccc; --soft-fill: #eee;
  }
  html.legal, html.legal body { background: #fff; color: #000; }
  .legal-summary { background: none; }
  .legal-home, .legal-links { display: none; }
}

/* ==========================================================================
   SCENES — animated SVG teaching objects (engines/scene.js). Art uses these five
   classes only, so every theme recolours a scene for free.
   ========================================================================== */
.scene { display: block; width: 100%; height: 100%; overflow: visible; }
.scene .sc-line   { fill: none; stroke: var(--accent); stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; }
.scene .sc-ink    { fill: none; stroke: var(--text); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; opacity: 0.7; }
.scene .sc-soft   { fill: rgba(var(--accent-rgb), 0.14); stroke: none; }
.scene .sc-ground { fill: rgba(var(--accent-rgb), 0.06); stroke: var(--text-dim); stroke-width: 3; stroke-linejoin: round; }
/* Ground split in two, so a ground shape that runs to the edge of the art has
   no stroked sides or bottom framing the scene: `sc-ground sc-soft` is the
   ground's fill with no stroke; `sc-ground sc-ink` is the ground's edge line
   (an open path along the top of the land) with no fill. */
.scene .sc-ground.sc-soft { fill: rgba(var(--accent-rgb), 0.06); stroke: none; }
.scene .sc-ground.sc-ink  { fill: none; stroke: var(--text-dim); stroke-width: 3; stroke-linecap: round; opacity: 1; }

/* sc-fire / sc-fire-core: fire is warm in EVERY theme. Drawn with sc-line / sc-ink
   it took the theme's accent and text colours, so flames went blue in midnight
   (and grey in slate, rose in crimson). The colours are theme variables tuned
   for contrast with each board: bright orange outline and yellow inner tongue
   on the dark boards, a deeper red-orange and amber on the light parchment and
   arctic boards. `sc-fire` is the flame's outline (as sc-line: width 5);
   `sc-fire sc-soft` is its warm translucent fill (with sc-mask: opaque, the
   fire colour mixed into the board); `sc-fire-core` is the inner tongue
   (as sc-ink: width 3, but full strength).
   sc-shade: an opaque fill that is always DARKER than the board (night, the
   darkness that may be felt). sc-mask is the board colour, which on a light
   board is cream: a "darkness" drawn with it lightened the scene. Near-black on
   the dark boards, a deep sepia / slate on the light ones; dim it with the
   part's opacity as before. */
:root {
  --sc-fire: #ff8a2a; --sc-fire-rgb: 255, 138, 42; --sc-fire-core: #ffd35c;
  --sc-shade: #020308;
}
[data-theme="ember"]     { --sc-fire: #ff6f1f; --sc-fire-rgb: 255, 111, 31; --sc-fire-core: #ffdc70; }
[data-theme="crimson"]   { --sc-fire: #ff9433; --sc-fire-rgb: 255, 148, 51; --sc-fire-core: #ffdf73; }
[data-theme="sunset"]    { --sc-fire: #ffa035; --sc-fire-rgb: 255, 160, 53; --sc-fire-core: #ffe27a; }
[data-theme="royal"]     { --sc-fire: #ff8a2a; --sc-fire-rgb: 255, 138, 42; --sc-fire-core: #ffd866; }
/* Light mode, whatever the palette: the deeper flame of the light boards. */
:root[data-mode="light"] { --sc-fire: #c9460b; --sc-fire-rgb: 201, 70, 11; --sc-fire-core: #de8a00; --sc-shade: #2b2117; }
:root[data-mode="light"][data-theme="arctic"] { --sc-fire: #d0480f; --sc-fire-rgb: 208, 72, 15; --sc-fire-core: #df8900; --sc-shade: #131c27; }
.scene .sc-fire      { fill: none; stroke: var(--sc-fire); stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; }
.scene .sc-fire-core { fill: none; stroke: var(--sc-fire-core); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.scene .sc-soft.sc-fire { fill: rgba(var(--sc-fire-rgb), 0.22); stroke: none; }

.scene .sc-shade     { fill: var(--sc-shade); stroke: none; }
/* sc-feather: a ground/sea/hill shape that runs to the art's left or right edge
   fades out over the last --sc-feather of the art's width on each side, so the
   fill (and any stroke on it) melts into the board instead of ending in a cut
   side that reads as a panel. A CSS alpha mask, not a filter or url(#id): the
   gradient's box is the art (`mask-origin: view-box` = the viewBox's size, from
   user-space 0 0; engines/scene.js sets --sc-vbx to the viewBox's min-x so it starts at
   the art's left edge), repeated down so the shape's height never matters. Fills
   still come from the class colours, so themes recolour it. Outside the art the
   shape is masked away (no-repeat across). A browser without CSS masks on SVG
   elements just draws the plain cut edge. Combine with any fill class. An
   sc-mask shape still hides to the very edge: engines/scene.js turns this mask off on it
   and fills it with a gradient (board colour at the edges, its tint inside), so
   only its tint fades and it stays one opaque layer. */
.scene .sc-feather {
  --sc-fe: var(--sc-feather, 5%);
  -webkit-mask-image: linear-gradient(to right, transparent, rgba(0, 0, 0, 0.45) calc(var(--sc-fe) * 0.4), #000 var(--sc-fe), #000 calc(100% - var(--sc-fe)), rgba(0, 0, 0, 0.45) calc(100% - var(--sc-fe) * 0.4), transparent);
          mask-image: linear-gradient(to right, transparent, rgba(0, 0, 0, 0.45) calc(var(--sc-fe) * 0.4), #000 var(--sc-fe), #000 calc(100% - var(--sc-fe)), rgba(0, 0, 0, 0.45) calc(100% - var(--sc-fe) * 0.4), transparent);
  -webkit-mask-origin: view-box; mask-origin: view-box;
  -webkit-mask-repeat: repeat-y; mask-repeat: repeat-y;
  -webkit-mask-position: var(--sc-vbx, 0px) 0; mask-position: var(--sc-vbx, 0px) 0;
}




/* ============================================================================
   DIALOGUE  (#dialogueRoot, body.dialogue-mode)
   Two sides of a debated question challenge each other in a growing chat
   thread. Side colour comes from --side-left-rgb / --side-right-rgb (set by
   engines/dialogue.js from the data); .dlg-left / .dlg-right pick one as --side-rgb.
   Text always uses the theme's --text, so contrast never depends on the hue.
   ============================================================================ */
#dialogueRoot { display: none; }
body.dialogue-mode #landing { display: none; }














@keyframes dlg-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }













/* ---- Windows forced-colours (High Contrast) ----------------------------- */
/* Last in the file so it beats each engine's own `border: none`. Background-
   only dots vanish when colours are forced: outline them, keep the current one
   filled. (Focus rings: see the :where(...):focus-visible rule at the top.) */
@media (forced-colors: active) {
  .scrub-dot, .story-dot { border: 1px solid ButtonText; }
  .scrub-dot.on, .story-dot.is-active { forced-color-adjust: none; background: Highlight; }
}
/* ============================================================================
   BEREAN  (#bereanRoot, body.berean-mode) — Search the Scriptures.
   A single-column reading page (max ~860px): question · positions · sticky
   jump bar · The Scriptures · Where the teaching appears (left-ruled dated
   trail) · Our reading · For study. Follows the chart theme (default
   parchment). The theme accent marks ONLY the verdict band — never decoration.
   No animation, no backdrop-filter, so low-power mode needs nothing extra.
   ============================================================================ */
#bereanRoot { display: none; }
body.berean-mode #landing { display: none; }
#bereanRoot h2 { font-family: var(--font-display); font-size: 28px; margin: 34px 0 10px; color: var(--ink); }
#bereanRoot h3 { font-family: var(--font-display); font-size: 21px; margin: 24px 0 8px; color: var(--ink); }
/* 64px by default; engines/berean.js keeps --ber-jump-h equal to the jump bar's real
   height, which grows to two or three lines when it wraps on narrow screens. */
#bereanRoot h2, #bereanRoot h3 { scroll-margin-top: calc(var(--ber-jump-h, 50px) + 14px); }
#bereanRoot a { color: var(--ink); text-decoration: underline; text-underline-offset: 2px; }
/* :where() keeps this default at zero specificity so class rules (and the
   centred draft banner) still set their own margins. */
:where(#bereanRoot) p { margin: 0 0 10px; }


















/* ============================================================================
   HARD  (#hardRoot, body.hard-mode) — Hard Questions.
   A single-column reading page (max ~860px), Berean-family typography:
   question · short answer (the ONLY accent use) · the question at full
   strength · sticky jump bar · strands (details) · texts used wrongly ·
   history (details, dated trail) · what we're not told · answering it ·
   for study · related. Follows the chart theme (default parchment).
   No animation, no backdrop-filter, so low-power mode needs nothing extra.
   ============================================================================ */
#hardRoot { display: none; }
body.hard-mode #landing { display: none; }
#hardRoot h2 { font-family: var(--font-display); font-size: 28px; margin: 34px 0 10px; color: var(--ink); }
#hardRoot h3 { font-family: var(--font-display); font-size: 21px; margin: 24px 0 8px; color: var(--ink); }
#hardRoot a { color: var(--ink); text-decoration: underline; text-underline-offset: 2px; }
:where(#hardRoot) p { margin: 0 0 10px; }
/* engines/hard.js keeps --hq-jump-h equal to the jump bar's real height. */
#hardRoot h2, #hardRoot h3, #hqMain > section, .hq-strand, .hq-passage, .hq-misused, .hq-entry, .hq-differ {
  scroll-margin-top: calc(var(--hq-jump-h, 50px) + 14px); }


















/* ============================================================================
   NETWORK  (#networkRoot, body.network-mode) — the shared relationship graph.
   Focus at the centre, ring 1 in family arcs, faded ring 2. Family colours are
   tokens on #networkRoot; tone sets the line style (solid / thin / dashed).
   ============================================================================ */
#networkRoot { display: none; }
body.network-mode #landing { display: none; }




















/* ---- Gallery badge tint + per-type shelf accent (pink) ----------------- */
.chart-type-badge.type-network { color: #f472b6; }
.chart-card.type-network { --accent: #f472b6; --accent-soft: #f9a8d4; --accent-rgb: 244, 114, 182; }

/* ============================================================================
   IMMERSIVE MODE (HL.immersive in core/hl-common.js) — any engine
   body.hl-immersive while on (native fullscreen where allowed); body.hl-idle
   after 2.5 s without input. Each engine marks its floating chrome .hl-fades and
   re-fits its own view; these rules fade that chrome and the cursor, then hide
   it (visibility: out of the tab order, no stray clicks). Any input wakes it.
   ============================================================================ */
body.hl-immersive .hl-fades { transition: opacity 0.5s ease, visibility 0s linear 0s; }
body.hl-idle .hl-fades { opacity: 0 !important; visibility: hidden !important; transition: opacity 0.8s ease, visibility 0s linear 0.8s; }
body.hl-idle, body.hl-idle * { cursor: none !important; }
.hl-fs-btn[aria-pressed="true"] { background: var(--ctrl-bg-hover); border-color: var(--accent-soft); }

/* ============================================================================
   LIFESPANS, CALENDAR, CONTROVERSY, STEMMA, LENS, DENOMINATION — each engine's look lives in
   its own sheet (engines/lifespans.css, engines/calendar.css, engines/controversy.css, engines/stemma.css, engines/lens.css,
   engines/denomination.css), loaded with the engine. Only
   the hide/show switch is here, so a root never flashes on the gallery before
   its sheet arrives.
   ============================================================================ */
#lifespansRoot, #calendarRoot, #controversyRoot, #stemmaRoot, #lensRoot, #denominationRoot { display: none; }
body.lifespans-mode #landing, body.calendar-mode #landing, body.controversy-mode #landing,
body.stemma-mode #landing, body.lens-mode #landing, body.denomination-mode #landing { display: none; }
