/* ============================================================
   tokens
   ============================================================ */
:root {
  /* paper & ink */
  --paper:        #F2EBDD;
  --paper-warm:   #EBE3D0;            /* voice ground, one step warmer */
  --ink:          #1C1A17;
  --ink-secondary: rgba(28, 26, 23, 0.72);
  --ink-tertiary:  rgba(28, 26, 23, 0.55);
  --hairline:      rgba(28, 26, 23, 0.16);
  --highlight:     rgba(242, 230, 186, 0.55);
  /* page-background dot pattern colour — switches with theme */
  --bg-dot:        rgba(28, 26, 23, 0.018);
  /* warm-ochre accent (cross-topic + tertiary CTA) — same hue both
     themes, but stronger in dark to maintain legibility on dark
     ground. */
  --ochre-1:       rgba(160, 95, 40, 0.55);  /* main */
  --ochre-2:       rgba(160, 95, 40, 0.95);  /* hover / strong */

  /* type */
  --serif: 'PT Serif', 'Iowan Old Style', 'Charter', Georgia, serif;
  --mono:  'IBM Plex Mono', 'SF Mono', Menlo, Consolas, monospace;

  /* reading register */
  --reading-size:    18px;
  --reading-leading: 1.7;
  --reading-paragraph-gap: 1.4em;

  /* dense register */
  --dense-size:    13px;
  --dense-leading: 1.5;
  --dense-row-h:   38px;

  /* spacing scale — let sections breathe */
  --section-gap:   80px;
  --row-pad-y:     11px;

  /* heading scale — index-card register: position carries hierarchy more
     than size. h1 is responsive; on mobile it clamps down so titles
     never break one-word-per-line. */
  --h1: clamp(20px, 1.4vw + 14px, 24px);
  --h2: 20px;
  --h3: 18px;

  /* margin / column geometry */
  --col-rail:    12ch;
  --col-prose:   62ch;
  --col-margin:  24ch;
  --gap:         3ch;

  /* voice block */
  --voice-bar-w: 3px;
  --voice-pad:   12px;
}

/* Dark theme — same identity, different lighting. Late-night
   reading by candle: warm dark ground, slightly muted cream ink. */
[data-theme="dark"] {
  --paper:        #15130F;
  --paper-warm:   #1E1B16;
  --ink:          #E8DFC9;
  --ink-secondary: rgba(232, 223, 201, 0.78);
  --ink-tertiary:  rgba(232, 223, 201, 0.58);
  /* 0.18 cream on warm-black is barely there — hairlines hold the
     whole dense register (chips, table dividers, panel edges).
     Bumped so structure reads. */
  --hairline:      rgba(232, 223, 201, 0.26);
  --highlight:     rgba(160, 95, 40, 0.28);
  --bg-dot:        rgba(232, 223, 201, 0.025);
  /* Ochre needs a touch more pop on dark — same hue, lighter
     midpoint so it stays warm but reads cleanly against dark ground. */
  --ochre-1:       rgba(196, 130, 70, 0.65);
  --ochre-2:       rgba(220, 152, 90, 1);
}

/* SVG presentation attributes are baked into the build output as
   fill="#F2EBDD" or stroke="#F2EBDD". CSS overrides them so the
   theme swap also flips paper-backings on map nodes, inset glyph
   paper, and label haloes. CSS wins over presentation attributes. */
[data-theme="dark"] .map-node-bg,
[data-theme="dark"] .map-quest-mark-bg,
[data-theme="dark"] .concept-territory-inset svg circle[fill="#F2EBDD"],
[data-theme="dark"] .entity-territory-inset svg circle[fill="#F2EBDD"],
[data-theme="dark"] .source-territory-inset svg circle[fill="#F2EBDD"],
[data-theme="dark"] .quest-territory-inset svg circle[fill="#F2EBDD"],
[data-theme="dark"] .map-minimap-bg {
  fill: var(--paper);
}
[data-theme="dark"] .map-node-label,
[data-theme="dark"] .concept-territory-inset svg text[stroke="#F2EBDD"],
[data-theme="dark"] .entity-territory-inset svg text[stroke="#F2EBDD"],
[data-theme="dark"] .source-territory-inset svg text[stroke="#F2EBDD"],
[data-theme="dark"] .quest-territory-inset svg text[stroke="#F2EBDD"] {
  stroke: var(--paper);
  fill: var(--ink);
}
/* The Q letter inside quest-mark badges */
[data-theme="dark"] .map-quest-mark-q {
  fill: var(--ink-secondary);
}
/* Trail-overlay dots have white stroke for separation in light mode;
   in dark, switch to the paper colour. */
[data-theme="dark"] .map-trail-dot {
  stroke: var(--paper);
}
/* Cursor flashlight on territory + insets — keep warm, but slightly
   stronger on dark ground so the cursor presence is felt. */
[data-theme="dark"] .topic-map--territory .map-flashlight {
  background:
    radial-gradient(circle 220px at var(--cursor-x) var(--cursor-y),
      rgba(255, 220, 160, 0.10) 0%,
      rgba(255, 220, 160, 0.04) 50%,
      transparent 80%);
  mix-blend-mode: screen;
}
[data-theme="dark"] .inset-svg-wrap .inset-flashlight {
  background:
    radial-gradient(circle 160px at var(--cursor-x) var(--cursor-y),
      rgba(255, 220, 160, 0.10) 0%,
      rgba(255, 220, 160, 0.04) 50%,
      transparent 80%);
  mix-blend-mode: screen;
}
/* Edges in dark mode: rgba(28, 26, 23, X) is hardcoded in stroke
   colours and would render almost invisible on dark. Override via
   inversion: dark theme uses cream strokes at similar opacity. */
[data-theme="dark"] .map-edge:not(.map-edge--cross-topic) {
  stroke: rgba(232, 223, 201, 0.20);
}
[data-theme="dark"] .topic-map.map--exploring .map-edge:not(.map-edge--cross-topic) {
  stroke: rgba(232, 223, 201, 0.10);
}
[data-theme="dark"] .topic-map.map--exploring .map-edge--neighbor {
  stroke: rgba(232, 223, 201, 0.55) !important;
}
/* Concept inset / person inset / source / quest insets: edges
   use rgba(28, 26, 23, X) inline. Invert. */
[data-theme="dark"] .concept-territory-inset line[stroke^="rgba(28"],
[data-theme="dark"] .entity-territory-inset line[stroke^="rgba(28"],
[data-theme="dark"] .source-territory-inset line[stroke^="rgba(28"],
[data-theme="dark"] .quest-territory-inset line[stroke^="rgba(28"] {
  stroke: rgba(232, 223, 201, 0.25);
}
[data-theme="dark"] .concept-inset-nodes circle[fill^="rgba(28"],
[data-theme="dark"] .person-inset-nodes circle[fill^="rgba(28"],
[data-theme="dark"] .map-minimap-edges line[stroke^="rgba(28"] {
  fill: rgba(232, 223, 201, 0.25);
  stroke: rgba(232, 223, 201, 0.18);
}
/* Map region labels: italic serif title at low opacity. The fill
   on these uses the topic colour, which works on dark. The flanker
   lines are also topic-coloured. Fine as-is. */

/* Pathfind start/end node rings — keep ochre. Already CSS-driven. */

/* Dark-mode hover and active tints — the light-mode pattern uses
   rgba(28,26,23,X) (dark ink dimming cream paper). On dark ground
   those vanish into the page. Flip the register: cream tints
   lift the dark ground a hair, same delta in the opposite direction. */
[data-theme="dark"] .site-nav a:hover,
[data-theme="dark"] .search-trigger:hover,
[data-theme="dark"] .theme-toggle:hover {
  background: rgba(232, 223, 201, 0.06);
}
[data-theme="dark"] .site-nav a.site-nav-active {
  background: rgba(232, 223, 201, 0.08);
}
[data-theme="dark"] .dense-table tr:hover {
  background: rgba(232, 223, 201, 0.035);
}

/* Search modal + kbd-overlay backdrops: light-mode uses
   rgba(28,26,23,0.18) — a dark veil over cream that focuses the eye.
   On dark ground that reads as nothing. Use a deep black overlay
   so the modal lifts off. */
[data-theme="dark"] .search-modal,
[data-theme="dark"] #kbd-overlay {
  background: rgba(0, 0, 0, 0.55);
}

/* Algorithmic glyph SVGs (topic mastheads, per-concept marks,
   per-person marks, per-source plates, per-quest signatures, the
   quest-pitch flow watermark) all emit hardcoded
   stroke="rgba(28,26,23,X)" / fill="rgba(28,26,23,X)" — dark-on-dark
   in dark mode. Re-stroke to cream with similar base alpha; the
   per-element stroke-opacity attribute still multiplies. */
[data-theme="dark"] .topic-masthead-svg path,
[data-theme="dark"] .quest-pitch-watermark path,
[data-theme="dark"] .concept-glyph path,
[data-theme="dark"] .concept-glyph line,
[data-theme="dark"] .concept-glyph circle:not([fill="#F2EBDD"]),
[data-theme="dark"] .concept-glyph polyline,
[data-theme="dark"] .concept-constellation path,
[data-theme="dark"] .concept-constellation line,
[data-theme="dark"] .concept-constellation circle:not([fill="#F2EBDD"]),
[data-theme="dark"] .person-mark path,
[data-theme="dark"] .person-mark line,
[data-theme="dark"] .person-mark circle:not([fill="#F2EBDD"]),
[data-theme="dark"] .source-plate path,
[data-theme="dark"] .source-plate line,
[data-theme="dark"] .source-plate rect:not([fill="#F2EBDD"]),
[data-theme="dark"] .quest-signature path,
[data-theme="dark"] .quest-signature line,
[data-theme="dark"] .quest-signature circle:not([fill="#F2EBDD"]),
[data-theme="dark"] .note-mark path,
[data-theme="dark"] .note-mark line,
[data-theme="dark"] .ending-mark path,
[data-theme="dark"] .ending-mark line {
  stroke: rgba(232, 223, 201, 0.85);
}
[data-theme="dark"] .concept-glyph [fill^="rgba(28"],
[data-theme="dark"] .person-mark [fill^="rgba(28"],
[data-theme="dark"] .source-plate [fill^="rgba(28"],
[data-theme="dark"] .quest-signature [fill^="rgba(28"],
[data-theme="dark"] .note-mark [fill^="rgba(28"] {
  fill: rgba(232, 223, 201, 0.85);
}

/* Shadows: rgba(28,26,23,X) shadows are dark-on-dark in dark mode,
   so depth vanishes. Switch to true-black at higher opacity for the
   floating panels that need actual lift. Hairline borders carry
   most of the work otherwise. */
[data-theme="dark"] .search-modal-inner,
[data-theme="dark"] .xref-tip,
[data-theme="dark"] .territory-welcome,
[data-theme="dark"] .kbd-overlay-card {
  box-shadow: 0 24px 60px -16px rgba(0, 0, 0, 0.7);
  border: 1px solid var(--hairline);
}
[data-theme="dark"] .territory-drawer {
  box-shadow: -20px 0 40px -20px rgba(0, 0, 0, 0.7);
  border-left: 1px solid var(--hairline);
}

/* ============================================================
   base
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

body {
  background: var(--paper);
  background-image:
    radial-gradient(var(--bg-dot) 1px, transparent 1px);
  background-size: 3px 3px;
  color-scheme: light dark;
  transition: background-color 200ms ease, color 200ms ease;
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--reading-size);
  line-height: var(--reading-leading);
  text-rendering: geometricPrecision;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Sticky-bottom footer on short pages */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--ink-tertiary);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
a:hover {
  text-decoration-color: var(--ink);
}

::selection { background: var(--highlight); color: var(--ink); }

/* ============================================================
   site header
   ============================================================ */
.site-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 16px 24px 12px;
  border-bottom: 1px solid var(--hairline);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
@media (max-width: 720px) {
  .site-header { padding: 10px 14px 8px; align-items: center; }
}
.site-name {
  text-decoration: none;
  font-weight: 600;
  letter-spacing: 0.08em;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.site-mark {
  display: inline-block;
  vertical-align: middle;
}
.site-nav {
  display: flex;
  gap: 4px;
  align-items: center;
  flex-wrap: wrap;
}
.site-nav a, .search-trigger {
  text-decoration: none;
  color: var(--ink-secondary);
  font: inherit;
  background: none;
  border: 0;
  padding: 6px 9px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 2px;
  transition: color 140ms ease, background 140ms ease;
}
.site-nav a:hover, .search-trigger:hover {
  color: var(--ink);
  background: rgba(28, 26, 23, 0.045);
}
.site-nav a.site-nav-active {
  color: var(--ink);
  background: rgba(28, 26, 23, 0.06);
  position: relative;
}
.site-nav a.site-nav-active::after {
  content: '';
  position: absolute;
  left: 9px;
  right: 9px;
  bottom: 0;
  height: 1.5px;
  background: rgba(160, 95, 40, 0.85);
}
.site-nav a.site-nav-active .site-nav-glyph {
  color: var(--ink);
}
.site-nav-glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  color: var(--ink-tertiary);
  transition: color 140ms ease, transform 220ms ease;
  flex: none;
}
.site-nav-glyph svg { display: block; }
.site-nav a:hover .site-nav-glyph,
.search-trigger:hover .site-nav-glyph {
  color: var(--ink);
  transform: scale(1.08);
}
.site-nav-label {
  /* labels stay visible on desktop; collapse to glyph-only on narrow widths */
}
.search-trigger kbd {
  font: inherit;
  border: 1px solid var(--hairline);
  border-radius: 2px;
  padding: 1px 4px;
  margin-left: 4px;
  color: var(--ink-tertiary);
}

/* Theme toggle — sits at the end of the nav. Shows the moon when
   the current theme is light (click to go dark); shows the sun when
   the current theme is dark. The opposite-icon convention reads as
   'this is what you'd switch to.' */
.theme-toggle {
  background: none;
  border: 0;
  padding: 6px;
  cursor: pointer;
  color: var(--ink-secondary);
  border-radius: 2px;
  transition: color 140ms ease, background 140ms ease;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.theme-toggle:hover { color: var(--ink); background: rgba(28, 26, 23, 0.045); }
[data-theme="dark"] .theme-toggle:hover { background: rgba(232, 223, 201, 0.07); }
.theme-toggle-glyph {
  display: inline-flex;
  width: 16px;
  height: 16px;
  align-items: center;
  justify-content: center;
  transition: color 140ms ease, transform 220ms ease;
}
/* Light mode shows the moon (click → go dark); dark mode shows the
   sun (click → go light). Hide the inactive glyph. */
.theme-toggle-glyph--sun { display: none; }
[data-theme="dark"] .theme-toggle-glyph--moon { display: none; }
[data-theme="dark"] .theme-toggle-glyph--sun { display: inline-flex; }
.theme-toggle:hover .theme-toggle-glyph { transform: rotate(10deg); }

/* Medium: at 12-section width the toggle gets pushed onto a second
   line. Tighten link padding and drop the search kbd hint so
   everything stays on one row down to ~1100px. */
@media (max-width: 1280px) {
  .site-nav a, .search-trigger { padding: 6px 6px; }
  .site-nav { gap: 2px; }
  .search-trigger kbd { display: none; }
}

/* Narrow: collapse nav labels, keep glyphs so the nav is a visual
   row instead of overflowing text. Labels return as tooltips via
   the link's text content. */
@media (max-width: 820px) {
  .site-nav-label { display: none; }
  .site-nav a, .search-trigger { padding: 6px; }
}

/* ============================================================
   main container
   ============================================================ */
main {
  flex: 1 0 auto;            /* stretch to push footer to bottom */
  max-width: 1180px;
  margin: 0 auto;
  padding: 48px 32px 96px;
}
@media (max-width: 720px) {
  main { padding: 24px 16px 64px; }
}

/* ============================================================
   typographic markers — small hairline SVG glyph per entity kind
   ============================================================ */
/* The kind-mark sits at the gutter of every entity row, breadcrumb
   and ref chip. The glyph variant renders a 14×14 hairline SVG;
   the text variant (--text) keeps the original mono-uppercase
   label and is the fallback for kinds without a glyph (MAP, ABT).
   currentColor on the SVG paths lets each surface inherit its
   own ink shade (typically --ink-tertiary). */
.kind-mark {
  color: var(--ink-tertiary);
  margin-right: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.kind-mark--glyph {
  width: 12px;
  height: 12px;
  vertical-align: -2px;
}
.kind-mark--glyph svg {
  width: 100%;
  height: 100%;
  display: block;
}
/* Breadcrumb register: glyph reads at a slightly larger size and
   sits on the baseline with mono-uppercase neighbours. */
.kind-mark--breadcrumb.kind-mark--glyph {
  width: 14px;
  height: 14px;
  vertical-align: -3px;
  margin-right: 8px;
}
/* Fallback for kinds without a glyph (MAP, ABT): keep the original
   mono-uppercase label register so they still read as section IDs. */
.kind-mark--text {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  vertical-align: 0.10em;
}
.section-glyph {
  color: var(--ink-tertiary);
  margin-right: 0.5em;
  font-weight: 400;
}
.section-meta {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-left: 0.5em;
}

/* ============================================================
   article — three-zone reading grid
   ============================================================ */
.prose-grid {
  display: grid;
  grid-template-columns: var(--col-rail) var(--col-prose) var(--col-margin);
  column-gap: var(--gap);
  align-items: start;
  position: relative;
}
.prose-grid--solo {
  grid-template-columns: var(--col-rail) minmax(0, 72ch);
}

/* Pages that don't carry margin voices — sources, concepts, persons,
   notes — get a symmetric grid so the prose reads centred on the page.
   The default rail-prose-margin asymmetry is intentional only for
   articles and quests, where voice barges fire into the wider right
   column. Without voices, the right column is empty whitespace and the
   prose reads as left-of-centre. Equal rails on both sides fixes it. */
.source-page .prose-grid,
.concept-page .prose-grid,
.person-page .prose-grid,
.note-page .prose-grid {
  grid-template-columns: var(--col-rail) var(--col-prose) var(--col-rail);
}

.article-header {
  grid-column: 1 / -1;
  margin-bottom: 40px;
  padding: 22px 0 26px;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.breadcrumb {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-secondary);
  margin: 0 0 14px;
}
.article-header h1 {
  font-size: clamp(34px, 3vw + 18px, 52px);
  line-height: 1.08;
  font-weight: 500;
  margin: 0 0 18px;
  letter-spacing: -0.018em;
}
.article-meta {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-secondary);
  margin: 0;
  display: flex;
  gap: 18px;
  text-transform: uppercase;
}

/* ============================================================
   essence deck — the plain-language orientation that sits between
   a concept/article header and the scholarship below it. It exists
   so a page opens on *what the idea is* rather than mid-citation.
   Upright serif (not the italic blockquote register), one step
   larger than reading body, capped to the prose measure and sealed
   by a hairline so it reads as a deck, not as the argument's first
   paragraph.
   ============================================================ */
.essence {
  grid-column: 2;
  max-width: 58ch;
  margin: 0 0 34px;
  padding: 0 0 26px;
  border-bottom: 1px solid var(--hairline);
}
.essence-kicker {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0 0 12px;
}
.essence p {
  font-family: var(--serif);
  font-size: 21px;
  line-height: 1.5;
  color: var(--ink);
  margin: 0 0 0.55em;
}
.essence p:last-child { margin-bottom: 0; }

.rail {
  grid-column: 1;
  position: relative;
}

/* voice spine — ticked column showing where each voice spoke
   in the article. Lives in the rail; only renders on wide viewports. */
.voice-spine {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 1px;
  pointer-events: none;
}
.voice-spine::before {
  content: '';
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  background: var(--hairline);
  width: 1px;
}
.voice-spine-cap {
  position: absolute;
  left: -3px;
  width: 7px;
  height: 1px;
  background: var(--ink-tertiary);
}
.voice-spine-tick {
  position: absolute;
  left: -4px;
  width: 9px;
  height: 3px;
  background: var(--voice-color, var(--ink));
  border-radius: 1px;
  pointer-events: auto;
  cursor: pointer;
  transition: transform 0.12s, height 0.12s;
}
.voice-spine-tick:hover {
  transform: translateX(-1px);
  height: 5px;
  width: 11px;
}
@media (max-width: 959px) {
  .voice-spine { display: none; }
}

.prose {
  grid-column: 2;
  max-width: var(--col-prose);
}
.prose h2 {
  font-size: var(--h2);
  line-height: 1.3;
  margin: 1.8em 0 0.5em;
  font-weight: 600;
  letter-spacing: 0;
}
.prose h3 {
  font-size: var(--h3);
  line-height: 1.35;
  margin: 1.4em 0 0.4em;
  font-weight: 600;
}
.prose p {
  margin: 0 0 var(--reading-paragraph-gap);
}
.prose blockquote {
  margin: 1.2em 0;
  padding: 0 0 0 2ch;
  border-left: 1px solid var(--ink-tertiary);
  color: var(--ink-secondary);
  font-style: italic;
}
.prose ul, .prose ol {
  margin: 0 0 var(--reading-paragraph-gap);
  padding-left: 1.4em;
}
.prose li { margin-bottom: 0.3em; }
.prose hr {
  border: 0;
  border-top: 1px solid var(--hairline);
  margin: 2em 0;
}
.prose code {
  font-family: var(--mono);
  font-size: 0.85em;
  background: rgba(28,26,23,0.05);
  padding: 1px 4px;
  border-radius: 2px;
}

/* ============================================================
   the margin lane — voices live here
   ============================================================ */
.margin {
  grid-column: 3;
  position: relative;
  /* JS positions voice blocks absolutely against this column on wide
     viewports. On narrow viewports the column collapses and voices
     fall into the prose flow (see media query below). */
}

.voice {
  /* Marginalia register: no fill, narrower than the body, italic
     prose with a small mono name as inline prefix. The voice's
     color lives only in the left hairline and the name. */
  background: transparent;
  padding: 2px 0 2px 12px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-secondary);
  position: relative;
  margin-bottom: 4px;
}
.voice::before {
  content: '';
  position: absolute;
  left: 0; top: 2px; bottom: 2px;
  width: 2px;
  background: var(--voice-color, var(--ink-tertiary));
  opacity: 0.85;
}
.voice-name {
  display: inline;
  font-family: var(--mono);
  font-style: normal;
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--voice-color, var(--ink-secondary));
  margin-right: 10px;
  vertical-align: 1px;
}
/* Voice glyph — small hand-designed mark per voice, sits as a
   prefix to the voice name. Sized to read at 10px and currentColor
   so it inherits the voice's tint. */
.voice-glyph {
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-right: 5px;
  vertical-align: -1px;
}
.voice-name-text {
  display: inline-block;
}
.voice-body {
  display: inline;
  font-style: italic;
}
.voice-body p {
  display: inline;
  margin: 0;
}

/* Gated voice — the earned marker. A small ochre tick that appears in
   the name row only once the gate clears (voice-gates.js adds
   .is-earned). Hairline, no fill beyond the mark glyph; warm-ochre is
   the corpus's "you did something" accent. Inert until earned. */
.voice-gate-mark {
  display: none;
}
.voice-gate-mark.is-earned {
  display: inline-block;
  width: 6px;
  height: 9px;
  margin-left: 6px;
  vertical-align: 1px;
  /* hairline check: an L drawn with right + bottom borders, rotated 45° */
  border: solid rgba(160, 95, 40, 0.95);
  border-width: 0 1.4px 1.4px 0;
  transform: rotate(45deg);
}

/* On wide viewports, JS sets `position: absolute; top: <y>px;` on each
   voice. The container (.margin) is set to `position: relative` above. */
@media (min-width: 960px) {
  .voice[data-positioned] {
    position: absolute;
    left: 0;
    width: 100%;
  }
}

/* highlight on hover (sentence ↔ voice mutual) */
.voice.is-active,
.prose p.is-anchor-active {
  background: var(--highlight);
}

/* ============================================================
   narrow-viewport fallback: voices inline after their anchor
   ============================================================ */
@media (max-width: 959px) {
  .prose-grid,
  .prose-grid--solo {
    grid-template-columns: minmax(0, 1fr);
  }
  /* With only one explicit column, .prose's grid-column: 2 would
     create an implicit second column and put the prose alongside
     the header — collapse them both to column 1. */
  .prose-grid .prose,
  .prose-grid--solo .prose,
  .prose-grid .margin,
  .prose-grid--solo .margin,
  .prose-grid .essence,
  .prose-grid--solo .essence {
    grid-column: 1;
  }
  .rail, .margin { display: contents; }
  .voice {
    margin: 4px 0 var(--reading-paragraph-gap);
    font-size: 13px;
  }
  .entity-row {
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    row-gap: 2px;
  }
  .entity-row .row-meta {
    grid-column: 2;
    grid-row: 2;
  }
}

/* ============================================================
   the voices — /voices.html index + /voices/<name>.html collected pages
   ============================================================ */

.voice-index,
.voice-collect {
  max-width: 46rem;
  margin: 0 auto;
  padding: var(--space-6, 2rem) var(--space-4, 1rem);
}
.voice-index-head,
.voice-collect-head {
  margin-bottom: var(--space-6, 2rem);
}
.voice-index-head h1 {
  margin: 0 0 0.4rem;
}
.voice-index-standfirst,
.voice-collect-standfirst {
  color: var(--ink-secondary);
  font-style: italic;
  margin: 0;
}
.voice-collect-name {
  margin: 0.2rem 0 0.4rem;
}
.voice-collect-count {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0.6rem 0 0;
}

/* index: one row per voice, hairline-separated */
.voice-index-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.voice-index-row {
  border-top: 1px solid var(--rule);
}
.voice-index-row:last-child {
  border-bottom: 1px solid var(--rule);
}
.voice-index-link {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 0.2rem;
  text-decoration: none;
  position: relative;
}
.voice-index-link::before {
  /* the voice's hairline bar, echoing the margin grammar */
  content: '';
  position: absolute;
  left: -10px; top: 0.7rem; bottom: 0.7rem;
  width: 2px;
  background: var(--voice-color, var(--ink-tertiary));
  opacity: 0.85;
}
.voice-index-name {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.voice-index-count {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ink-tertiary);
}

/* collected page: list of remarks, each with its margin-style aside
   and a quiet attribution line back to the host page */
.voice-collect-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.voice-collect-entry {
  padding: var(--space-4, 1rem) 0;
  border-top: 1px solid var(--rule);
}
.voice-collect-entry:last-child {
  border-bottom: 1px solid var(--rule);
}
/* the aside reuses .voice / .voice-body styling; in this context it's
   never positioned (no JS), so override the wide-viewport absolute rule */
.voice--collected {
  position: static;
  width: auto;
  max-width: none;
  font-size: 14px;
  margin: 0;
}
.voice--collected .voice-body {
  display: block;
}
.voice--collected .voice-body p {
  display: inline;
}
.voice-collect-attr {
  margin: 0.5rem 0 0 12px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.voice-collect-attr .xref {
  color: var(--ink-secondary);
}
.voice-collect-gated,
.voice-collect-empty {
  color: var(--ink-tertiary);
}
.voice-collect-gated {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 1px 4px;
  margin-left: 0.3rem;
}

/* ============================================================
   xrefs (links to other entities)
   ============================================================ */
.xref {
  text-decoration: none;
  border-bottom: 1px dotted var(--ink-tertiary);
  position: relative;
  padding-right: 0.6em;
}
.xref::after {
  content: '°';
  position: absolute;
  right: 0;
  top: -0.15em;
  font-size: 0.75em;
  color: var(--ink-tertiary);
}
.xref:hover { border-bottom-color: var(--ink); }
.xref-missing { color: var(--ink-tertiary); border-bottom-style: dashed; }

/* ============================================================
   xref tooltip — substance leads, attribution sits in a quiet footer
   ============================================================ */
.xref-tip {
  position: absolute;
  z-index: 50;
  width: 360px;
  background: var(--paper);
  padding: 14px 20px 12px;
  font-family: var(--serif);
  color: var(--ink);
  border: 1px solid var(--hairline);
  box-shadow: 0 12px 30px -8px rgba(28,26,23,0.18);
  pointer-events: none;
  animation: tip-in 140ms ease-out;
}
@keyframes tip-in {
  from { opacity: 0; transform: translateY(-3px); }
  to   { opacity: 1; transform: translateY(0); }
}
.xref-tip[hidden] { display: none; }
.xref-tip .tip-title {
  font-weight: 500;
  margin: 0 0 8px;
  font-size: 16px;
  line-height: 1.2;
  letter-spacing: -0.005em;
  color: var(--ink);
}
.xref-tip .tip-lead {
  margin: 0 0 10px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-secondary);
}
.xref-tip .tip-attribution {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0;
  padding-top: 10px;
  border-top: 1px solid var(--hairline);
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.xref-tip .tip-attribution .sep { opacity: 0.5; }
.xref-tip .tip-unvetted { color: var(--voice-color, var(--ink-tertiary)); font-weight: 600; }
.xref-tip[data-flipped] {
  box-shadow: 0 -14px 44px rgba(28,26,23,0.14), 0 -1px 0 var(--hairline);
}

/* ============================================================
   topic page
   ============================================================ */
/* Generative masthead — flow-field hairline plate seeded from
   topic id. Sits above the header as a wide print-aesthetic
   illustration. Tyler-Hobbs-style "flow field" — the algorithm
   defines the field; the seed jitters it; particles trace its
   currents as ink-coloured paths. */
.topic-masthead {
  margin: 0 0 16px;
  padding: 0;
}
.topic-masthead svg {
  display: block;
  width: 100%;
  height: auto;
  max-height: 160px;
}
/* Animated reveal: each path traces itself in. The generator emits
   pathLength="1" on every path so dash math is normalised — we
   can use stroke-dasharray:1 and animate stroke-dashoffset 1→0
   to trace the full path regardless of its geometric length.
   Per-path animation-delay is inline-styled by the generator so
   the 22 mast lines stagger over the first ~1.3 seconds.
   The pitch-box watermark reuses the same generator + class
   conventions and gets a gentler trace. */
.topic-masthead-svg path,
.quest-pitch-watermark path {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: topic-masthead-trace 1800ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
@keyframes topic-masthead-trace {
  to { stroke-dashoffset: 0; }
}
/* Reduced-motion: skip the trace, just show the lines. */
@media (prefers-reduced-motion: reduce) {
  .topic-masthead-svg path,
  .quest-pitch-watermark path {
    animation: none;
    stroke-dashoffset: 0;
  }
}
/* Living glyphs, part 3 — view transitions between the map and a
   concept page. Same-origin MPA navigation cross-fades by default
   (kept subtle and fast); the clicked map node's glyph and the
   destination page's header glyph share a per-concept
   view-transition-name (glyph-<id>, set inline in build.mjs) so that
   pair MORPHS while everything else cross-fades. Progressive
   enhancement: unsupported browsers just navigate.
   The transformed pan/zoom territory SVG is a known hazard for the
   morph — see the build report for the scoping decision. */
@view-transition {
  navigation: auto;
}
::view-transition-group(*) {
  animation-duration: 320ms;
  animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
}
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 280ms;
  animation-timing-function: ease-out;
}
/* Reduced motion: no view transitions at all — snap navigate. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* Living glyphs, part 1 — the bespoke header mark draws itself in on
   first paint. Every stroked element carries pathLength="1" (injected
   at build time in glyphs/index.mjs → normalizePathLength), so a single
   dash trace reads the same on a 4px tick and a 300px ring. Children
   stagger across the first ~700ms via :nth-child delays; the whole
   draw settles under 900ms, ease-out, once. Only the header instance
   animates — the tiny map-node and chip glyphs are excluded by the
   ancestor selector. Solid-fill backings aren't stroked, so they never
   flash; they sit fully painted from frame one.
   Repeat visits: a per-page sessionStorage flag (set by
   reading-progress.js → the .glyph-seen body class) skips the redraw,
   so re-entering a page you've already opened shows the mark settled. */
.concept-header-glyph .concept-glyph line,
.concept-header-glyph .concept-glyph polyline,
.concept-header-glyph .concept-glyph polygon,
.concept-header-glyph .concept-glyph path,
.concept-header-glyph .concept-glyph circle:not([fill="#F2EBDD"]),
.person-header-mark .person-mark line,
.person-header-mark .person-mark polyline,
.person-header-mark .person-mark path {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: glyph-draw-in 620ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
/* Stagger the children so the mark assembles stroke-by-stroke rather
   than blooming all at once — a draftsman's hand, not a stamp. Caps at
   ~280ms of lead-in so even busy glyphs finish under 900ms. */
.concept-header-glyph .concept-glyph > *:nth-child(2) { animation-delay: 40ms; }
.concept-header-glyph .concept-glyph > *:nth-child(3) { animation-delay: 80ms; }
.concept-header-glyph .concept-glyph > *:nth-child(4) { animation-delay: 110ms; }
.concept-header-glyph .concept-glyph > *:nth-child(5) { animation-delay: 140ms; }
.concept-header-glyph .concept-glyph > *:nth-child(6) { animation-delay: 165ms; }
.concept-header-glyph .concept-glyph > *:nth-child(7) { animation-delay: 190ms; }
.concept-header-glyph .concept-glyph > *:nth-child(8) { animation-delay: 210ms; }
.concept-header-glyph .concept-glyph > *:nth-child(n+9) { animation-delay: 240ms; }
.concept-header-glyph .concept-glyph > *:nth-child(n+14) { animation-delay: 280ms; }
/* Text labels inside person-marks (years) shouldn't trace — fade them. */
.person-header-mark .person-mark text {
  opacity: 0;
  animation: glyph-label-fade 360ms ease-out 280ms forwards;
}
@keyframes glyph-draw-in {
  to { stroke-dashoffset: 0; }
}
@keyframes glyph-label-fade {
  to { opacity: 1; }
}
/* Already-seen this page this session: paint settled, no redraw. */
.glyph-seen .concept-header-glyph .concept-glyph line,
.glyph-seen .concept-header-glyph .concept-glyph polyline,
.glyph-seen .concept-header-glyph .concept-glyph polygon,
.glyph-seen .concept-header-glyph .concept-glyph path,
.glyph-seen .concept-header-glyph .concept-glyph circle:not([fill="#F2EBDD"]),
.glyph-seen .person-header-mark .person-mark line,
.glyph-seen .person-header-mark .person-mark polyline,
.glyph-seen .person-header-mark .person-mark path,
.glyph-seen .person-header-mark .person-mark text {
  animation: none;
  stroke-dashoffset: 0;
  opacity: 1;
}
/* Reduced motion: no trace, mark sits fully inked. */
@media (prefers-reduced-motion: reduce) {
  .concept-header-glyph .concept-glyph line,
  .concept-header-glyph .concept-glyph polyline,
  .concept-header-glyph .concept-glyph polygon,
  .concept-header-glyph .concept-glyph path,
  .concept-header-glyph .concept-glyph circle:not([fill="#F2EBDD"]),
  .person-header-mark .person-mark line,
  .person-header-mark .person-mark polyline,
  .person-header-mark .person-mark path,
  .person-header-mark .person-mark text {
    animation: none;
    stroke-dashoffset: 0;
    opacity: 1;
  }
}

/* On the kind-index pages, the masthead is shorter (the hubs need
   to surface lots of items quickly), and the breadcrumb beneath
   it is more compact. */
.kind-index-masthead {
  margin: 0 0 8px;
}
.kind-index-masthead svg {
  max-height: 110px;
}
/* On the home page, the masthead is the front door: taller, denser,
   the algorithmic art at maximum visibility. */
.home-masthead {
  margin: 0 0 28px;
}
.home-masthead svg {
  max-height: 220px;
}

/* Copy-link affordances — tiny clipboard glyph next to entity h1s.
   Visible only on hover/focus of the heading, so the page chrome
   stays quiet. ⌥+click anywhere (xrefs, internal links) also
   triggers copy via copy-link.js, with no visible affordance. */
h1[data-copyable-h1] {
  position: relative;
}
.copy-link-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: 0.18em;
  margin-left: 12px;
  width: 22px;
  height: 22px;
  padding: 0;
  background: none;
  border: 1px solid transparent;
  border-radius: 2px;
  color: var(--ink-tertiary);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity 180ms ease, color 120ms ease, border-color 120ms ease;
}
h1[data-copyable-h1]:hover .copy-link-btn,
h1[data-copyable-h1]:focus-within .copy-link-btn,
.copy-link-btn:focus-visible {
  opacity: 0.6;
}
.copy-link-btn:hover,
.copy-link-btn:focus-visible {
  opacity: 1 !important;
  color: var(--ink);
  border-color: var(--hairline);
  outline: none;
}

/* Heading anchors — small § link injected into every h2/h3/h4
   rendered through marked. Invisible until the heading is hovered
   or focused; click copies URL+anchor to clipboard (via
   copy-link.js) and also navigates to the anchor naturally. */
.heading-anchor {
  display: inline-block;
  margin-left: 8px;
  color: var(--ink-tertiary);
  text-decoration: none;
  opacity: 0;
  font-weight: 400;
  font-size: 0.7em;
  vertical-align: 0.18em;
  transition: opacity 180ms ease, color 120ms ease;
}
h1:hover > .heading-anchor,
h2:hover > .heading-anchor,
h3:hover > .heading-anchor,
h4:hover > .heading-anchor,
h5:hover > .heading-anchor,
h6:hover > .heading-anchor,
.heading-anchor:focus-visible {
  opacity: 0.55;
}
.heading-anchor:hover,
.heading-anchor:focus-visible {
  opacity: 1 !important;
  color: var(--ink);
  outline: none;
}

/* Trail panel — bottom-left rabbit-hole memory. Lists last ~6
   entity visits. Persistent across sessions via localStorage.
   The "where you've been" affordance for an unquiet mind. */
.trail-panel {
  position: fixed;
  bottom: 28px;
  left: 24px;
  z-index: 4;
  max-width: 220px;
  padding: 10px 0 4px;
  background: var(--paper);
  border-top: 1px solid var(--hairline);
  animation: trail-panel-in 320ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes trail-panel-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.trail-panel-head {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-bottom: 6px;
}
.trail-panel-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.trail-panel-item {
  margin: 0;
}
.trail-panel-link {
  display: grid;
  grid-template-columns: 22px 1fr auto;
  align-items: baseline;
  column-gap: 8px;
  padding: 4px 0;
  text-decoration: none;
  color: var(--ink-secondary);
  font-family: var(--mono);
  font-size: 10px;
  line-height: 1.3;
  transition: color 120ms ease;
}
.trail-panel-link:hover,
.trail-panel-link:focus-visible {
  color: var(--ink);
  outline: none;
}
.trail-panel-kind {
  font-size: 8px;
  letter-spacing: 0.08em;
  color: var(--ink-tertiary);
}
/* When the trail-kind cell holds a glyph (via kind-mark), keep
   it small and centered with the row text. */
.trail-panel-kind.kind-mark--glyph {
  width: 11px;
  height: 11px;
  margin: 0;
}
.trail-panel-title {
  font-family: var(--serif);
  font-size: 12px;
  font-style: normal;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 140px;
}
.trail-panel-time {
  font-size: 8.5px;
  color: var(--ink-tertiary);
  letter-spacing: 0;
}
@media (max-width: 1099px) {
  .trail-panel { display: none; }
}

/* Forward panel — top-right counterpart to the trail. Scans the
   current page for outbound concept links and shows the first
   three that the reader hasn't yet visited. The "where to go
   next" affordance for an unquiet mind. */
.forward-panel {
  position: fixed;
  top: 76px;          /* clear of the sticky header */
  right: 24px;
  z-index: 4;
  max-width: 220px;
  padding: 10px 0 4px;
  background: var(--paper);
  border-top: 1px solid var(--hairline);
  text-align: right;
  animation: forward-panel-in 320ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes forward-panel-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.forward-panel-head {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-bottom: 6px;
}
.forward-panel-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.forward-panel-item {
  margin: 0;
}
.forward-panel-link {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  padding: 6px 0;
  text-decoration: none;
  color: var(--ink-secondary);
  font-family: var(--mono);
  font-size: 10px;
  line-height: 1.3;
  transition: color 120ms ease;
  text-align: right;
}
.forward-panel-link:hover,
.forward-panel-link:focus-visible {
  color: var(--ink);
  outline: none;
}
.forward-panel-kind {
  font-size: 8px;
  letter-spacing: 0.08em;
  color: var(--ink-tertiary);
}
.forward-panel-title {
  font-family: var(--serif);
  font-size: 13px;
  font-style: normal;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
  color: var(--ink);
}
.forward-panel-via {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
  font-style: italic;
  max-width: 200px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.forward-panel-cross {
  display: inline-block;
  font-family: var(--mono);
  font-size: 11px;
  color: rgba(160, 95, 40, 0.95);
  margin-left: 4px;
  vertical-align: middle;
}
.forward-panel-link--cross .forward-panel-title {
  border-bottom: 1px dashed rgba(160, 95, 40, 0.55);
  padding-bottom: 1px;
}
.forward-panel-link:hover {
  color: var(--ink);
}
.forward-panel-link:hover .forward-panel-via {
  color: var(--ink-secondary);
}
@media (max-width: 1099px) {
  .forward-panel { display: none; }
}

/* Reading-progress ruler — thin algorithmic strip in the very left
   margin (6px from the edge), showing scroll progress as
   incrementally-lit ticks. Stable per-page via deterministic
   seed: every visit, the same tick rhythm. */
.reading-progress {
  position: fixed;
  top: 15vh;
  left: 6px;
  width: 14px;
  height: 70vh;
  z-index: 3;
  color: var(--ink);
  pointer-events: none;
}
.reading-progress-svg {
  display: block;
  width: 100%;
  height: 100%;
}
.reading-progress-tick {
  transition: stroke-opacity 240ms ease;
}
.reading-progress-end {
  transition: fill-opacity 320ms ease;
}
.reading-progress--complete .reading-progress-end {
  fill: rgba(160, 95, 40, 0.9);
}
@media (max-width: 1099px) {
  .reading-progress { display: none; }
}

/* Living glyphs, part 2 — the page's own mark as reading progress.
   A small fixed clone of the header glyph in the bottom-right corner.
   Built by reading-progress.js on concept/person pages (which carry a
   bespoke header glyph); other long pages keep the tick-ruler above.
   Strokes ink from --ink-tertiary toward --ink as scroll progress
   fills them (stroke-dashoffset driven inline by JS). At 100% the mark
   sits fully inked and warms a hair, echoing the ruler's "read" end. */
.reading-glyph {
  position: fixed;
  bottom: 26px;
  right: 24px;
  z-index: 3;
  width: 40px;
  height: 24px;
  color: var(--ink-tertiary);
  pointer-events: none;
  transition: color 400ms ease;
}
.reading-glyph-svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}
/* Re-stroke every inked element to the wrapper's currentColor so the
   tertiary→ink warming reads. Solid paper backings keep their fill. */
.reading-glyph-svg line,
.reading-glyph-svg polyline,
.reading-glyph-svg polygon,
.reading-glyph-svg path,
.reading-glyph-svg circle:not([fill="#F2EBDD"]),
.reading-glyph-svg rect:not([fill="#F2EBDD"]),
.reading-glyph-svg ellipse {
  stroke: currentColor;
  stroke-opacity: 0.85;
  transition: stroke-dashoffset 180ms ease;
}
.reading-glyph-svg [fill="#F2EBDD"] { display: none; }
.reading-glyph-svg text { display: none; }
.reading-glyph--complete {
  color: var(--ink);
}
[data-theme="dark"] .reading-glyph { color: var(--ink-tertiary); }
[data-theme="dark"] .reading-glyph--complete { color: var(--ink); }
@media (max-width: 1099px) {
  .reading-glyph { display: none; }
}
/* Reduced motion: progress still tracks scroll (JS-driven), but drop
   the easing so each inked stroke snaps to position with no animation. */
@media (prefers-reduced-motion: reduce) {
  .reading-glyph,
  .reading-glyph-svg line,
  .reading-glyph-svg polyline,
  .reading-glyph-svg polygon,
  .reading-glyph-svg path,
  .reading-glyph-svg circle,
  .reading-glyph-svg rect,
  .reading-glyph-svg ellipse {
    transition: none;
  }
}

/* Floating section TOC — sits in the left margin on wide
   viewports for pages with 3+ headings (h2/h3). Built from
   the same heading ids that power # anchor copy. Current section
   highlighted via IntersectionObserver. */
.section-toc {
  position: fixed;
  top: 50%;
  left: 24px;
  transform: translateY(-50%);
  z-index: 5;
  max-width: 200px;
  max-height: 70vh;
  overflow-y: auto;
  padding: 12px 8px;
  animation: section-toc-in 320ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes section-toc-in {
  from { opacity: 0; transform: translate(-6px, -50%); }
  to   { opacity: 1; transform: translate(0, -50%); }
}
.section-toc-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  border-left: 1px solid var(--hairline);
}
.section-toc-item {
  position: relative;
  padding-left: 14px;
}
.section-toc-item--h3 {
  padding-left: 22px;
  font-size: 9.5px;
}
.section-toc-link {
  display: inline-block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.05em;
  line-height: 1.45;
  color: var(--ink-tertiary);
  text-decoration: none;
  transition: color 150ms ease;
  max-width: 180px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.section-toc-link:hover,
.section-toc-link:focus-visible {
  color: var(--ink);
  outline: none;
}
.section-toc-item--active::before {
  content: '';
  position: absolute;
  left: -1px;
  top: 50%;
  transform: translateY(-50%);
  width: 6px;
  height: 1.5px;
  background: var(--ink);
}
.section-toc-item--active .section-toc-link {
  color: var(--ink);
}
/* Only show TOC when there's room. */
@media (max-width: 1179px) {
  .section-toc { display: none; }
}

/* Adjacent-entity nav at the bottom of every entity page.
   Prev on the left, next on the right; both omitted at the ends
   of an alphabetical list. Visible counterpart to the keyboard
   `[` and `]` bindings. Hairline top border separates it from
   the page's main content. */
.adjacent-nav {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  margin: 72px 0 0;
  padding: 24px 0 0;
  border-top: 1px solid var(--hairline);
}
.adjacent-nav-link {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 16px;
  border: 1px solid transparent;
  text-decoration: none;
  color: inherit;
  transition: border-color 120ms ease, background 120ms ease;
}
.adjacent-nav-link:hover,
.adjacent-nav-link:focus-visible {
  border-color: var(--hairline);
  background: rgba(28, 26, 23, 0.025);
  outline: none;
}
.adjacent-nav-link--prev {
  text-align: left;
}
.adjacent-nav-link--next {
  text-align: right;
  align-items: flex-end;
}
.adjacent-nav-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.adjacent-nav-link--prev .adjacent-nav-label::before { content: '← '; }
.adjacent-nav-link--next .adjacent-nav-label::after { content: ' →'; }
.adjacent-nav-title {
  font-family: var(--serif);
  font-size: 16px;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.3;
}
.adjacent-nav-link:hover .adjacent-nav-title {
  text-decoration: underline;
  text-decoration-color: var(--ink-tertiary);
  text-underline-offset: 3px;
}
.adjacent-nav-spacer {
  /* keeps the grid balanced when only one side has a link */
}

/* Source citation copy button — sits below the citation dl. Quiet
   mono pill that wakes up on hover. Click → formatted one-line
   citation goes to clipboard. */
.source-citation-copy {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 12px 0 0;
  padding: 5px 10px;
  background: transparent;
  border: 1px solid var(--hairline);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.source-citation-copy:hover,
.source-citation-copy:focus-visible {
  color: var(--ink);
  border-color: var(--ink);
  background: var(--paper);
  outline: none;
}
/* The curator-facing capture line — small italic register, sits
   below the citation. Says when this entered the corpus, and
   (optionally) when it was vetted. Adjacent in tone to a librarian's
   pencil mark on the inside cover. */
.source-caught-line {
  font-family: var(--serif);
  font-size: 13px;
  font-style: italic;
  color: var(--ink-tertiary);
  margin: 10px 0 0;
  letter-spacing: 0;
}

/* Keyboard shortcuts overlay — appears on '?', dismisses on '?'
   again or Esc. Click outside the card also closes. */
#kbd-overlay {
  position: fixed;
  inset: 0;
  background: rgba(28, 26, 23, 0.18);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 200;
  animation: kbd-overlay-fade 180ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
#kbd-overlay.kbd-overlay--visible {
  display: flex;
}
@keyframes kbd-overlay-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
.kbd-overlay-card {
  background: var(--paper);
  border: 1px solid var(--ink);
  padding: 28px 32px;
  min-width: 360px;
  max-width: 560px;
  max-height: 86vh;
  overflow-y: auto;
  box-shadow: 0 12px 36px -8px rgba(28, 26, 23, 0.2);
}
.kbd-overlay-card h2 {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  font-weight: 500;
  margin: 0 0 18px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--hairline);
}
.kbd-overlay-subtitle {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(160, 95, 40, 0.92);
  font-weight: 500;
  margin: 22px 0 12px;
  padding-bottom: 10px;
  border-bottom: 1px dashed rgba(160, 95, 40, 0.5);
}
.kbd-gesture {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-secondary);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}
.kbd-list--territory > div {
  grid-template-columns: 130px 1fr;
}
.kbd-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
  margin: 0;
}
.kbd-list > div {
  display: grid;
  grid-template-columns: 100px 1fr;
  gap: 16px;
  font-size: 13px;
  line-height: 1.4;
  font-family: var(--serif);
  color: var(--ink-secondary);
}
.kbd-list dt {
  margin: 0;
  display: flex;
  gap: 4px;
  align-items: center;
}
.kbd-list dd {
  margin: 0;
}
.kbd-list kbd {
  font-family: var(--mono);
  font-size: 11px;
  padding: 1px 6px;
  border: 1px solid var(--hairline);
  border-bottom-width: 2px;
  border-radius: 3px;
  background: var(--paper-warm);
  color: var(--ink);
  letter-spacing: 0;
}
.kbd-list-aside {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--hairline);
  font-size: 11px;
  color: var(--ink-tertiary);
  font-style: italic;
}

/* Toast — confirms a copy. Subtle, fixed bottom-centre, mono. */
#copy-toast {
  position: fixed;
  left: 50%;
  bottom: 32px;
  transform: translate(-50%, 12px);
  opacity: 0;
  pointer-events: none;
  padding: 8px 14px;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  z-index: 100;
  transition: opacity 220ms ease, transform 220ms cubic-bezier(0.22, 1, 0.36, 1);
}
#copy-toast.copy-toast--visible {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* Gallery page — the exhibit of every algorithmic mark. */
.gallery-head {
  margin: 4px 0 56px;
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 24px;
}
.gallery-head h1 {
  font-size: clamp(36px, 3vw + 18px, 52px);
  line-height: 1.05;
  margin: 0 0 12px;
}
.gallery-section {
  margin: 0 0 72px;
}
.gallery-section h2 {
  font-family: var(--serif);
  font-size: 24px;
  font-weight: 500;
  margin: 0 0 8px;
}
.gallery-section-note {
  font-family: var(--serif);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-secondary);
  max-width: 70ch;
  margin: 0 0 24px;
  font-style: italic;
}
.gallery-row {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 24px;
  align-items: end;
}
.gallery-row--mastheads {
  grid-template-columns: 1fr;
  gap: 32px;
}
.gallery-row--signatures {
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.gallery-row--endings {
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  gap: 12px;
}
.gallery-row--persons {
  grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
  gap: 16px;
  align-items: start;
}
.gallery-row--constellations {
  grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
  gap: 32px;
}
.gallery-cell {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
}
.gallery-cell figcaption {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  font-weight: 500;
}
.gallery-cell svg {
  display: block;
  width: 100%;
  height: auto;
  align-self: stretch;
}
.gallery-cell--portrait {
  align-items: center;
}
.gallery-cell--portrait svg {
  width: 60px;
  height: 140px;
}
.gallery-row--notes {
  grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
  gap: 16px;
}
.gallery-cell--note {
  align-items: flex-start;
}
.gallery-cell--note svg {
  width: 56px;
  height: 56px;
}
.gallery-cell--tiny svg {
  width: 80px;
  height: 60px;
}

/* Note page mark — small algorithmic marginal-jot square seeded
   from note id. Sits in the top-right corner of the note header.
   Smallest mark in the catalogue (56×56), in keeping with the
   note's register as a margin observation. */
.note-header-mark {
  float: right;
  margin: -2px 0 12px 16px;
  shape-outside: inset(0 0 0 0);
  opacity: 0.9;
}
.note-header-mark svg {
  display: block;
  width: 56px;
  height: 56px;
}
@media (max-width: 720px) {
  .note-header-mark {
    float: none;
    margin: 0 0 16px;
  }
}

/* Person page mark — vertical algorithmic lifeline seeded from
   person id. Portrait orientation distinguishes it from the
   source plate's landscape orientation; both are geometric (Mohr
   family). Sits in the upper-right of the person header. */
.person-header-mark {
  float: right;
  margin: -2px 0 12px 16px;
  shape-outside: inset(0 0 0 0);
  opacity: 0.9;
}
.person-header-mark svg {
  display: block;
  width: 60px;
  height: 140px;
}
@media (max-width: 720px) {
  .person-header-mark {
    float: none;
    margin: 0 0 16px;
  }
}

/* Source page plate — small library-card-style algorithmic mark.
   Sits in the upper-right of the source header. Manfred-Mohr
   geometric family: horizontal strata + vertical tick marks +
   a corner identifier (filled if vetted). Unique per source id. */
.source-header-plate {
  float: right;
  margin: -4px 0 12px 16px;
  shape-outside: inset(0 0 0 0);
  opacity: 0.85;
}
.source-header-plate svg {
  display: block;
  width: 220px;
  height: 80px;
}
@media (max-width: 720px) {
  .source-header-plate {
    float: none;
    margin: 0 0 16px;
  }
  .source-header-plate svg {
    width: 100%;
    max-width: 220px;
  }
}
.topic-view header {
  margin-bottom: 48px;
  padding: 22px 0 28px;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.topic-territory-link {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5em;
  margin-top: 20px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-secondary);
  border-bottom: 1px solid rgba(160, 95, 40, 0.5);
  padding-bottom: 2px;
  transition: color 120ms ease, border-color 120ms ease;
}
.topic-territory-link:hover {
  color: rgba(120, 70, 28, 1);
  border-bottom-color: rgba(160, 95, 40, 0.95);
}
.topic-territory-arrow {
  font-family: var(--serif);
  font-size: 14px;
  color: rgba(160, 95, 40, 0.85);
  transition: transform 220ms ease;
}
.topic-territory-link:hover .topic-territory-arrow {
  transform: translateX(3px);
}
.topic-view h1 {
  font-size: clamp(36px, 3.4vw + 18px, 56px);
  font-weight: 500;
  margin: 0 0 18px;
  line-height: 1.06;
  letter-spacing: -0.02em;
}
.topic-view .breadcrumb { margin: 0 0 14px; }
.topic-view .article-meta {
  /* Counts get more breathing room, hairline-separated rather than just gapped */
  gap: 0;
  flex-wrap: wrap;
  align-items: baseline;
}
.topic-view .article-meta > * {
  padding: 0 14px;
  border-left: 1px solid var(--hairline);
}
.topic-view .article-meta > *:first-child {
  padding-left: 0;
  border-left: none;
}
.topic-summary {
  max-width: 60ch;
  margin: 22px 0 0;
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink-secondary);
}

/* concept page header — algorithmic ink-mark inline with the title,
   then aka pills and related-concept chips. The glyph carries the
   visual signature of the entity kind; sources and people use plainer
   headers. */
.concept-header {
  display: block;
}
.concept-header > .breadcrumb { margin-bottom: 18px; }
.concept-header-main {
  display: flex;
  align-items: center;
  gap: 26px;
  margin: 0;
}
.concept-header-text { min-width: 0; flex: 1 1 auto; }
.concept-header-text h1 { margin: 0; }
.concept-header-glyph {
  flex: 0 0 auto;
  width: 180px;
  opacity: 0.92;
}
.concept-header-glyph svg {
  display: block;
  width: 180px;
  height: 108px;
}

/* also-known-as: horizontal row of inline pills */
.concept-aka-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 20px 0 0;
}
.concept-aka-pill {
  display: inline-block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-secondary);
  background: var(--paper-warm);
  border: 1px solid var(--hairline);
  padding: 4px 10px;
  line-height: 1.2;
}

/* related concepts: row of chips, each a tiny glyph + linked name,
   borderless, separated by a hairline rule between chips */
.concept-related-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
  margin: 14px 0 0;
}
.concept-related-row-label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.concept-related-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}
.concept-related-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 12px;
  color: var(--ink);
  text-decoration: none;
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.2;
  border-left: 1px solid var(--hairline);
  transition: background 140ms ease;
}
.concept-related-chips > .concept-related-chip:first-child {
  border-left: none;
  padding-left: 4px;
}
.concept-related-chip:hover {
  background: rgba(28, 26, 23, 0.04);
  text-decoration: underline;
  text-decoration-color: rgba(160, 95, 40, 0.55);
  text-underline-offset: 3px;
}
.concept-related-chip:hover .concept-related-chip-glyph { opacity: 1; }
.concept-related-chip--missing { color: var(--ink-tertiary); font-style: italic; }
.concept-related-chip-glyph {
  display: inline-flex;
  flex: 0 0 auto;
  opacity: 0.85;
  transition: opacity 140ms ease;
}
.concept-related-chip-glyph svg {
  display: block;
  width: 24px;
  height: 14px;
}

/* Cross-topic relation — same ochre vocabulary as the territory.
   Small ↗ marker after the label hints 'this leads to the other
   continent'. */
.concept-related-chip--cross-topic .concept-related-chip-label {
  border-bottom: 1px dashed rgba(160, 95, 40, 0.5);
  padding-bottom: 1px;
}
.concept-related-chip-cross {
  font-family: var(--mono);
  font-size: 11px;
  color: rgba(160, 95, 40, 0.85);
  margin-left: 2px;
}
.concept-related-chip--cross-topic:hover {
  background: rgba(160, 95, 40, 0.05);
}
.concept-related-chip--cross-topic:hover .concept-related-chip-label {
  border-bottom-color: rgba(160, 95, 40, 0.85);
}

/* narrow viewports: stack glyph above title, scale it down */
@media (max-width: 720px) {
  .concept-header-main {
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
  }
  .concept-header-glyph,
  .concept-header-glyph svg {
    width: 140px;
  }
  .concept-header-glyph svg { height: 84px; }
}

/* chronograph — generated timeline at top of topic page */
.chronograph {
  margin: 0 0 32px;
  padding: 0;
  max-width: 100%;
}
.chronograph svg { display: block; color: var(--ink); }
.chronograph figcaption {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-top: 6px;
}

/* chronology — the corpus-wide time plate at /chronology.html.
   Lanes carry topic colour baked into the SVG strokes; the axis and
   dividers use currentColor (= --ink) so they swap with the theme,
   exactly as the chronograph does. */
.chronology-page {
  max-width: 1040px;
  margin: 0 auto;
  padding: 0 24px 72px;
}
.chronology-header { margin: 8px 0 24px; }
.chronology-title {
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  margin: 0 0 8px;
}
.chronology-pitch {
  color: var(--ink-secondary);
  max-width: 60ch;
  margin: 0 0 14px;
}
.chronology-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  align-items: center;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0;
}
.chronology-legend-item { display: inline-flex; align-items: center; gap: 7px; }
.chronology-legend-tick {
  display: inline-block;
  width: 1px;
  height: 11px;
  background: var(--ink-secondary);
}
.chronology-legend-line {
  display: inline-block;
  width: 18px;
  height: 1px;
  background: var(--ink-secondary);
}
.chronology-legend-note { color: var(--ink-tertiary); opacity: 0.85; }

.chronology-plate-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.chronology-plate {
  margin: 0;
  /* Below ~700px the axis would crush; keep it legible and let the
     container scroll horizontally instead. */
  min-width: 680px;
}
.chronology-plate svg { display: block; color: var(--ink); width: 100%; height: auto; }

/* Lane labels and marks: ochre on hover, consistent with the site. */
.chronology-lane-label text { transition: fill-opacity 0.12s; }
.chronology-lane-label:hover text { fill-opacity: 0.7; }
.chronology-mark { cursor: pointer; }
.chronology-mark line { transition: stroke 0.1s, stroke-opacity 0.1s; }
.chronology-mark:hover line,
.chronology-mark:focus-visible line {
  stroke: var(--ochre-2);
  stroke-opacity: 1;
}
.chronology-mark:focus-visible { outline: none; }

.chronology-foot {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 14px 0 0;
}

@media (max-width: 640px) {
  .chronology-page { padding: 0 16px 56px; }
}

/* topic map — Castlevania world view */
.topic-map {
  margin: 0;
  padding: 0;
}
.topic-map svg {
  display: block;
  width: 100%;
  height: auto;
  background: var(--paper);
}
/* Hover-highlight: when a node is hovered (.map-node--focus), JS
   marks neighbour edges and nodes with --neighbor / --highlighted
   classes. Non-neighbours dim down. */
.topic-map.map--exploring .map-edge {
  stroke-opacity: 0.10;
  transition: stroke-opacity 200ms ease;
}
.topic-map.map--exploring .map-edge--neighbor {
  stroke-opacity: 0.45 !important;
  stroke-width: 0.9;
}
.topic-map.map--exploring .map-node {
  opacity: 0.55;
  transition: opacity 200ms ease;
}
.topic-map.map--exploring .map-node--neighbor,
.topic-map.map--exploring .map-node--focus {
  opacity: 1;
}
.topic-map.map--exploring .map-node--focus .map-node-bg {
  stroke: var(--ink);
  stroke-width: 1.4;
}
.map-node {
  cursor: pointer;
}
.topic-map figcaption {
  margin-top: 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.map-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.map-legend-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid var(--ink-tertiary);
  background: transparent;
}
.map-legend-item--visited .map-legend-dot {
  background: var(--ink);
  border-color: var(--ink);
}
.map-legend-item--quest .map-legend-dot {
  background: var(--ink);
  border-color: var(--ink);
  position: relative;
}
.map-legend-item--quest .map-legend-dot::after {
  content: 'Q';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 6px;
  font-weight: 600;
  color: var(--paper);
  letter-spacing: 0;
}

/* Quest-mark state — painted by topic-map.js from localStorage. */
.map-quest-mark .map-quest-mark-bg {
  transition: fill 200ms ease, stroke 200ms ease;
}
.map-quest-mark .map-quest-mark-q {
  transition: fill 200ms ease;
}
.map-quest-mark--available .map-quest-mark-bg {
  fill: var(--paper);
  stroke: rgba(28,26,23,0.7);
}
.map-quest-mark--available .map-quest-mark-q {
  fill: rgba(28,26,23,0.7);
}
.map-quest-mark--partial .map-quest-mark-bg {
  fill: rgba(140, 107, 47, 0.85);  /* ochre — half walked */
  stroke: rgba(28,26,23,0.85);
}
.map-quest-mark--partial .map-quest-mark-q {
  fill: var(--paper);
}
.map-quest-mark--full .map-quest-mark-bg {
  fill: #1C1A17;
  stroke: #1C1A17;
}
.map-quest-mark--full .map-quest-mark-q {
  fill: var(--paper);
}

/* node states (set by topic-map.js) */
.map-node a { text-decoration: none; outline: none; }
.map-node-bg {
  transition: stroke 180ms ease, fill 180ms ease;
}
.map-node-glyph {
  transition: opacity 180ms ease;
}
.map-node-label {
  transition: fill 180ms ease, font-weight 180ms ease;
  pointer-events: none;
}
.map-node--unread .map-node-glyph { opacity: 0.32; }
.map-node--unread .map-node-label { fill: rgba(28,26,23,0.45); }
.map-node--unread .map-node-bg { stroke: rgba(28,26,23,0.12); }
.map-node--visited .map-node-glyph { opacity: 1; }
.map-node--visited .map-node-label { fill: rgba(28,26,23,0.95); }
.map-node--visited .map-node-bg { stroke: rgba(28,26,23,0.55); }
/* Dark-mode equivalents — same engagement-state hierarchy, cream
   ink instead of dark ink. */
[data-theme="dark"] .map-node--unread .map-node-label { fill: rgba(232,223,201,0.55); }
[data-theme="dark"] .map-node--unread .map-node-bg    { stroke: rgba(232,223,201,0.16); }
[data-theme="dark"] .map-node--visited .map-node-label { fill: rgba(232,223,201,0.95); }
[data-theme="dark"] .map-node--visited .map-node-bg    { stroke: rgba(232,223,201,0.55); }
/* Quest-mark badge in dark: the dark-on-paper register flips */
[data-theme="dark"] .map-quest-mark--available .map-quest-mark-bg {
  fill: var(--paper);
  stroke: rgba(232, 223, 201, 0.6);
}
[data-theme="dark"] .map-quest-mark--available .map-quest-mark-q {
  fill: rgba(232, 223, 201, 0.78);
}
[data-theme="dark"] .map-quest-mark--partial .map-quest-mark-bg {
  fill: rgba(196, 130, 70, 0.7);
  stroke: rgba(232, 223, 201, 0.55);
}
[data-theme="dark"] .map-quest-mark--partial .map-quest-mark-q {
  fill: var(--paper);
}
[data-theme="dark"] .map-quest-mark--full .map-quest-mark-bg {
  fill: rgba(232, 223, 201, 0.85);
  stroke: rgba(232, 223, 201, 0.85);
}
[data-theme="dark"] .map-quest-mark--full .map-quest-mark-q {
  fill: var(--paper);
}
/* Pinned node ring + glyph shadow translate */
[data-theme="dark"] .map-node--pinned .map-node-glyph {
  filter: drop-shadow(0 0 8px rgba(220, 152, 90, 0.45));
}
/* Reading-progress ruler picked up `color: var(--ink)` — already
   inherits via currentColor, no override needed. */
/* Hover lifts the glyph fully bright even if unread, so the user
   can scan what's there. */
.map-node:hover .map-node-glyph { opacity: 1; }
.map-node:hover .map-node-bg { stroke: var(--ink); }
.map-node:hover .map-node-label {
  fill: var(--ink);
  font-weight: 600;
}

/* topic-section variant: the map sits in a full-width section without
   the usual H2 + list register pattern. */
.topic-section--map {
  margin-top: var(--section-gap);
}

/* Dedicated map page — minimal chrome around the map figure */
.topic-map-page {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 16px;
}
/* Map is the page's centerpiece — give it room. */
.topic-map-page-body main {
  max-width: none;
}
.topic-map-header {
  padding: 24px 0 32px;
  margin-bottom: 32px;
}
.topic-map-header .breadcrumb {
  margin-bottom: 16px;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5em;
}
.topic-map-header .breadcrumb a {
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
  color: inherit;
}
.topic-map-header .breadcrumb a:hover {
  border-bottom-color: var(--ink);
}
.topic-map-breadcrumb-sep {
  color: var(--ink-tertiary);
}
.topic-map-title {
  font-size: clamp(28px, 2.4vw + 16px, 40px);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.012em;
  margin: 0 0 16px;
}
.topic-map-pitch {
  max-width: 60ch;
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink-secondary);
  margin: 0;
}

/* ----------------- person-page territory inset ----------------- */
/* Each scholar's concept network on the corpus miniature. Their
   associated concepts as full dots with topic-ring colour;
   connecting edges in their network in full ink; the rest of the
   corpus faded to faintest. For the human bridges (scholars who
   span both topics), the inset takes a different border style
   and the eyebrow line acknowledges the span. */
.entity-territory-inset {
  max-width: 1100px;
  margin: 48px auto 40px;
  padding: 22px 22px 26px;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  text-align: center;
}
.entity-territory-inset--bridge {
  border-top-color: rgba(160, 95, 40, 0.55);
  border-bottom-color: rgba(160, 95, 40, 0.55);
}
.person-inset-eyebrow {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-bottom: 6px;
}
.entity-territory-inset--bridge .person-inset-eyebrow {
  color: rgba(160, 95, 40, 0.95);
  font-style: italic;
}
.entity-territory-inset svg {
  display: block;
  width: 100%;
  height: auto;
  max-height: 620px;
}
.person-inset-caption {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
  margin: 8px 0 0;
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}
.entity-territory-inset--bridge .person-inset-caption {
  color: rgba(120, 70, 28, 1);
}
.person-inset-deep-link {
  margin-left: 14px;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
  font-style: italic;
}
.person-inset-deep-link:hover {
  border-bottom-color: var(--ink);
}
@media (max-width: 720px) {
  .entity-territory-inset {
    margin: 36px -16px 24px;
    padding: 14px 12px 16px;
  }
}

/* ----------------- source + quest territory insets ----------------- */
/* Same inset chrome as concept/person, with semantically distinct
   eyebrow text. Source: 'the concepts this source discusses'.
   Quest: 'the territory this quest walks'. */
.source-territory-inset,
.quest-territory-inset {
  max-width: 1100px;
  margin: 48px auto 40px;
  padding: 22px 22px 26px;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  text-align: center;
}
.source-territory-inset--bridge,
.quest-territory-inset--bridge {
  border-top-color: rgba(160, 95, 40, 0.55);
  border-bottom-color: rgba(160, 95, 40, 0.55);
}
.source-inset-eyebrow,
.quest-inset-eyebrow {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-bottom: 6px;
}
.source-territory-inset--bridge .source-inset-eyebrow,
.quest-territory-inset--bridge .quest-inset-eyebrow {
  color: rgba(160, 95, 40, 0.95);
  font-style: italic;
}
.source-territory-inset svg,
.quest-territory-inset svg {
  display: block;
  width: 100%;
  height: auto;
  max-height: 620px;
}
.source-inset-caption,
.quest-inset-caption {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
  margin: 8px 0 0;
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}
.source-territory-inset--bridge .source-inset-caption,
.quest-territory-inset--bridge .quest-inset-caption {
  color: rgba(120, 70, 28, 1);
}
.source-inset-deep-link,
.quest-inset-deep-link {
  margin-left: 14px;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
  font-style: italic;
}
.source-inset-deep-link:hover,
.quest-inset-deep-link:hover {
  border-bottom-color: var(--ink);
}
@media (max-width: 720px) {
  .source-territory-inset,
  .quest-territory-inset {
    margin: 36px -16px 24px;
    padding: 14px 12px 16px;
  }
}

/* ----------------- concept-page territory inset ----------------- */
/* Tiny star-chart annotation at the bottom of every concept page:
   "you are here in the territory." All 26 concepts as small dots
   at their shared-with-the-main-map positions; current concept
   filled and haloed with a slow pulsing ring; 1-hop neighbours
   filled and labelled; 2-hop smaller; 3+-hop faintest. The
   reader's spatial sense of the corpus, always available without
   leaving the page they're reading. */
.concept-territory-inset {
  max-width: 1100px;
  margin: 56px auto 40px;
  padding: 22px 22px 26px;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  text-align: center;
}
.concept-inset-eyebrow {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-bottom: 6px;
}
.concept-territory-inset svg {
  display: block;
  width: 100%;
  height: auto;
  max-height: 620px;
}
.inset-svg-wrap {
  position: relative;
  --cursor-x: 50%;
  --cursor-y: 50%;
}
.inset-svg-wrap .inset-flashlight {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  opacity: 0;
  transition: opacity 420ms ease;
  background:
    radial-gradient(circle 140px at var(--cursor-x) var(--cursor-y),
      rgba(255, 240, 210, 0.10) 0%,
      rgba(255, 240, 210, 0.03) 50%,
      transparent 80%);
  mix-blend-mode: normal;
}
.inset-svg-wrap.has-cursor .inset-flashlight {
  opacity: 1;
}
.concept-territory-inset .concept-inset-nodes circle:hover {
  cursor: pointer;
}
.concept-inset-caption {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
  margin: 8px 0 0;
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.concept-inset-caption b {
  font-weight: 600;
  color: var(--ink);
  font-feature-settings: "tnum";
}
.concept-inset-sep {
  color: var(--hairline);
}
.concept-inset-deep-link {
  margin-left: 14px;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
  font-style: italic;
}
.concept-inset-deep-link:hover {
  border-bottom-color: var(--ink);
}
@media (max-width: 720px) {
  .concept-territory-inset {
    margin: 36px -16px 24px;
    padding: 14px 12px 16px;
    border-radius: 0;
  }
}

/* ----------------- territory page ----------------- */
/* The full-corpus landscape — every concept on one canvas, tied by
   the actual relation graph. The map for an unquiet mind. */
.territory-page-body main {
  max-width: none;
}
.territory-page {
  max-width: 1620px;
  margin: 0 auto;
  padding: 0 16px;
}
.territory-header {
  padding: 18px 0 12px;
}

/* ----------------- the itinerary (mobile projection) -----------------
   The territory page ships two projections of the same data: the
   pannable map (.territory-map-view) and an ordered list (.itinerary).
   Desktop shows the map and hides the itinerary; below the trail/forward
   breakpoint (1100px) the map becomes unusable as a pannable canvas, so
   the list takes over and the map is tucked behind an "open the map
   anyway" disclosure. State marks (read / frontier / resume) are
   layered on by itinerary.js. */
.itinerary { display: none; }
.itinerary-map-reveal { display: none; margin: 18px 0 8px; }
@media (max-width: 1099px) {
  .itinerary { display: block; }
  .itinerary-map-reveal { display: block; }
  .territory-map-view { display: none; }
}
.itinerary-region {
  margin: 0 0 22px;
}
.itinerary-region-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 0 0 6px;
  border-bottom: 1px solid var(--hairline);
  margin: 0 0 6px;
}
.itinerary-region-name {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  font-size: 21px;
  line-height: 1.2;
  margin: 0;
  color: var(--region-color, var(--ink));
}
.itinerary-region-count {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  white-space: nowrap;
}
.itinerary-rows {
  list-style: none;
  margin: 0;
  padding: 0;
}
.itinerary-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 0;
}
.itinerary-link {
  display: flex;
  align-items: center;
  gap: 9px;
  flex: 1 1 auto;
  min-width: 0;
  text-decoration: none;
  color: var(--ink);
}
.itinerary-glyph {
  flex: 0 0 auto;
  width: 22px;
  height: 14px;
  opacity: 0.7;
}
.itinerary-glyph svg { width: 100%; height: 100%; }
.itinerary-row-title {
  font-family: var(--serif);
  font-size: 16px;
  line-height: 1.3;
  border-bottom: 1px solid transparent;
}
.itinerary-link:hover .itinerary-row-title {
  border-bottom-color: var(--ochre-1);
}
/* Cross-topic bridge marker — same ochre vocabulary as the re-entry
   strip and the map's bridges. */
.itinerary-bridge {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ochre-2);
  line-height: 1;
}
/* State cell — read tick or frontier tag, set by itinerary.js. */
.itinerary-state {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  min-width: 0;
}
.itinerary-tick {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-tertiary);
}
.itinerary-frontier-tag {
  /* Mono tertiary label, no pill — a boxed dashed tag read as a UI
     button, which the creed forbids. Now it sits in the same grammar
     as the territory-chip labels: small mono small-caps, ochre to mark
     it as a frontier/tertiary signal, no border, no fill. */
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ochre-1);
}
.itinerary-row--read .itinerary-row-title {
  color: var(--ink-secondary);
}
/* Resume line — "you were at <last entity>". One quiet line, the
   compact cousin of the home re-entry strip. */
.itinerary-resume {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 7px;
  padding: 8px 0 14px;
  margin: 0 0 8px;
  border-bottom: 1px solid var(--hairline);
}
.itinerary-resume-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.itinerary-resume-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  text-decoration: none;
  color: var(--ink);
}
.itinerary-resume-kind {
  width: 20px;
  height: 13px;
  opacity: 0.7;
}
.itinerary-resume-kind svg { width: 100%; height: 100%; }
.itinerary-resume-title {
  font-family: var(--serif);
  font-size: 15px;
  border-bottom: 1px solid var(--ochre-1);
}
/* "open the map anyway" disclosure. Sits between the itinerary and the
   (mobile-hidden) map-view; opening it reveals the pannable SVG below.
   The map markup already lives in the page — the disclosure only
   chooses which projection is visible, loading nothing new.
   (display + breakpoint handling declared above.) */
.itinerary-map-reveal-summary {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  cursor: pointer;
  list-style: none;
  display: inline-block;
}
.itinerary-map-reveal-summary::-webkit-details-marker { display: none; }
.itinerary-map-reveal-summary:hover { color: var(--ink); }
.itinerary-map-reveal[open] .itinerary-map-reveal-summary {
  color: var(--ink);
}
/* When the map is revealed on mobile, surface the hidden map-view. */
@media (max-width: 1099px) {
  .itinerary-map-reveal[open] ~ .territory-map-view {
    display: block;
  }
}
.territory-header-aside {
  margin: 10px 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
}
.territory-expedition-link {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color 120ms ease, border-color 120ms ease;
  /* keep each link+arrow atomic so the row only ever breaks between
     links — never orphaning a trailing arrow onto its own line */
  white-space: nowrap;
}
.territory-expedition-link:hover {
  color: var(--ink);
  border-bottom-color: var(--ochre-1);
}
.territory-header .breadcrumb {
  margin-bottom: 16px;
  display: flex;
  align-items: baseline;
  gap: 0.5em;
  color: var(--ink-tertiary);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.territory-title {
  font-size: clamp(32px, 2.6vw + 18px, 44px);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.014em;
  margin: 0 0 18px;
}
.territory-pitch {
  max-width: 60ch;
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink-secondary);
  margin: 0 0 8px;
}
.territory-pitch em {
  font-style: italic;
  color: var(--ink);
}
.territory-pitch-bridge {
  color: rgba(160, 95, 40, 0.95);
  font-style: italic;
  border-bottom: 1px dashed rgba(160, 95, 40, 0.55);
  padding-bottom: 1px;
}

/* The toolbar — overlay chips, sits inside .territory-controls. */
.territory-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.territory-chip {
  --chip-color: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px 4px 8px;
  background: transparent;
  border: 1px solid var(--hairline);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--ink-secondary);
  cursor: pointer;
  border-radius: 0;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease, opacity 140ms ease;
}
.territory-chip:hover {
  border-color: var(--ink);
  color: var(--ink);
}
.territory-chip[aria-pressed="true"] {
  color: var(--ink);
  background: rgba(28, 26, 23, 0.04);
}
.territory-chip[aria-pressed="false"] {
  opacity: 0.55;
  border-style: dashed;
}
.territory-chip-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--chip-color);
  display: inline-block;
  flex: none;
}
.territory-chip-dot--frontier {
  background: transparent;
  border: 1.4px dashed var(--ink);
}
/* Fog chip dot — a half-inked circle: one side full, one side
   withheld, reading as "ink in where you've walked". */
.territory-chip-dot--fog {
  background:
    linear-gradient(90deg, var(--ink) 0%, var(--ink) 50%,
      transparent 50%, transparent 100%);
  border: 1px solid var(--ink);
}
.territory-chip-label {
  font-variant: small-caps;
  letter-spacing: 0.04em;
}
.territory-chip-count {
  font-size: 10px;
  color: var(--ink-tertiary);
  font-feature-settings: "tnum";
}

.territory-footnote {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
  margin: 14px 0 32px;
  text-align: center;
}
.territory-footnote kbd {
  font-family: var(--mono);
  font-size: 10px;
  padding: 1px 4px;
  border: 1px solid var(--hairline);
  border-radius: 0;
  background: transparent;
  color: var(--ink);
}
.territory-footnote-bridge {
  color: rgba(160, 95, 40, 0.95);
  font-style: italic;
  border-bottom: 1px dashed rgba(160, 95, 40, 0.6);
  padding-bottom: 1px;
}
.territory-footnote-nav {
  display: inline-block;
  margin-left: 0.5em;
  color: var(--ink-tertiary);
  font-style: italic;
}

/* Human bridges — scholars whose associated_concepts span both
   topics. The corpus's connective tissue made visible at the
   personal level. Each entry: a small vertical person-mark, the
   name, and chip-tags for the topics they cross. */
.territory-human-bridges {
  max-width: 980px;
  margin: 36px auto 12px;
  padding: 22px 0 6px;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.territory-human-bridges-head {
  text-align: center;
  margin-bottom: 18px;
}
.territory-human-bridges-eyebrow {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0 0 6px;
}
.territory-human-bridges-title {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.012em;
  margin: 0 0 8px;
}
.territory-human-bridges-pitch {
  font-family: var(--serif);
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--ink-secondary);
  max-width: 60ch;
  margin: 0 auto;
}
.territory-human-bridges-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 0;
}
.territory-human-bridge {
  margin: 0;
  padding: 14px 12px;
  border-right: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.territory-human-bridge:last-child { border-right: none; }
@media (max-width: 720px) {
  .territory-human-bridge { border-right: none; }
}
.territory-human-bridge-link {
  display: grid;
  grid-template-columns: 40px 1fr;
  gap: 12px;
  align-items: center;
  text-decoration: none;
  color: var(--ink);
}
.territory-human-bridge-mark svg {
  display: block;
  width: 40px;
  height: 96px;
}
.territory-human-bridge-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.territory-human-bridge-name {
  font-family: var(--serif);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.2;
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 4px;
  transition: border-color 120ms ease;
}
.territory-human-bridge-link:hover .territory-human-bridge-name {
  border-bottom-color: var(--ink);
}
.territory-human-bridge-topics {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 2px;
}
.territory-bridge-topic {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.06em;
  color: var(--ink-secondary);
  text-transform: lowercase;
}
.territory-bridge-topic-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--chip-color, var(--ink));
}
.territory-human-bridge-count {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
}

/* Bridge-story caption panel — a paper card that fades in when
   hovering a cross-topic edge. Sits centred below the territory
   map, hairline-only chrome, ochre eyebrow and pair display. */
.territory-bridge-caption {
  max-width: 640px;
  margin: 14px auto 0;
  padding: 14px 22px 16px;
  background: var(--paper);
  border-top: 1px solid rgba(160, 95, 40, 0.5);
  border-bottom: 1px solid rgba(160, 95, 40, 0.5);
  text-align: center;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 220ms ease, transform 220ms ease;
}
.territory-bridge-caption--visible {
  opacity: 1;
  transform: translateY(0);
}
.territory-bridge-caption-eyebrow {
  display: block;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(160, 95, 40, 0.85);
  margin-bottom: 6px;
}
.territory-bridge-caption-pair {
  font-family: var(--serif);
  font-size: 16px;
  font-weight: 500;
  margin: 0 0 8px;
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 12px;
}
.territory-bridge-caption-link {
  font-family: var(--mono);
  font-size: 14px;
  color: rgba(160, 95, 40, 0.85);
  letter-spacing: 0;
}
.territory-bridge-caption-a,
.territory-bridge-caption-b {
  font-style: normal;
}
.territory-bridge-caption-text {
  font-family: var(--serif);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-secondary);
  margin: 0;
  max-width: 56ch;
  margin-left: auto;
  margin-right: auto;
}

/* Territory drawer — slides in from the right when shift+click on
   a concept node. Paper card with hairline edges, fixed width,
   scrollable body. Doesn't replace the page; just floats above. */
.territory-drawer {
  position: fixed;
  top: 56px;
  right: 0;
  bottom: 0;
  width: min(380px, 100vw);
  z-index: 30;
  background: var(--paper);
  border-left: 1px solid var(--ink);
  box-shadow: -20px 0 40px -20px rgba(28, 26, 23, 0.18);
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform 240ms cubic-bezier(0.22, 1, 0.36, 1);
}
.territory-drawer--open {
  transform: translateX(0);
}
.territory-drawer-close {
  position: sticky;
  top: 0;
  display: block;
  margin-left: auto;
  padding: 10px 14px;
  background: var(--paper);
  border: none;
  border-bottom: 1px solid var(--hairline);
  font-family: var(--mono);
  font-size: 16px;
  color: var(--ink);
  cursor: pointer;
  width: 100%;
  text-align: right;
}
.territory-drawer-close:hover {
  color: var(--ink);
  background: rgba(28, 26, 23, 0.04);
}
.territory-drawer-body {
  padding: 18px 22px 40px;
}
.territory-drawer-header {
  margin-bottom: 14px;
}
.territory-drawer-breadcrumb {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0 0 6px;
  display: flex;
  align-items: baseline;
  gap: 0.5em;
}
.territory-drawer-title {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 500;
  line-height: 1.2;
  margin: 0;
}
.territory-drawer-lead {
  font-family: var(--serif);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-secondary);
  margin: 0 0 18px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--hairline);
}
.territory-drawer-section {
  margin-bottom: 18px;
}
.territory-drawer-section-title {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0 0 6px;
  font-weight: 500;
}
.territory-drawer-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.territory-drawer-list li {
  margin: 0;
  padding: 3px 0;
  font-size: 13px;
  line-height: 1.4;
}
.territory-drawer-link {
  text-decoration: none;
  color: var(--ink);
  border-bottom: 1px solid var(--hairline);
  transition: border-color 120ms ease;
}
.territory-drawer-link:hover {
  border-bottom-color: var(--ink);
}
.territory-drawer-kind {
  display: inline-block;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  color: var(--ink-tertiary);
  margin-right: 6px;
}
.territory-drawer-meta {
  color: var(--ink-tertiary);
  font-size: 11.5px;
}
.territory-drawer-footer {
  margin-top: 22px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
  text-align: right;
}
.territory-drawer-deep-link {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
}
.territory-drawer-deep-link:hover {
  border-bottom-color: var(--ink);
}
@media (max-width: 640px) {
  .territory-drawer {
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100vw;
    max-height: 70vh;
    border-left: none;
    border-top: 1px solid var(--ink);
    transform: translateY(100%);
    transition: transform 240ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  .territory-drawer--open { transform: translateY(0); }
}

/* Territory controls — search + toolbar chips in a tight row. */
.territory-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin: 16px 0 12px;
  padding: 8px 0;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.territory-search-input {
  flex: 1 1 240px;
  min-width: 200px;
  background: transparent;
  border: none;
  border-bottom: 1px solid transparent;
  padding: 4px 8px 4px 0;
  font-family: var(--serif);
  font-size: 15px;
  color: var(--ink);
  outline: none;
  transition: border-color 140ms ease;
}
.territory-search-input:focus {
  border-bottom-color: var(--ink);
}
.territory-search-input::placeholder {
  color: var(--ink-tertiary);
  font-style: italic;
}
.territory-search-count {
  font-family: var(--mono);
  font-size: 10px;
  color: rgba(160, 95, 40, 0.78);
  letter-spacing: 0.04em;
  font-style: italic;
  min-width: 8ch;
}

/* Search-paint states. Matches get a halo; misses dim hard. */
.topic-map--territory.searching .map-node--search-miss {
  opacity: 0.14;
  transition: opacity 220ms ease;
}
.topic-map--territory.searching .map-node--search-match {
  opacity: 1;
  filter: drop-shadow(0 0 8px rgba(160, 95, 40, 0.65));
  transition: opacity 220ms ease, filter 220ms ease;
}
.topic-map--territory.searching .map-edge--search-neither {
  stroke-opacity: 0.04 !important;
}
.topic-map--territory.searching .map-edge--search-one {
  stroke-opacity: 0.16 !important;
}
.topic-map--territory.searching .map-edge--search-both {
  stroke-opacity: 0.85 !important;
}

/* Region labels — the two continents named. Italic serif at low
   opacity, with a hairline-curl flanker on each side. Fade in on
   page load so they don't fight for attention. */
.map-region-label {
  opacity: 0;
  animation: region-label-fade 1.4s ease 1.8s both;
}
@keyframes region-label-fade {
  to { opacity: 1; }
}

/* Territory-mode tweaks on the shared .topic-map figure */
.topic-map--territory {
  margin: 0;
  position: relative;
  --cursor-x: 50%;
  --cursor-y: 50%;
}
.topic-map--territory svg {
  background: transparent;
  cursor: grab;
  touch-action: none;
}

/* Cursor flashlight — a radial gradient that warms the local paper
   under the cursor. Fades in when the cursor enters the figure;
   fades out when it leaves. The map "lights up where you look." */
.topic-map--territory .map-flashlight {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  opacity: 0;
  transition: opacity 420ms ease;
  /* Quiet warm halo only — no darkening of the rest of the map.
     The cursor is a presence, not a spotlight. */
  background:
    radial-gradient(circle 180px at var(--cursor-x) var(--cursor-y),
      rgba(255, 240, 210, 0.10) 0%,
      rgba(255, 240, 210, 0.03) 50%,
      transparent 80%);
  mix-blend-mode: normal;
}
.topic-map--territory.has-cursor .map-flashlight {
  opacity: 1;
}

/* Pan + zoom cursor states. The cursor reads "grab" on hover, "grabbing"
   while dragging. Subtle flash on double-click reset. */
.topic-map--territory.panning svg {
  cursor: grabbing;
}
.topic-map--territory .map-node,
.topic-map--territory .map-node a {
  cursor: pointer;
}
.topic-map--territory.reset-flash svg {
  animation: territory-reset-flash 380ms ease-out;
}
@keyframes territory-reset-flash {
  0%   { opacity: 1; }
  40%  { opacity: 0.85; }
  100% { opacity: 1; }
}

/* Minimap navigator — a thumbnail of the full territory in the
   bottom-right of the territory figure. Shows the viewport
   rectangle synchronized with the main map's viewBox. Drag the
   rectangle to pan the main map; click outside the rectangle to
   centre the main map on that point. Paper-card register. */
.map-minimap {
  position: absolute;
  right: 16px;
  bottom: 64px;            /* clear of figcaption */
  z-index: 6;
  padding: 8px 8px 6px;
  background: var(--paper);
  border: 1px solid var(--hairline);
  box-shadow: -4px 4px 14px -6px rgba(28,26,23,0.18);
  font-family: var(--mono);
  user-select: none;
}
.map-minimap-eyebrow {
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-bottom: 4px;
  text-align: center;
}
.map-minimap-svg {
  display: block;
  cursor: pointer;
}
.map-minimap-viewport {
  cursor: grab;
  transition: stroke 140ms ease;
}
.map-minimap.dragging .map-minimap-viewport {
  cursor: grabbing;
  stroke: rgba(160, 95, 40, 1);
  stroke-width: 8;
}
@media (max-width: 720px) {
  .map-minimap { display: none; }
  /* Give the territory map proper vertical room on small screens —
     a 1640×1080 viewBox scaled to width 343 gives only ~225px tall
     by default; glyphs become unreadable. min-height ensures the
     map fills the visible area. Pan/zoom still work for detail. */
  .topic-map--territory svg {
    min-height: 70vh;
  }
  /* Insets get a smaller bump so they stay subordinate to the
     prose, but bigger than the default aspect-ratio render. */
  .concept-territory-inset svg,
  .entity-territory-inset svg,
  .source-territory-inset svg,
  .quest-territory-inset svg {
    min-height: 50vh;
  }
}

/* Topic-coloured ring around each node in territory mode. Fades to
   secondary opacity when its topic is filtered out. */
.topic-map--territory .map-node-ring {
  transition: stroke-opacity 220ms ease;
}

/* Topic-filter dimming. When the page has classes like .filter-out-
   chartered-violence on the territory figure, hide those nodes. */
.topic-map--territory .map-node[data-topic-id].map-node--filtered-out {
  opacity: 0.07;
  pointer-events: none;
}
.topic-map--territory .map-edge--filtered-out {
  stroke-opacity: 0.03 !important;
}

/* Territory composition — when the page loads, the corpus draws
   itself: intra-topic edges trace in stroke-by-stroke staggered
   left-to-right; cross-topic bridges fade in last; nodes bloom
   into place shortly after their first edge appears. Reads like
   a draftsman composing the territory in front of you. */

/* Intra-topic edges: draw in via pathLength + dashoffset. The
   delay is set per-edge via --edge-anim-delay (computed at build
   time from each edge's position in the topic-ordered list). */
.topic-map--territory .map-edge--composing {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: territory-edge-draw 0.7s cubic-bezier(0.22, 1, 0.36, 1) var(--edge-anim-delay, 0s) forwards;
}
@keyframes territory-edge-draw {
  to { stroke-dashoffset: 0; }
}

/* Cross-topic edges: fade in via opacity (preserves the dashed
   look) with the same per-edge delay. They land last because the
   stagger places them at the end of the order. */
.topic-map--territory .map-edge--cross-topic {
  opacity: 0;
  animation: territory-edge-fade 0.7s ease-out var(--edge-anim-delay, 1.2s) forwards;
}
@keyframes territory-edge-fade {
  to { opacity: 1; }
}

/* Nodes: scale + fade in with a per-node delay shortly after their
   first edge gets drawn. The topic-coloured ring and the glyph
   come together in one motion. */
.topic-map--territory .map-node--composing {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: territory-node-arrive 0.55s cubic-bezier(0.22, 1, 0.36, 1) var(--node-anim-delay, 0s) forwards;
}
@keyframes territory-node-arrive {
  0%   { opacity: 0; }
  60%  { opacity: 1; }
  100% { opacity: 1; }
}
.topic-map--territory.bridges-only .map-edge:not(.map-edge--cross-topic) {
  stroke-opacity: 0.05 !important;
}
.topic-map--territory.bridges-only .map-node:not(.map-node--bridge-endpoint) {
  opacity: 0.22;
}
.map-legend-item--bridge .map-legend-dot--bridge {
  background: rgba(160, 95, 40, 0.55);
  border-radius: 50%;
  width: 7px;
  height: 7px;
  display: inline-block;
}
.map-legend-item--bridge {
  color: rgba(160, 95, 40, 0.72);
  font-style: italic;
}
.territory-chip-dot--bridge {
  background: rgba(160, 95, 40, 0.85);
}
.territory-chip--bridges[aria-pressed="true"] {
  border-color: rgba(160, 95, 40, 0.85);
  color: rgba(120, 70, 28, 1);
}

/* Pinned indicator — slim ochre chip that appears in the toolbar
   when a node is pinned. Shows the pinned concept's title; the ×
   button unpins. */
.territory-pinned-indicator {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  padding: 4px 8px 4px 12px;
  margin-left: auto;
  background: rgba(160, 95, 40, 0.06);
  border: 1px solid rgba(160, 95, 40, 0.55);
  font-family: var(--mono);
  font-size: 11px;
}
.territory-pinned-indicator[hidden] { display: none; }
.territory-pinned-indicator-label {
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(160, 95, 40, 0.9);
}
.territory-pinned-indicator-title {
  font-family: var(--serif);
  font-style: italic;
  font-size: 13px;
  color: var(--ink);
}
.territory-pinned-unpin {
  background: transparent;
  border: none;
  font-family: var(--mono);
  font-size: 14px;
  color: rgba(160, 95, 40, 0.7);
  cursor: pointer;
  padding: 0 2px;
  line-height: 1;
}
.territory-pinned-unpin:hover {
  color: var(--ink);
}

/* Pinned node visual: a steady warm-ochre ring outside the topic
   ring, plus a small filled circle in the upper-right corner of
   the node (the "pin head"). Slightly steadier than the breathing
   frontier ring — it's not asking for attention, just holding it. */
.topic-map .map-node--pinned .map-node-bg {
  stroke: rgba(160, 95, 40, 0.9);
  stroke-width: 1.4;
}
.topic-map .map-node--pinned::after { /* SVG can't use ::after, kept for parity */ }
.topic-map .map-node--pinned .map-node-glyph {
  filter: drop-shadow(0 0 8px rgba(160, 95, 40, 0.4));
}

/* When a node is pinned, the neighbourhood-highlight effect is the
   same as during hover (.map--exploring takes care of dimming/
   brightening). Just don't fade the pinned node itself even when
   moving the cursor away. */
.topic-map.has-pinned.map--exploring .map-node--pinned {
  opacity: 1;
}

/* Quest paths chip — small striped dot signalling "multiple curated
   paths overlay". Active state picks up a teal-leaning ink. */
.territory-chip-dot--paths {
  background:
    linear-gradient(135deg,
      hsl(20 35% 40%) 0%, hsl(20 35% 40%) 33%,
      hsl(160 30% 36%) 33%, hsl(160 30% 36%) 66%,
      hsl(260 30% 40%) 66%, hsl(260 30% 40%) 100%);
}
.territory-chip--paths[aria-pressed="true"] {
  border-color: var(--ink);
  color: var(--ink);
}

/* Quest path stroke animation — draws in over 1.4s on first appearance. */
@keyframes quest-path-draw {
  to { stroke-dashoffset: 0; }
}

/* Route chip — your own walk. Its dot is a dotted hairline ring in
   tertiary ink, echoing the dotted route stroke so the chip reads as
   "your record" rather than the coloured quest paths or the umber
   trail. */
.territory-chip-dot--route {
  background: transparent;
  border: 1px dotted var(--ink-tertiary);
  border-radius: 50%;
}
.territory-chip--route[aria-pressed="true"] {
  border-color: var(--ink);
  color: var(--ink);
}
.territory-chip--route[aria-pressed="true"] .territory-chip-dot--route {
  border-color: var(--ink-secondary);
}

/* Path-finder mode — territory becomes a route-tracer. The chip dot
   gets a small directional mark. While pathfinding is active, the
   cursor reads 'crosshair' over the map; nodes look 'selectable'. */
.territory-chip-dot--pathfind {
  background: transparent;
  border: 1.4px solid var(--ink);
  border-radius: 50%;
  position: relative;
}
.territory-chip-dot--pathfind::after {
  content: '';
  position: absolute;
  inset: 2.5px;
  border-radius: 50%;
  background: rgba(160, 95, 40, 0.85);
}
.territory-chip--pathfind[aria-pressed="true"] {
  border-color: rgba(160, 95, 40, 0.95);
  color: rgba(120, 70, 28, 1);
  background: rgba(160, 95, 40, 0.06);
}
.topic-map--territory.pathfinding svg {
  cursor: crosshair;
}
.topic-map--territory.pathfinding .map-node {
  cursor: crosshair;
}

/* First-visit welcome card — paper card centered on the territory
   that explains what the map is. Shown once per browser; dismissed
   by × or by the 'got it' button. */
.territory-welcome {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -52%) scale(0.96);
  z-index: 40;
  width: min(440px, 92vw);
  padding: 26px 30px 22px;
  background: var(--paper);
  border: 1px solid var(--ink);
  box-shadow: 0 24px 60px -16px rgba(28, 26, 23, 0.25);
  opacity: 0;
  transition: opacity 280ms ease, transform 280ms cubic-bezier(0.22, 1, 0.36, 1);
}
.territory-welcome--visible {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
.territory-welcome-close {
  position: absolute;
  top: 8px;
  right: 12px;
  background: transparent;
  border: none;
  font-family: var(--mono);
  font-size: 18px;
  color: var(--ink-tertiary);
  cursor: pointer;
  line-height: 1;
  padding: 4px 6px;
}
.territory-welcome-close:hover { color: var(--ink); }
.territory-welcome-eyebrow {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(160, 95, 40, 0.95);
  font-style: italic;
  margin: 0 0 6px;
}
.territory-welcome-h {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.01em;
  margin: 0 0 12px;
  color: var(--ink);
}
.territory-welcome-text {
  font-family: var(--serif);
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--ink-secondary);
  margin: 0 0 14px;
}
.territory-welcome-text em { font-style: italic; color: var(--ink); }
.territory-welcome-fog-note {
  font-size: 13px;
  color: var(--ink-tertiary);
  border-top: 1px solid var(--hairline);
  padding-top: 12px;
  margin-bottom: 0;
}
.territory-welcome-bridge {
  color: rgba(160, 95, 40, 0.95);
  border-bottom: 1px dashed rgba(160, 95, 40, 0.6);
  padding-bottom: 1px;
  font-style: italic;
}
.territory-welcome-gestures {
  list-style: none;
  margin: 0 0 18px;
  padding: 14px 0 0;
  border-top: 1px solid var(--hairline);
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-secondary);
  line-height: 1.8;
}
.territory-welcome-gestures li {
  margin: 0;
}
.territory-welcome-key {
  font-family: var(--mono);
  font-size: 10.5px;
  background: rgba(28, 26, 23, 0.06);
  padding: 1px 5px;
  border: 1px solid var(--hairline);
  margin-right: 6px;
  color: var(--ink);
}
.territory-welcome-foot {
  margin: 14px 0 0;
  text-align: right;
}
.territory-welcome-dismiss {
  background: transparent;
  border: 1px solid var(--ink);
  padding: 6px 14px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink);
  cursor: pointer;
  letter-spacing: 0.04em;
  font-style: italic;
}
.territory-welcome-dismiss:hover {
  background: var(--ink);
  color: var(--paper);
}

/* Pathfind animated trace + node states */
@keyframes pathfind-draw {
  to { stroke-dashoffset: 0; }
}
.map-node--pathfind-start-pending .map-node-bg {
  stroke: rgba(160, 95, 40, 0.9);
  stroke-width: 2;
  stroke-dasharray: 3 2;
}
.map-node--pathfind-start .map-node-bg,
.map-node--pathfind-end .map-node-bg {
  stroke: rgba(160, 95, 40, 0.95);
  stroke-width: 2.2;
}
.map-node--pathfind-via .map-node-bg {
  stroke: rgba(160, 95, 40, 0.55);
  stroke-width: 1.4;
}

/* The result panel below the toolbar */
.territory-pathfind-panel {
  max-width: 760px;
  margin: 14px auto 8px;
  padding: 12px 18px 14px;
  border-top: 1px solid rgba(160, 95, 40, 0.5);
  border-bottom: 1px solid rgba(160, 95, 40, 0.5);
  background: var(--paper);
}
.territory-pathfind-hint {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: rgba(160, 95, 40, 0.95);
  font-style: italic;
  text-align: center;
}
.territory-pathfind-hint em {
  font-style: normal;
  color: var(--ink);
}
.territory-pathfind-result {
  margin-top: 8px;
}
.territory-pathfind-eyebrow {
  font-family: var(--serif);
  font-size: 16px;
  text-align: center;
  margin: 0 0 8px;
  color: var(--ink);
}
.territory-pathfind-eyebrow-mark {
  font-family: var(--mono);
  color: rgba(160, 95, 40, 0.95);
  margin-right: 4px;
}
.territory-pathfind-eyebrow em {
  font-style: italic;
}
.territory-pathfind-steps {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 14px;
  font-family: var(--mono);
  font-size: 11px;
}
.territory-pathfind-step {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
}
.territory-pathfind-step-num {
  font-size: 9px;
  color: var(--ink-tertiary);
  letter-spacing: 0.08em;
}
.territory-pathfind-step-link {
  font-family: var(--serif);
  font-size: 13px;
  text-decoration: none;
  color: var(--ink);
  border-bottom: 1px solid var(--hairline);
}
.territory-pathfind-step-link:hover {
  border-bottom-color: var(--ink);
}
.territory-pathfind-cross {
  color: rgba(160, 95, 40, 0.95);
  font-size: 12px;
  margin-left: 2px;
}
.map-quest-paths {
  pointer-events: none;  /* let the path's own pointer-events:stroke handle */
}
.map-quest-path {
  cursor: pointer;
  transition: stroke-opacity 160ms ease, stroke-width 160ms ease;
}

/* Tooltip that appears when hovering a quest path. Paper card,
   positioned near the cursor via JS. */
.map-quest-path-tooltip {
  position: fixed;
  bottom: 20%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  padding: 10px 18px;
  background: var(--paper);
  border-top: 1px solid var(--accent, var(--ink));
  border-bottom: 1px solid var(--accent, var(--ink));
  font-family: var(--mono);
  font-size: 11px;
  text-align: center;
  display: none;
  pointer-events: none;
}
.map-quest-path-tooltip.visible {
  display: block;
}
.map-quest-path-tooltip-eyebrow {
  display: block;
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent, var(--ink-tertiary));
  margin-bottom: 4px;
}
.map-quest-path-tooltip-title {
  display: block;
  font-family: var(--serif);
  font-style: italic;
  font-size: 16px;
  color: var(--ink);
  margin-bottom: 4px;
}
.map-quest-path-tooltip-meta {
  display: block;
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-tertiary);
}

/* Frontier — unread concepts that touch a visited one. Three layers
   of signal: a dashed backing stroke; a small dashed-circle sigil
   in the upper-left corner of the node; and a breathing animation
   so the eye catches them as "live next steps". */
.topic-map--territory .map-node-frontier-sigil {
  opacity: 0;
  transition: opacity 220ms ease;
}
.topic-map--territory .map-node--frontier .map-node-frontier-sigil {
  opacity: 1;
}
.topic-map--territory .map-node--frontier .map-node-bg {
  stroke-dasharray: 2 2;
  stroke: rgba(28, 26, 23, 0.55);
}
.topic-map--territory .map-node--frontier .map-node-glyph {
  opacity: 0.75;
}

/* When the frontier filter is off, return to plain unread state. */
.topic-map--territory.frontier-off .map-node--frontier .map-node-bg {
  stroke-dasharray: none;
  stroke: rgba(28, 26, 23, 0.25);
}
.topic-map--territory.frontier-off .map-node-frontier-sigil {
  opacity: 0;
}

/* Trail overlay — the user's last ~6 visits drawn as a path on the
   territory. Feels like a worn pencil-line tracking a recent walk;
   the head (most recent) is marked with a small text label "here".
   Strokes draw on first appearance via pathLength=1 + dashoffset. */
.map-trail-path {
  stroke: rgba(140, 75, 50, 0.88);  /* warm umber */
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: map-trail-draw 1.6s cubic-bezier(0.22, 1, 0.36, 1) 0.2s both;
  mix-blend-mode: multiply;
}
@keyframes map-trail-draw {
  to { stroke-dashoffset: 0; }
}
.map-trail-dot {
  fill: rgba(140, 75, 50, 0.85);
  stroke: var(--paper);
  stroke-width: 0.8;
  opacity: 0;
  animation: map-trail-dot-in 360ms ease 1.4s forwards;
}
@keyframes map-trail-dot-in {
  to { opacity: 1; }
}
.map-trail-dot--head {
  fill: rgba(140, 75, 50, 0.95);
  stroke: var(--paper);
  stroke-width: 1.2;
  animation: map-trail-head-pulse 2.2s ease-in-out 1.7s infinite;
}
@keyframes map-trail-head-pulse {
  0%, 100% { transform: scale(1);    opacity: 0.95; }
  50%      { transform: scale(1.18); opacity: 1; }
}
.map-trail-head-label {
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  fill: rgba(140, 75, 50, 0.9);
  opacity: 0;
  animation: map-trail-dot-in 360ms ease 1.6s forwards;
}

/* Route overlay — the reader's whole walk through the corpus, in
   journey order. Deliberately quieter and plainer than the trail
   overlay (warm umber, bowed, automatic) and the quest paths
   (coloured, splined): a dotted hairline in tertiary ink that reads
   as a survey line drawn over the map. Survives the fog. */
.map-route-overlay {
  pointer-events: none;
}
.map-route-path {
  stroke: var(--ink-tertiary);
  stroke-width: 1;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 1.5 3;
}
.map-route-dot {
  fill: var(--ink-tertiary);
  stroke: var(--paper);
  stroke-width: 0.6;
}
.map-route-dot--start,
.map-route-dot--end {
  fill: var(--ink-secondary);
  stroke-width: 0.9;
}

/* Pulse animation on the frontier ring — extremely subtle. */
@keyframes territory-frontier-breathe {
  0%, 100% { stroke-opacity: 0.30; }
  50%      { stroke-opacity: 0.55; }
}
.topic-map--territory:not(.frontier-off) .map-node--frontier .map-node-bg {
  animation: territory-frontier-breathe 4.2s ease-in-out infinite;
}
@keyframes territory-sigil-pulse {
  0%, 100% { transform: translate(0, 0); opacity: 0.85; }
  50%      { transform: translate(-1px, -1px); opacity: 1; }
}
.topic-map--territory:not(.frontier-off) .map-node--frontier .map-node-frontier-sigil {
  transform-box: fill-box;
  transform-origin: center;
  animation: territory-sigil-pulse 2.6s ease-in-out infinite;
}

/* Fog of war — the territory as a record of the reader's own walk.
   Engaged only when .fog-on is present (paintMap decides: a stored
   'fog' preference, or the visited-count default). Three perceptual
   tiers replace the flat read/unread split: explored inks in at full
   strength, frontier sits between, terra incognita recedes into haze
   with its label withheld. A fogged concept is never sealed off —
   hover reveals its name and search lifts the fog off any match
   (rules at the foot of this block), and the link underneath still
   navigates. Hairlines only; ink/paper tokens carry both themes. */

/* Explored — visited. Already at full ink via .map-node--visited;
   under fog we let it breathe a touch brighter than its surroundings
   by leaving it untouched (the contrast comes from fogging the rest). */

/* Frontier tier — unread, one hop from somewhere walked. Dimmer than
   explored, clearer than deep fog. The dashed frontier ring + sigil
   from the frontier feature already mark these; here we just hold the
   glyph and label at a middle legibility. */
.topic-map--territory.fog-on .map-node--frontier-tier .map-node-glyph {
  opacity: 0.6;
}
.topic-map--territory.fog-on .map-node--frontier-tier .map-node-label {
  fill: rgba(28, 26, 23, 0.5);
}
[data-theme="dark"] .topic-map--territory.fog-on .map-node--frontier-tier .map-node-label {
  fill: rgba(232, 223, 201, 0.5);
}

/* Terra incognita — unread, no walked neighbour. The deep fog: the
   node stroke thins almost to nothing, the glyph hazes back, and the
   label is withheld (opacity near zero — still in the DOM so hover
   and search can recall it, and screen readers still reach it). */
.topic-map--territory.fog-on .map-node--terra-incognita .map-node-bg {
  stroke: rgba(28, 26, 23, 0.07);
}
.topic-map--territory.fog-on .map-node--terra-incognita .map-node-glyph {
  opacity: 0.16;
}
.topic-map--territory.fog-on .map-node--terra-incognita .map-node-label {
  opacity: 0.06;
}
.topic-map--territory.fog-on .map-node--terra-incognita .map-node-ring {
  stroke-opacity: 0.1;
}
[data-theme="dark"] .topic-map--territory.fog-on .map-node--terra-incognita .map-node-bg {
  stroke: rgba(232, 223, 201, 0.09);
}

/* A traveller can always ask directions: hovering or focusing a
   fogged node recalls its label and inks its glyph back in. The plain
   .map-node:hover rules already lift glyph + label fill; this restores
   the label's withheld *opacity* on the terra-incognita tier. */
.topic-map--territory.fog-on .map-node--terra-incognita:hover .map-node-label,
.topic-map--territory.fog-on .map-node--terra-incognita:focus-within .map-node-label {
  opacity: 1;
}
.topic-map--territory.fog-on .map-node--terra-incognita:hover .map-node-glyph,
.topic-map--territory.fog-on .map-node--terra-incognita:focus-within .map-node-glyph {
  opacity: 1;
}

/* Search lifts the fog off any match — a searched-for concept inks
   fully back in regardless of tier, so the map stays a discovery tool
   under fog. (Misses are already dimmed hard by the search-paint
   rules above; this overrides the withheld terra-incognita label so a
   matched-but-fogged concept reads.) */
.topic-map--territory.fog-on.searching .map-node--search-match .map-node-label {
  opacity: 1;
}
.topic-map--territory.fog-on.searching .map-node--search-match .map-node-glyph {
  opacity: 1;
}
.topic-map--territory.fog-on.searching .map-node--search-match .map-node-bg {
  stroke: var(--ink);
}

/* Legend: the explored / frontier / unsurveyed tier items belong to
   the fogged map; the plain read / unread pair belongs to the
   un-fogged one. Show whichever set matches the current mode. The
   fog-tier items are hidden by default (per-topic maps, un-fogged
   territory) and the tier dots take their colour from the tier ink. */
.map-legend-item--explored,
.map-legend-item--frontier-tier,
.map-legend-item--terra {
  display: none;
}
.topic-map--territory.fog-on .map-legend-item--unread,
.topic-map--territory.fog-on .map-legend-item--visited {
  display: none;
}
.topic-map--territory.fog-on .map-legend-item--explored,
.topic-map--territory.fog-on .map-legend-item--frontier-tier,
.topic-map--territory.fog-on .map-legend-item--terra {
  display: inline-flex;
}
.map-legend-item--explored .map-legend-dot {
  background: var(--ink);
  border-color: var(--ink);
}
.map-legend-item--frontier-tier .map-legend-dot {
  border: 1px dashed currentColor;
  background: transparent;
}
.map-legend-item--terra .map-legend-dot {
  opacity: 0.28;
}

/* ----------------- index-page toggle ----------------- */
.kind-index-toggle {
  display: inline-flex;
  align-items: stretch;
  margin-top: 8px;
  border: 1px solid var(--hairline);
}
.kind-index-toggle-btn {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-secondary);
  background: transparent;
  border: none;
  border-right: 1px solid var(--hairline);
  padding: 7px 14px;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.kind-index-toggle-btn:last-child { border-right: none; }
.kind-index-toggle-btn:hover {
  color: var(--ink);
  background: rgba(28, 26, 23, 0.04);
}
.kind-index-toggle-btn[aria-pressed="true"] {
  background: var(--ink);
  color: var(--paper);
}

/* Show only the currently-active view. Default: flat. */
.kind-index-view { display: none; }
.kind-index[data-active-view="flat"]    .kind-index-view--flat,
.kind-index[data-active-view="grouped"] .kind-index-view--grouped {
  display: block;
}
/* No-JS fallback: the first view rendered (flat) shows by default
   because we put data-active-view="flat" on the wrapper at init time. */
.kind-index:not([data-active-view]) .kind-index-view--flat {
  display: block;
}

.topic-section { margin-top: var(--section-gap); }
.topic-section h2 {
  font-size: 16px;
  font-weight: 600;
  margin: 0 0 20px;
  display: flex;
  align-items: baseline;
  gap: 0.4em;
  letter-spacing: 0.01em;
}
.entity-list { list-style: none; padding: 0; margin: 0; }
/* Two-line row: title dominates on the first line, meta sits below
   in mono. The grid lets us keep the kind-mark in a tiny left gutter
   when present, and lets the title span its own row even when long. */
.entity-row {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: auto auto;
  align-items: baseline;
  column-gap: 12px;
  row-gap: 2px;
  padding: 12px 12px 12px 10px;
  margin-left: -10px;
  margin-right: -12px;
  border-bottom: 1px solid var(--hairline);
  font-size: 16px;
  line-height: 1.3;
  transition: background 140ms ease;
}
.entity-row:hover {
  background: rgba(28, 26, 23, 0.025);
}
.entity-row > .kind-mark {
  grid-column: 1;
  grid-row: 1;
  font-size: 9px;
  letter-spacing: 0.08em;
  color: var(--ink-tertiary);
  opacity: 0.5;
  transition: opacity 140ms ease, color 140ms ease;
}
.entity-row:hover > .kind-mark {
  opacity: 0.95;
  color: var(--ink-secondary);
}
.entity-row > a,
.entity-row > .note-title-and-lead {
  grid-column: 2;
  grid-row: 1;
  font-family: var(--serif);
  color: var(--ink);
  font-weight: 500;
  line-height: 1.3;
}
.entity-row a { text-decoration: none; }
.entity-row a:hover {
  text-decoration: underline;
  text-decoration-color: rgba(160, 95, 40, 0.55);
  text-underline-offset: 3px;
}
.entity-row.unvetted {
  position: relative;
  box-shadow: inset 2px 0 0 rgba(122, 46, 42, 0.55);
}
.entity-row.unvetted .row-meta { color: var(--ink-tertiary); }
.row-meta {
  grid-column: 2;
  grid-row: 2;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-tertiary);
  letter-spacing: 0.02em;
}

/* ============================================================
   source page — reads like a bibliographic record / citation card
   ============================================================ */

/* source header is a tighter, citation-card register. The breadcrumb
   replaces SRC kind-mark with a § glyph and a mono "source" label. */
.source-header .breadcrumb {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-secondary);
}
.source-header .breadcrumb .section-glyph {
  color: var(--ink-tertiary);
  font-family: var(--serif);
  font-size: 14px;
  margin: 0;
  line-height: 1;
}
.source-header .source-kind {
  color: var(--ink-secondary);
}

/* dominant author·year top line — big mono caps, letter-spaced */
.source-author-year {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 12px;
  line-height: 1.4;
}
.source-author-year .year {
  font-family: var(--serif);
  font-weight: 500;
  font-style: italic;
  font-size: 17px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  vertical-align: baseline;
}
.source-author-year .sep {
  color: var(--ink-tertiary);
  font-weight: 400;
  margin: 0 0.15em;
}
.source-author-year a.xref {
  color: inherit;
  text-decoration-color: var(--ink-tertiary);
  text-underline-offset: 3px;
}
.source-author-year a.xref:hover {
  text-decoration-color: var(--ink);
}

/* quoted, italic-serif title block — the heart of the citation card */
.source-quoted-title {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(28px, 2.2vw + 14px, 38px);
  line-height: 1.22;
  letter-spacing: -0.005em;
  margin: 0 0 22px;
  color: var(--ink);
  text-indent: -0.35em;
  padding-left: 0.35em;
}
.source-quoted-title::before {
  content: "\201C";
  color: var(--ink-tertiary);
  font-style: normal;
  margin-right: 0.04em;
}
.source-quoted-title::after {
  content: "\201D";
  color: var(--ink-tertiary);
  font-style: normal;
  margin-left: 0.04em;
}

/* dense citation strip — paired key/value cells separated by vertical
   hairlines. Wraps onto multiple rows on narrow viewports. */
.source-citation {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  margin: 0 0 4px;
  padding: 0;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.5;
}
.source-citation-pair {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 10px 14px;
  border-right: 1px solid var(--hairline);
  min-width: 0;
}
.source-citation-pair:last-child { border-right: none; }
.source-citation-pair:first-child { padding-left: 0; }
.source-citation dt {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  flex-shrink: 0;
}
.source-citation dd {
  margin: 0;
  min-width: 0;
}
.source-citation-val {
  font-family: var(--serif);
  font-style: normal;
  font-size: 14px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  line-height: 1.4;
}
.source-citation-val.source-citation-url,
.source-citation dd a {
  word-break: break-all;
}
.source-citation dd a.xref {
  color: var(--ink);
}

/* narrow-viewport fallback: stack pairs vertically, drop vertical
   hairlines in favour of horizontal ones */
@media (max-width: 720px) {
  .source-citation {
    flex-direction: column;
  }
  .source-citation-pair {
    border-right: none;
    border-bottom: 1px solid var(--hairline);
    padding: 8px 0;
  }
  .source-citation-pair:last-child { border-bottom: none; }
  .source-citation-pair:first-child { padding-top: 10px; }
}

.excerpt {
  margin: 24px 0 0;
  padding: 0 0 0 2ch;
  border-left: 1px solid var(--ink-tertiary);
  font-style: italic;
  font-size: 17px;
  line-height: 1.5;
  color: var(--ink);
}
/* source header's excerpt: a touch quieter, sits just under the
   citation strip so it reads as part of the citation card */
.source-header-excerpt {
  margin-top: 20px;
  color: var(--ink-secondary);
  font-size: 16px;
}
.unvetted-note {
  font-family: var(--mono);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-tertiary);
  padding: 24px 0;
}
/* Inline italic tag in the inbox "state" column — the curator's
   wry one-line note on an unvetted source ("skimmed in airport",
   "bought on a recommendation, never opened"). Reads as marginalia
   alongside the bare em-dash. */
.unvetted-tag {
  font-family: var(--serif);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-tertiary);
  letter-spacing: 0;
}

/* ============================================================
   home & inbox tables (dense register)
   ============================================================ */
.home, .inbox { max-width: 980px; }

/* ----------------- /journal — in-progress thinking ----------------- */
.journal-page-body main { max-width: 780px; margin: 0 auto; padding: 0 16px; }
.journal-page { padding: 24px 0 80px; }
.journal-head {
  padding: 18px 0 28px;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 36px;
}
.journal-head h1 {
  font-size: clamp(32px, 2.6vw + 16px, 42px);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.014em;
  margin: 12px 0 18px;
}
.journal-lead {
  max-width: 60ch;
  font-family: var(--serif);
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--ink-secondary);
  margin: 0;
}

.journal-entries {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.journal-entry {
  position: relative;
  padding: 32px 0 36px;
  border-bottom: 1px solid var(--hairline);
}
.journal-entry:last-child { border-bottom: none; }
.journal-entry-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 14px;
  font-family: var(--mono);
}
.journal-entry-date {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  margin: 0;
  color: var(--ink-tertiary);
  font-size: 12px;
  letter-spacing: 0.04em;
}
.journal-entry-day {
  font-size: 22px;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: 0;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.journal-entry-month {
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--ink-secondary);
  text-transform: uppercase;
  font-feature-settings: 'tnum';
}
.journal-entry-year {
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--ink-tertiary);
  font-feature-settings: 'tnum';
  margin-left: 4px;
}
.journal-entry-num {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0;
}

.journal-entry-body {
  font-family: var(--serif);
  font-size: 16px;
  line-height: 1.65;
  color: var(--ink);
  max-width: 64ch;
}
.journal-entry-body p {
  margin: 0 0 14px;
}
.journal-entry-body p:last-child { margin-bottom: 0; }
.journal-entry-body em { font-style: italic; }
.journal-entry-body strong { font-weight: 600; }

.journal-entry-refs {
  margin: 18px 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 10px;
  padding-top: 12px;
  border-top: 1px dotted var(--hairline);
}
.journal-ref {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  text-decoration: none;
  color: var(--ink-secondary);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.02em;
  padding: 2px 6px;
  border: 1px solid var(--hairline);
  transition: border-color 120ms ease, color 120ms ease;
}
.journal-ref:hover {
  border-color: var(--ink);
  color: var(--ink);
}
.journal-ref-kind {
  font-size: 9px;
  letter-spacing: 0.12em;
  color: var(--ink-tertiary);
  text-transform: uppercase;
}
/* When the journal-ref-kind cell holds a glyph (via kind-mark),
   keep it inline-sized to match the surrounding text. */
.journal-ref-kind.kind-mark--glyph {
  width: 11px;
  height: 11px;
  margin: 0 4px 0 0;
}
.journal-empty {
  font-family: var(--serif);
  font-style: italic;
  color: var(--ink-tertiary);
  padding: 32px 0;
  text-align: center;
}

/* ----------------- expedition: the reader's field log ----------------- */
/* Client-rendered. Mirrors the journal's date grammar — date in mono
   in the margin, waypoints with kind-glyphs + linked titles — but
   reads as the reader's own walk rather than the author's notes. */
.expedition-page-body main { max-width: 780px; margin: 0 auto; padding: 0 16px; }
.expedition-page { padding: 24px 0 80px; }
.expedition-head {
  padding: 18px 0 24px;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 32px;
}
.expedition-head h1 {
  font-size: clamp(32px, 2.6vw + 16px, 42px);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.014em;
  margin: 12px 0 18px;
}
.expedition-lead {
  max-width: 60ch;
  font-family: var(--serif);
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--ink-secondary);
  margin: 0;
}
.expedition-summary {
  margin: 16px 0 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.expedition-empty {
  font-family: var(--serif);
  font-style: italic;
  color: var(--ink-tertiary);
  padding: 40px 0;
  text-align: center;
}
.expedition-day {
  display: grid;
  grid-template-columns: 9ch 1fr;
  gap: 0 24px;
  padding: 22px 0 24px;
  border-bottom: 1px solid var(--hairline);
}
.expedition-day:last-child { border-bottom: none; }
.expedition-date {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  margin: 0;
  font-family: var(--mono);
  position: sticky;
  top: 16px;
  align-self: start;
}
.expedition-date-day {
  font-size: 22px;
  font-weight: 600;
  color: var(--ink);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.expedition-date-month {
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--ink-secondary);
  text-transform: uppercase;
  font-feature-settings: 'tnum';
  margin-top: 3px;
}
.expedition-date-year {
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--ink-tertiary);
  font-feature-settings: 'tnum';
  margin-top: 1px;
}
.expedition-waypoints {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.expedition-waypoint {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.expedition-time {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
  min-width: 5ch;
}
.expedition-link {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  text-decoration: none;
  color: var(--ink);
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 1px;
  font-family: var(--serif);
  font-size: 15px;
  transition: border-color 120ms ease, color 120ms ease;
}
.expedition-link:hover {
  border-bottom-color: var(--ochre-2);
}
.expedition-kind {
  display: inline-flex;
  align-items: center;
  color: var(--ink-secondary);
}
.expedition-kind.kind-mark--glyph {
  width: 13px;
  height: 13px;
  position: relative;
  top: 1px;
}
.expedition-again {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  flex-shrink: 0;
}
.expedition-waypoint--again .expedition-link {
  color: var(--ink-secondary);
  border-bottom-color: transparent;
}
@media (max-width: 600px) {
  .expedition-day {
    grid-template-columns: 1fr;
    gap: 10px 0;
  }
  .expedition-date {
    flex-direction: row;
    align-items: baseline;
    gap: 6px;
    text-align: left;
    position: static;
  }
}

/* ----------------- home: recent activity ----------------- */
.home-activity {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  margin: 48px -20px 32px;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.home-activity-col {
  padding: 20px 22px 24px;
  border-right: 1px solid var(--hairline);
}
.home-activity-col:last-child { border-right: none; }
.home-activity-h {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  font-weight: 500;
  margin: 0 0 14px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.home-activity-glyph {
  display: inline-flex;
  width: 14px; height: 14px;
  color: var(--ink-secondary);
}
.home-activity-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.home-activity-item { margin: 0; }
.home-activity-link {
  display: grid;
  grid-template-columns: 42px 1fr;
  gap: 12px;
  align-items: baseline;
  text-decoration: none;
  color: inherit;
}
.home-activity-link:hover .home-activity-text {
  color: var(--ink);
}
.home-activity-date {
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.home-activity-mo {
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  font-weight: 400;
  margin-left: 2px;
}
.home-activity-text {
  font-family: var(--serif);
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-secondary);
  transition: color 140ms ease;
}
.home-activity-sources { gap: 8px; }
.home-activity-more {
  margin: 14px 0 0;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-tertiary);
  font-style: italic;
}
.home-activity-more a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(160, 95, 40, 0.4);
  padding-bottom: 1px;
  transition: color 120ms ease, border-color 120ms ease;
}
.home-activity-more a:hover { border-bottom-color: rgba(160, 95, 40, 0.9); color: rgba(120, 70, 28, 1); }
@media (max-width: 720px) {
  .home-activity {
    grid-template-columns: 1fr;
    margin-left: 0;
    margin-right: 0;
  }
  .home-activity-col {
    border-right: none;
    border-bottom: 1px solid var(--hairline);
  }
  .home-activity-col:last-child { border-bottom: none; }
}

/* ----------------- home: re-entry threshold -----------------------
   Client-rendered strip for returning readers. Three rows: last
   visit, open quest, frontier candidates. First-time visitors never
   see it (container stays hidden). Renders on all viewports — unlike
   the trail/forward panels, this is the re-entry ramp for mobile too.
   ----------------------------------------------------------------- */
.home-reentry-wrap {
  /* Reserve no vertical space before JS populates; once JS writes
     the inner content we un-hide the element — no layout jump. */
  display: none;
}
.home-reentry-wrap:not([hidden]) {
  display: block;
  margin: 0 0 40px;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.reentry-inner {
  padding: 16px 0 18px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.reentry-row {
  display: flex;
  align-items: baseline;
  gap: 16px;
  flex-wrap: wrap;
}
.reentry-label {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  white-space: nowrap;
  flex-shrink: 0;
  /* widest label is "the frontier near you" — 21ch before tracking */
  width: 25ch;
  text-align: right;
}
.reentry-content {
  font-family: var(--serif);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-secondary);
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px;
}
.reentry-link {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 1px;
  transition: border-color 120ms ease, color 120ms ease;
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
}
.reentry-link:hover {
  border-bottom-color: var(--ochre-2);
  color: var(--ink);
}
.reentry-title {
  font-family: var(--serif);
}
.reentry-kind {
  display: inline-flex;
  align-items: center;
  color: var(--ink-secondary);
}
.reentry-kind.kind-mark--glyph {
  width: 13px;
  height: 13px;
  vertical-align: middle;
  position: relative;
  top: -1px;
}
.reentry-frontier {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  align-items: baseline;
}
.reentry-link--frontier {
  font-size: 13.5px;
  color: var(--ink-secondary);
}
.reentry-link--frontier:hover {
  color: var(--ink);
}
.reentry-link--cross .reentry-title {
  border-bottom: 1px solid var(--ochre-1);
}
.reentry-foot {
  margin: 2px 0 0;
  padding-left: calc(25ch + 16px);  /* align under the content column */
}
.reentry-foot-link {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color 120ms ease, border-color 120ms ease;
}
.reentry-foot-link:hover {
  color: var(--ink);
  border-bottom-color: var(--ochre-1);
}
@media (max-width: 680px) {
  .reentry-foot { padding-left: 0; }
}
.reentry-cross {
  font-family: var(--mono);
  font-size: 9px;
  color: var(--ochre-2);
  flex-shrink: 0;
  line-height: 1;
  align-self: center;
}
/* Mobile: stack label above content; full-width */
@media (max-width: 600px) {
  .reentry-row {
    flex-direction: column;
    gap: 4px;
  }
  .reentry-label {
    width: auto;
    text-align: left;
  }
}

/* ----------------- /about — the form made legible ----------------- */
.about-page-body main { max-width: 880px; margin: 0 auto; padding: 0 16px; }
.about-page { padding: 24px 0 80px; }
.about-head {
  padding: 18px 0 32px;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 32px;
}
.about-head h1 {
  font-size: clamp(32px, 2.6vw + 16px, 44px);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.014em;
  margin: 12px 0 18px;
}
.about-lead {
  max-width: 64ch;
  font-family: var(--serif);
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink-secondary);
  margin: 0 0 14px;
}
.about-lead em { font-style: italic; color: var(--ink); }
.about-lead--secondary { font-size: 15.5px; color: var(--ink-tertiary); }

.about-section {
  margin: 0 0 56px;
}
.about-section-head { margin-bottom: 18px; }
.about-section-eyebrow {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0 0 4px;
}
.about-section h2 {
  font-family: var(--serif);
  font-size: 26px;
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0;
}
.about-prose {
  max-width: 64ch;
  font-family: var(--serif);
  font-size: 15.5px;
  line-height: 1.65;
  color: var(--ink-secondary);
  margin: 0 0 14px;
}
.about-prose em { font-style: italic; color: var(--ink); }
.about-prose strong { font-weight: 600; color: var(--ink); }
.about-prose code {
  font-family: var(--mono);
  font-size: 12.5px;
  padding: 1px 5px;
  background: rgba(28, 26, 23, 0.05);
  color: var(--ink);
}

/* The specimen row — five cards explaining each entity kind */
.about-spec-row {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 0;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  margin-top: 12px;
}
.about-spec {
  padding: 22px 22px 18px;
  border-right: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.about-spec:nth-last-child(-n+3):nth-child(3n+1),
.about-spec:nth-last-child(-n+3):nth-child(3n+1) ~ .about-spec { border-bottom: none; }
.about-spec:last-child { border-right: none; }
.about-spec-h {
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 500;
  margin: 0 0 8px;
  color: var(--ink);
}
.about-spec-desc {
  font-family: var(--serif);
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-secondary);
  margin: 0 0 14px;
}
.about-spec-desc em { font-style: italic; color: var(--ink); }
.about-spec-desc strong { color: var(--ink); font-weight: 600; font-feature-settings: 'tnum'; }
.about-spec-link {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: inherit;
  padding: 8px 0 0;
  border-top: 1px solid var(--hairline);
}
.about-spec-link--person {
  align-items: flex-start;
}
.about-spec-glyph {
  display: inline-block;
  width: 44px;
  height: 28px;
  overflow: hidden;
  flex: none;
}
.about-spec-glyph svg { width: 100%; height: 100%; display: block; }
.about-spec-glyph--person {
  width: 36px;
  height: 84px;
  overflow: visible;
}
.about-spec-glyph--person svg { width: 36px; height: 84px; }
.about-spec-meta {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
  min-width: 0;
}
.about-spec-kind {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.about-spec-title {
  font-family: var(--serif);
  font-size: 14px;
  color: var(--ink);
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 2px;
  display: inline-block;
}
.about-spec-link:hover .about-spec-title { border-bottom-color: var(--ink); }
.about-spec-sub {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink-tertiary);
  letter-spacing: 0.04em;
}
.about-spec-list { display: flex; flex-wrap: wrap; gap: 14px; margin: 6px 0 0; padding: 0; }
.about-spec-link--inline {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  border-top: none;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
}
.about-spec-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent, var(--ink));
}

/* CTA links — italic ochre with sliding arrow */
.about-cta-row { margin: 18px 0 0; }
.about-cta {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--serif);
  font-style: italic;
  font-size: 16px;
  color: rgba(120, 70, 28, 1);
  text-decoration: none;
  border-bottom: 1px solid rgba(160, 95, 40, 0.55);
  padding-bottom: 1px;
}
.about-cta:hover { border-bottom-color: rgba(120, 70, 28, 1); }
.about-cta-arrow { transition: transform 220ms ease; }
.about-cta:hover .about-cta-arrow { transform: translateX(4px); }

/* Voice row — four little stance lines */
.about-voice-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px 28px;
  margin: 14px 0 0;
}
.about-voice {
  font-family: var(--serif);
  font-size: 14px;
}
.about-voice-name {
  font-style: italic;
  font-weight: 600;
}
.about-voice-stance {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--ink-tertiary);
  margin-left: 6px;
}

/* Fork section — slightly emphasized */
.about-section--fork {
  margin-top: 64px;
  padding-top: 32px;
  border-top: 1px solid var(--ink);
}
.about-fork-link {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--ink);
  border-bottom: 1px solid var(--ink);
  text-decoration: none;
  letter-spacing: 0.02em;
}
.about-fork-link:hover { background: rgba(28, 26, 23, 0.04); }

@media (max-width: 720px) {
  .about-spec-row { grid-template-columns: 1fr; }
  .about-spec { border-right: none; }
}

/* home head */
.home-head { margin-bottom: 32px; }
.home-pitch {
  max-width: 60ch;
  margin: 16px 0 0;
  font-family: var(--serif);
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink-secondary);
}
.home-pitch em {
  font-style: italic;
  color: var(--ink);
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 1px;
}

/* Routing bar — three small inline links under the home masthead.
   Replaces the earlier three-door card row and the big hero territory
   map: the nav already routes everywhere, and the home doesn't need
   to shout it. */
.home-routing {
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
  margin: 4px 0 40px;
  padding: 12px 0;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-secondary);
}
.home-routing a {
  text-decoration: none;
  color: var(--ink-secondary);
  transition: color 120ms ease;
}
.home-routing a:hover { color: var(--ink); }

/* Home band — the corpus as a full-bleed errant survey line under the
   masthead. Generated from the real territory: topics threaded left-to-right
   by centroid, the line wandering by their real positions, sized by concept
   count; the now-surveying leg + node carry the one ochre note; count-1
   outliers lift off as detached marks. Breaks out of the centred column to
   100vw. Draws itself in once on load (prefers-reduced-motion: no-preference)
   and otherwise settles to a still, complete state — the same state served
   with JS off. Both themes work off --ink / --ochre-2 / --hairline. */
.home-mark {
  width: 100vw;
  margin: 6px 0 30px;
  margin-left: calc(50% - 50vw);
  display: block;
  pointer-events: none;
}
.home-band-svg {
  display: block;
  width: 100%;
  height: auto;
  color: var(--ink);
  opacity: 0.92;
}
.home-baseline {
  stroke: var(--hairline);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.home-line {
  fill: none;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.home-line--ink { stroke: var(--ink); stroke-width: 1.5; opacity: 0.85; }
.home-line--now { stroke: var(--ochre-2); stroke-width: 1.7; }
.home-marks circle, .home-marks line { vector-effect: non-scaling-stroke; }
.home-node { fill: none; stroke: var(--ink); stroke-width: 1.2; opacity: 0.82; }
.home-node--now { stroke: var(--ochre-2); stroke-width: 1.4; opacity: 1; }
.home-out circle { fill: none; stroke: var(--ink); stroke-width: 1; opacity: 0.5; }
.home-out line { stroke: var(--ink); stroke-width: 1; opacity: 0.5; }
.home-out--now circle, .home-out--now line { stroke: var(--ochre-2); opacity: 1; }
.home-startdot { fill: var(--ink); stroke: none; opacity: 0.5; }

@media (prefers-reduced-motion: no-preference) {
  .home-line {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
    animation: home-line-draw 0.7s ease forwards;
    animation-delay: calc(var(--i) * 0.26s + 0.2s);
  }
  .home-marks { opacity: 0; animation: home-marks-in 0.6s ease 1.7s forwards; }
  .home-node--now { animation: home-now-breathe 3.8s ease-in-out 2.6s infinite alternate; }
}
@keyframes home-line-draw { to { stroke-dashoffset: 0; } }
@keyframes home-marks-in { to { opacity: 1; } }
@keyframes home-now-breathe { from { opacity: 0.6; } to { opacity: 1; } }

@media (max-width: 720px) {
  .home-mark { margin: 2px 0 22px; margin-left: calc(50% - 50vw); }
}

/* The living line — "now surveying — <topic>: <teaser> →". Mono caps
   prefix, serif teaser via the link; crossfades (surveying.js). */
.home-surveying {
  margin: 14px 0 0;
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink-secondary);
}
.home-surveying-prefix {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-right: 0.5em;
}
.home-surveying-link {
  color: var(--ink-secondary);
  text-decoration: none;
  opacity: 1;
  transition: color 120ms ease;
}
/* Underline the topic name only; the teaser stays bare. */
.home-surveying-title {
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 1px;
}
.home-surveying-rest { color: var(--ink-tertiary); }
.home-surveying-link:hover,
.home-surveying-link:focus { color: var(--ink); }
.home-surveying-link:hover .home-surveying-title,
.home-surveying-link:focus .home-surveying-title {
  border-bottom-color: var(--ochre-2);
}

/* topic gallery — the home-page catalogue spread.
   At wide widths the gallery reads as a two-column catalogue, not a stack. */
.topic-gallery {
  display: grid;
  gap: 24px;
  margin-bottom: 48px;
}
@media (min-width: 880px) {
  .topic-gallery { grid-template-columns: 1fr 1fr; }
}

/* The topic card — two-column asymmetric layout.
   Left rail: the apparatus (eyebrow + status, then a zero-suppressed stat block).
   Right body: title, clamped summary, optional concept-name chips.
   Footer: a full-width chronograph band — the topic's temporal signature. */
.topic-card {
  background: var(--paper-warm);
  border: 1px solid var(--hairline);
  padding: 26px 28px 0;
  display: grid;
  grid-template-columns: 96px 1fr;
  grid-template-areas:
    "rail body"
    "chrono chrono";
  column-gap: 28px;
  position: relative;
  transition: border-color 120ms ease, transform 120ms ease, box-shadow 220ms ease;
}
.topic-card:hover {
  border-color: var(--ink-tertiary);
  transform: translateY(-1px);
  box-shadow: 0 6px 18px -10px rgba(28, 26, 23, 0.18);
}

/* Left rail — the apparatus. */
.topic-card-rail {
  grid-area: rail;
  padding-top: 4px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.topic-card-eyebrow {
  margin: 0;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.topic-card-status {
  font-size: 9.5px;
  letter-spacing: 0.14em;
  color: var(--ink-secondary);
}
.topic-card-stats {
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--ink-secondary);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.topic-card-stats li {
  display: flex;
  gap: 6px;
  align-items: baseline;
}
.topic-card-stat-n {
  font-weight: 500;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.topic-card-stat-label {
  color: var(--ink-tertiary);
}

/* Right body — title, summary (clamped), optional concept-name chips. */
.topic-card-body {
  grid-area: body;
  padding-bottom: 22px;
  min-width: 0;
}
.topic-card-head {
  text-decoration: none;
  color: inherit;
  display: block;
}
.topic-card-title {
  font-size: 25px;
  line-height: 1.18;
  font-weight: 500;
  margin: 0 0 10px;
  letter-spacing: -0.004em;
}
.topic-card-head:hover .topic-card-title {
  text-decoration: underline;
  text-decoration-color: rgba(160, 95, 40, 0.55);
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.topic-card-pulse {
  display: inline-block;
  width: 6px;
  height: 6px;
  background: rgba(160, 95, 40, 0.85);
  border-radius: 50%;
  margin: 0 0 4px 10px;
  vertical-align: middle;
}
.topic-card-summary {
  margin: 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  max-width: 60ch;
}
.topic-card-chips {
  margin: 14px 0 0;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-secondary);
  line-height: 1.7;
}
.topic-card-chips a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 120ms ease, color 120ms ease;
}
.topic-card-chips a:hover {
  color: var(--ink);
  border-bottom-color: var(--ink-tertiary);
}

/* Footer band — the chronograph as the card's temporal signature. */
.topic-card-chrono {
  grid-area: chrono;
  border-top: 1px solid var(--hairline);
  padding: 14px 0 18px;
  margin: 6px 0 0;
}
.topic-card-chrono .chronograph { margin: 0; }

@media (max-width: 720px) {
  .topic-card {
    grid-template-columns: 1fr;
    grid-template-areas:
      "rail"
      "body"
      "chrono";
    padding: 20px 22px 0;
  }
  .topic-card-rail {
    flex-direction: row;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 16px;
  }
  .topic-card-stats {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 16px;
  }
  .topic-card-title { font-size: 22px; }
}
/* Flowing typographic index — concepts and people on a topic page render
   as inline serif links separated by middle dots, not a multi-column row
   list. Reads as an index in a book, not a directory dump. */
.topic-index {
  margin: 12px 0 0;
  font-family: var(--serif);
  font-size: 16px;
  line-height: 1.75;
  color: var(--ink);
  max-width: 78ch;
}
.topic-index a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 120ms ease;
}
.topic-index a:hover {
  border-bottom-color: var(--ink-tertiary);
}

.home-recent { margin-top: 40px; }
.home-recent h2 {
  font-size: 16px;
  font-weight: 600;
  margin: 0 0 16px;
  display: flex;
  align-items: baseline;
  gap: 0.4em;
  letter-spacing: 0.01em;
}
.home-inbox-cue {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-secondary);
  margin: 32px 0 0;
  display: flex;
  gap: 12px;
}
.home-inbox-cue a { text-decoration: none; }
.home-inbox-cue a:hover { text-decoration: underline; text-decoration-color: var(--ink); }
.home h1, .inbox h1 {
  font-size: var(--h1);
  font-weight: 500;
  margin: 0 0 4px;
  letter-spacing: -0.003em;
}
.kicker {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-secondary);
  text-transform: uppercase;
  margin: 0 0 32px;
}
.dense-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--mono);
  font-size: var(--dense-size);
  line-height: var(--dense-leading);
}
.dense-table th, .dense-table td {
  text-align: left;
  padding: 8px 16px 8px 0;
  border-bottom: 1px solid var(--hairline);
  vertical-align: baseline;
}
.dense-table th {
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-tertiary);
  font-size: 11px;
}
.dense-table td.num { text-align: right; }
.dense-table td a { text-decoration: none; }
.dense-table td a:hover { text-decoration: underline; text-decoration-color: rgba(160, 95, 40, 0.55); text-underline-offset: 3px; }
.dense-table tr.vetted td { color: var(--ink-secondary); }
.dense-table tr.vetted td:nth-child(3) a { color: var(--ink-secondary); }
.dense-table tr:hover { background: rgba(28,26,23,0.025); }

/* dense table title cell uses serif for readability */
.dense-table td:nth-child(3) { font-family: var(--serif); font-size: 14px; }

/* ============================================================
   search modal
   ============================================================ */
.search-modal {
  position: fixed;
  inset: 0;
  background: rgba(28,26,23,0.18);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 12vh;
  z-index: 100;
}
.search-modal[hidden] { display: none; }
.search-modal-inner {
  width: min(640px, calc(100vw - 32px));
  background: var(--paper);
  box-shadow: 0 20px 60px rgba(28,26,23,0.25);
}
#search-input {
  width: 100%;
  padding: 18px 20px;
  border: 0;
  background: transparent;
  font-family: var(--mono);
  font-size: 16px;
  color: var(--ink);
  border-bottom: 1px solid var(--hairline);
  caret-color: var(--ink);
}
#search-input:focus { outline: none; }
/* Style the browser's native clear-search × to match the register
   instead of using its default blue look. */
#search-input::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'><line x1='3' y1='3' x2='11' y2='11' stroke='%231C1A17' stroke-width='1.4' stroke-linecap='round'/><line x1='11' y1='3' x2='3' y2='11' stroke='%231C1A17' stroke-width='1.4' stroke-linecap='round'/></svg>") center / contain no-repeat;
  opacity: 0.5;
  cursor: pointer;
}
#search-input::-webkit-search-cancel-button:hover { opacity: 1; }
/* Also apply to territory-search-input */
.territory-search-input::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  width: 12px;
  height: 12px;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'><line x1='3' y1='3' x2='11' y2='11' stroke='%231C1A17' stroke-width='1.4' stroke-linecap='round'/><line x1='11' y1='3' x2='3' y2='11' stroke='%231C1A17' stroke-width='1.4' stroke-linecap='round'/></svg>") center / contain no-repeat;
  opacity: 0.4;
  cursor: pointer;
}
.territory-search-input::-webkit-search-cancel-button:hover { opacity: 0.9; }
.search-results {
  list-style: none;
  margin: 0;
  padding: 4px 0;
  max-height: 60vh;
  overflow-y: auto;
}
.search-results li {
  padding: 8px 20px;
  font-size: 14px;
  cursor: pointer;
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 12px;
  align-items: baseline;
  transition: background 120ms ease;
}
.search-results li:hover { background: rgba(28, 26, 23, 0.03); }
.search-results li.active {
  background: rgba(160, 95, 40, 0.06);
  box-shadow: inset 2px 0 0 rgba(160, 95, 40, 0.7);
}
.search-results .result-meta {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-secondary);
}
.search-results .search-empty {
  font-family: var(--serif);
  font-style: italic;
  font-size: 14px;
  color: var(--ink-tertiary);
  padding: 16px 14px;
  background: none;
  cursor: default;
}
.search-results .search-empty:hover { background: none; }

/* ============================================================
   concept & person page extras
   ============================================================ */
.aka {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-secondary);
  margin: 4px 0 8px;
}
.aka-item { margin-right: 8px; }
.related {
  font-family: var(--mono);
  font-size: 12px;
  margin: 4px 0 8px;
  color: var(--ink-secondary);
}
.related-label {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-tertiary);
  margin-right: 6px;
}

/* ============================================================
   person page — dossier register
   The person header reads like a memorial plaque + intellectual
   portrait: theatrical dates span at top, large serif name, mono
   chips for concepts, narrow prose column below.
   ============================================================ */
.person-header {
  /* slightly more vertical room than the default article-header —
     the dates span needs air to read as theatrical */
  padding: 26px 0 30px;
}
.person-header .breadcrumb { margin-bottom: 18px; }

/* Dates span — the dossier's headline element. Two years
   bracketing a hairline rule that fills the column. */
.person-dates-span {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 18px;
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.16em;
  color: var(--ink-secondary);
}
.person-date {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}
.person-date-rule {
  flex: 1 1 auto;
  height: 1px;
  background: var(--hairline);
  /* faint dot pattern so the rule reads as a span of em-dashes
     rather than a clean line — closer to "1888 — — — 1985" */
  background-image: linear-gradient(
    to right,
    var(--ink-tertiary) 0,
    var(--ink-tertiary) 6px,
    transparent 6px,
    transparent 12px
  );
  background-size: 12px 1px;
  background-repeat: repeat-x;
  background-position: 0 50%;
  background-color: transparent;
  opacity: 0.7;
}
.person-date-rule--open {
  /* trailing rule for a living author — fades to nothing rather
     than terminating at a year */
  background-image: linear-gradient(
    to right,
    var(--ink-tertiary) 0,
    var(--ink-tertiary) 6px,
    transparent 6px,
    transparent 12px
  );
  -webkit-mask-image: linear-gradient(to right, #000 0%, #000 55%, transparent 100%);
          mask-image: linear-gradient(to right, #000 0%, #000 55%, transparent 100%);
}

/* Name — large serif, normal weight. The visual anchor of the
   dossier; everything else is mono around it. */
.person-name {
  font-family: var(--serif);
  font-size: clamp(40px, 4vw + 16px, 64px);
  line-height: 1.05;
  font-weight: 400;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 0 0 18px;
}

/* Associated-concept chips — mono uppercase, hairline border.
   Inline-flex so they wrap nicely when there are several. */
.person-concept-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 6px;
  margin: 0 0 10px;
  padding: 0;
}
.person-concept-chip {
  display: inline-block;
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-secondary);
  padding: 4px 8px 3px;
  border: 1px solid var(--hairline);
  border-radius: 2px;
  text-decoration: none;
  background: transparent;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}
.person-concept-chip:hover {
  border-color: var(--ink);
  color: var(--ink);
  background: var(--paper-warm);
  text-decoration: none;
}
.person-concept-chip.xref-missing {
  border-style: dashed;
  color: var(--ink-tertiary);
}
.person-concept-chip--cross-topic {
  border-color: rgba(160, 95, 40, 0.45);
  color: rgba(120, 70, 28, 0.95);
}
.person-concept-chip--cross-topic:hover {
  border-color: rgba(160, 95, 40, 0.85);
  background: rgba(160, 95, 40, 0.06);
  color: var(--ink);
}
.person-concept-chip-cross {
  margin-left: 4px;
  color: rgba(160, 95, 40, 0.85);
  font-size: 11px;
}

/* Primary topics — small linked text beneath the chips.
   "in war-and-its-aftermath" style. */
.person-primary-topics {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-secondary);
  margin: 2px 0 0;
}
.person-primary-topics-label {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-tertiary);
  margin-right: 6px;
}
.person-primary-topics a {
  color: var(--ink-secondary);
  text-decoration-color: var(--ink-tertiary);
}
.person-primary-topics a:hover { color: var(--ink); }

/* Dossier reading column — narrow prose for the situating + stake
   block so the page feels portrait-shaped, not full-width. Only
   the prose body is constrained; the header still spans the grid. */
.person-page .prose {
  max-width: 52ch;
}

@media (max-width: 720px) {
  .person-name { font-size: clamp(34px, 8vw, 44px); }
  .person-dates-span { font-size: 12px; letter-spacing: 0.12em; gap: 10px; }
}

/* ============================================================
   article preview cards (the heavy "writing" treatment)
   ============================================================ */
.article-preview-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  margin: 0;
}
@media (min-width: 880px) {
  .article-preview-grid--multi {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.article-preview {
  background: transparent;
  border-top: 1px solid var(--ink-tertiary);
  border-bottom: 1px solid var(--hairline);
  padding: 24px 0 22px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-decoration: none;
  color: inherit;
  transition: border-color 120ms ease;
}
.article-preview:hover {
  border-top-color: var(--ink);
  background: rgba(28, 26, 23, 0.02);
}
/* In a 2-up grid on wide screens, the rules align to make a paper-card
   feel without the warm fill. */
.article-preview-grid--multi .article-preview {
  padding: 24px 24px 22px;
}
.article-preview .breadcrumb {
  margin: 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  display: flex;
  gap: 0.6em;
  align-items: baseline;
}
.article-preview-title {
  font-family: var(--serif);
  font-size: 22px;
  line-height: 1.2;
  font-weight: 500;
  margin: 0;
  color: var(--ink);
  letter-spacing: -0.005em;
}
.article-preview:hover .article-preview-title {
  text-decoration: underline;
  text-decoration-color: var(--ink-tertiary);
  text-underline-offset: 3px;
  border-bottom-color: rgba(160, 95, 40, 0.55);
}
.article-preview-summary {
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink-secondary);
  margin: 0;
  /* Limit to ~3 lines of summary so previews stay roughly aligned */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* note-preview-row: heavier than entity-row, lighter than article-preview */
.note-preview-row {
  display: grid;
  grid-template-columns: auto auto 1fr;
  align-items: baseline;
  gap: 12px;
  padding: 10px 10px;
  margin-left: -10px;
  margin-right: -10px;
  border-bottom: 1px solid var(--hairline);
  transition: background 140ms ease;
}
.note-preview-row:hover {
  background: rgba(28, 26, 23, 0.025);
}
.note-preview-row .kind-mark { align-self: baseline; }
.note-preview-row .note-date {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
}
.note-preview-row .note-title-and-lead {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.note-preview-row .note-title {
  font-family: var(--serif);
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.note-preview-row .note-title:hover {
  border-bottom-color: var(--ink);
}
.note-preview-row .note-lead {
  font-family: var(--serif);
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* multi-column entity lists. Default ramps from 1 → 2 → 3 columns by
   viewport. Sources cap at 2 because their titles are long — at 3
   columns the title gets ~120px and wraps to 8+ lines per row. */
.entity-list--columns {
  column-gap: 36px;
  column-rule: 1px solid var(--hairline);
}
@media (min-width: 1100px) {
  .entity-list--columns { column-count: 3; }
}
@media (min-width: 760px) and (max-width: 1099.98px) {
  .entity-list--columns { column-count: 2; }
}
/* Sources cap one column lower across the board — their titles
   ("Soul Repair — Recovering from Moral Injury After War") wrap to
   8+ lines at 120px columns. Two columns gives titles ~240px each,
   wrapping to 2-3 lines, which is what the eye can scan. */
.entity-list--sources.entity-list--columns {
  column-count: 2;
}
@media (max-width: 759.98px) {
  .entity-list--sources.entity-list--columns { column-count: 1; }
}
.entity-list--columns .entity-row {
  break-inside: avoid;
  padding: 13px 0;
  /* Each row keeps its own bottom border but the column-rule supplies
     the vertical divider between columns. */
}
/* In dense column lists, the °-marker on every xref clusters and adds
   noise. The list context is its own affordance — the cards are
   clearly cross-references. Hide the dot. */
.entity-list--columns .xref::after,
.note-preview-row .xref::after { content: none; }

/* topic-chip nav: small inline jump-links at the top of an index page
   so you can leap to a specific topic's section */
.kind-index-topic-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  margin: 0 0 40px;
  padding: 10px 0;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
.kind-index-topic-nav a {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-secondary);
  text-decoration: none;
  padding: 0;
  background: transparent;
  border: none;
  transition: color 120ms ease;
  display: inline-flex;
  align-items: baseline;
  gap: 0.5em;
}
.kind-index-topic-nav a:hover { color: var(--ink); }
.kind-index-topic-nav .topic-nav-count {
  color: var(--ink-tertiary);
  letter-spacing: 0.04em;
  position: relative;
  padding-left: 0.6em;
}
.kind-index-topic-nav .topic-nav-count::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.18em;
  bottom: 0.18em;
  width: 1px;
  background: var(--hairline);
}

/* ============================================================
   excerpts (verbatim quotations from sources)
   ============================================================ */
.excerpts {
  max-width: 720px;
  margin: 56px auto 0;
  padding: 24px 0 0;
  border-top: 1px solid var(--hairline);
}
.excerpts h2 {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-secondary);
  margin: 0 0 24px;
  display: flex;
  align-items: baseline;
  gap: 0.5em;
}
.excerpt-card {
  margin: 0 0 32px;
  padding: 0;
}
.excerpt-card:last-child { margin-bottom: 0; }
.excerpt-text {
  margin: 0;
  padding: 0 0 0 2ch;
  border-left: 1px solid var(--ink-tertiary);
  font-family: var(--serif);
  font-size: 17px;
  line-height: 1.5;
  font-style: italic;
  color: var(--ink);
}
.excerpt-card figcaption {
  margin: 8px 0 0 calc(2ch + 1px);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-secondary);
}
.excerpt-cite { font-style: normal; }
.excerpt-cite a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
}
.excerpt-cite a:hover {
  border-bottom-color: var(--ink);
}
.excerpt-note {
  margin: 8px 0 0 calc(2ch + 1px);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-secondary);
}
.excerpt-concepts {
  margin: 6px 0 0 calc(2ch + 1px);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-secondary);
}

/* topic / wide pages: excerpts may sit edge-to-edge */
.person-page .excerpts,
.source-page .excerpts { max-width: 720px; }

/* ============================================================
   backlinks ("mentioned in")
   ============================================================ */
.backlinks {
  max-width: 720px;
  margin: 64px auto 0;
  padding: 24px 0 0;
  border-top: 1px solid var(--hairline);
}
.backlinks h2 {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-secondary);
  margin: 0 0 20px;
  display: flex;
  align-items: baseline;
  gap: 0.5em;
}
.backlinks-group { margin-bottom: 20px; }
.backlinks-group h3 {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0 0 6px;
}
/* topic page: backlinks sit edge-to-edge with topic-view */
.topic-page .backlinks { max-width: 980px; }

/* ============================================================
   kind index pages (concepts / people / sources)
   ============================================================ */
.kind-index { max-width: 980px; }
.kind-index-head { margin-bottom: 24px; }
.kind-index-group h2 {
  font-size: 18px;
  font-weight: 500;
  margin: 0 0 12px;
  display: flex;
  align-items: baseline;
  gap: 0.4em;
}
.kind-index-group h2 a {
  text-decoration: none;
  color: inherit;
}
.kind-index-group h2 a:hover {
  text-decoration: underline;
  text-decoration-color: var(--ink);
}
.kind-index-group h2 .section-meta {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-left: auto;
  font-weight: 400;
}
.kind-index-substack { margin-top: 12px; }
.kind-index-substack-h {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0 0 6px;
  display: flex;
  align-items: baseline;
  gap: 0.5em;
}

/* ============================================================
   quests — chat-stream dialogue (Disco-Elysium-style)
   ============================================================ */

/* The quest page is a single scrolling transcript. The pitch leads,
   scenes flow chronologically beneath it. Past scenes stay in place,
   faded; the active scene is the last one and carries the option
   picker at its tail. When you choose, the active scene's options
   disappear into a "pick" callback, the scene fades to past, and the
   next scene fades in below. */

.quest {
  max-width: 720px;
  margin: 0 auto;
  padding: 0 16px 96px;
}

.quest-header {
  padding: 24px 0 20px;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 32px;
  /* Header doesn't need to be tall — the pitch is in the stream */
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 16px 24px;
}
.quest-title {
  font-size: clamp(18px, 1vw + 12px, 22px);
  font-weight: 500;
  margin: 0;
  line-height: 1.2;
  letter-spacing: -0.008em;
  flex: 1 1 100%;
  color: var(--ink-secondary);
}
/* Manuscript-opener: a larger algorithmic signature of the quest's
   branching graph, sitting above the controls. The same SVG
   generator as the landing card, scaled up. Reads as a small plate
   marking the start of the quest. Hairlines only; identifies the
   quest at a glance even before you read the title. */
.quest-header-signature {
  flex: 1 1 100%;
  display: flex;
  justify-content: flex-end;
  margin: -4px 0 12px;
  opacity: 0.85;
}
.quest-header-signature svg {
  display: block;
  width: 240px;
  height: 64px;
}
@media (max-width: 720px) {
  .quest-header-signature svg {
    width: 180px;
    height: 48px;
  }
}
.quest-controls {
  display: flex;
  align-items: baseline;
  gap: 16px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.quest-reset {
  background: none;
  border: none;
  border-bottom: 1px solid var(--hairline);
  padding: 2px 4px 2px 2px;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: var(--ink-tertiary);
  cursor: pointer;
  transition: color 150ms ease, border-bottom-color 150ms ease;
}
.quest-reset-glyph {
  display: inline-block;
  font-size: 13px;
  letter-spacing: 0;
  margin-right: 3px;
  /* No text-transform on glyphs — they're already symbolic. */
  text-transform: none;
  transition: transform 280ms cubic-bezier(0.22, 1, 0.36, 1);
  vertical-align: -1px;
}
.quest-reset:hover {
  color: var(--ink);
  border-bottom-color: var(--ink);
}
.quest-reset:hover .quest-reset-glyph {
  transform: rotate(-180deg);
}
/* Replace the browser-default blue focus ring with an in-register
   focus state: ink-colored hairline frame and a small left tick so
   the button reads as a control even before you mouse it. */
.quest-reset:focus-visible {
  outline: none;
  color: var(--ink);
  border-bottom-color: var(--ink);
  box-shadow: -8px 0 0 -6px var(--ink);
}
.quest-progress-indicator { color: var(--ink-secondary); }
.quest-progress-indicator:not(:empty)::before {
  content: '·';
  margin-right: 16px;
  color: var(--ink-tertiary);
}

/* ----------------- the stream ----------------- */
.quest-stream {
  display: flex;
  flex-direction: column;
  gap: 40px;
  /* Scene pagination: the opening is silently scene I (it wears the
     drop-cap), and counter-increment on subsequent regular scenes
     gives them II, III, IV... as small section markers. Endings
     already carry the fleuron divider and stay outside the count. */
  counter-reset: quest-scene 1;
}
.quest-scene:not(.quest-scene--opening):not(.quest-scene--ending) {
  counter-increment: quest-scene;
}
.quest-scene:not(.quest-scene--opening):not(.quest-scene--ending)::before {
  content: counter(quest-scene, upper-roman) ".";
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  color: var(--ink-tertiary);
  font-weight: 500;
  /* The flex gap on .quest-scene gives 18px between this and the body;
     pull it tighter so the numeral reads as a header on the section. */
  margin-bottom: -10px;
}

/* Pitch as the first message in the stream — system framing card.
   Small "the setup" label above marks it as scenario-frame, not
   yet the dialogue. */
.quest-stream .quest-pitch {
  font-family: var(--serif);
  font-size: 17px;
  line-height: 1.65;
  color: var(--ink-secondary);
  max-width: 62ch;
  font-style: italic;
  padding-top: 14px;
  padding-left: 16px;
  border-left: 2px solid var(--ink-tertiary);
  position: relative;
  /* The watermark sits inside this box and bleeds; clip it so it
     never escapes into the surrounding stream. */
  overflow: hidden;
  isolation: isolate;
}
/* Per-quest ambient watermark: a faint flow-field hairline behind
   the pitch text. Same generator as the topic mastheads but at
   ~12% opacity and overflow-clipped to the pitch box. Provides a
   tactile, printed-paper quality without competing with the text. */
.quest-pitch-watermark {
  position: absolute;
  left: 16px;
  right: 0;
  top: 0;
  bottom: 0;
  width: auto !important;
  height: 100% !important;
  opacity: 0.12;
  z-index: 0;
  pointer-events: none;
  /* preserveAspectRatio is set as an SVG attribute by
     renderTopicMasthead; can't be applied via CSS. */
}
.quest-stream .quest-pitch > *:not(.quest-pitch-watermark) {
  position: relative;
  z-index: 1;
}
.quest-stream .quest-pitch::before {
  content: 'the setup';
  position: absolute;
  top: -6px;
  left: 16px;
  padding: 0 8px;
  background: var(--paper);
  font-family: var(--mono);
  font-size: 10px;
  font-style: normal;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.quest-stream .quest-pitch p { margin: 0 0 12px; }
.quest-stream .quest-pitch p:last-child { margin-bottom: 0; }

/* Scenes flow chronologically inside the stream. */
.quest-scene {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* Newly-arriving content fades up. Past content (restored history)
   skips the animation by also being marked --past. */
.quest-scene--active,
.quest-pick {
  animation: quest-fade-in 360ms cubic-bezier(0.16, 0.84, 0.44, 1) both;
}
@keyframes quest-fade-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Past scenes fade — readable but quieter, so the eye lands on the
   active scene at the bottom. Skips the new-arrival animation too. */
.quest-scene--past {
  animation: none;
  opacity: 0.62;
  transition: opacity 250ms ease;
}
.quest-scene--past:hover { opacity: 0.9; }
.quest-scene--past .quest-body { color: var(--ink-secondary); }

/* The body. */
.quest-body {
  font-family: var(--serif);
  font-size: 18px;
  line-height: 1.65;
  color: var(--ink);
}
.quest-body p { margin: 0 0 1em; }
.quest-body p:last-child { margin-bottom: 0; }

/* Opening-scene drop-cap. A book's first page deserves a chapter
   opener; an interactive quest's first scene gets one too. Big
   hand-set initial on the first paragraph, optical-margin nudged
   left so it sits flush with the rest of the body's left edge.
   Only on the literal opening node — subsequent scenes don't
   reopen the chapter. */
.quest-scene--opening .quest-body > p:first-child::first-letter {
  font-family: var(--serif);
  font-size: 4.2em;
  line-height: 0.86;
  float: left;
  margin: 0.04em 0.08em 0 -0.03em;
  padding: 0;
  color: var(--ink);
  font-weight: 500;
  /* A faint italic-style flourish on the descender side. */
  font-feature-settings: "dlig" 1, "liga" 1;
}
/* On past-state, the drop-cap dims with the rest of the scene
   text but stays the chapter-anchor it always was. */
.quest-scene--past.quest-scene--opening .quest-body > p:first-child::first-letter {
  color: var(--ink-secondary);
}

/* The "pick" callback — what the user said, rendered as a quoted
   echo between scenes. Marked with a small "you said" label so the
   transcript shape is unambiguous. */
.quest-pick {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 12px 0 0 18px;
  padding: 14px 18px 12px;
  background: rgba(28, 26, 23, 0.035);
  border-left: 2px solid var(--ink-tertiary);
  font-family: var(--serif);
  position: relative;
}
.quest-pick::before {
  content: 'you said';
  position: absolute;
  top: -7px;
  left: 12px;
  padding: 0 6px;
  background: var(--paper);
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.quest-pick-arrow {
  display: none;
}
.quest-pick-text {
  font-size: 15px;
  line-height: 1.45;
  color: var(--ink);
  font-style: italic;
}

/* Excerpt-as-body: a scene whose substance is a verbatim corpus
   line. Weighted differently from authored prose — block-quote
   register with attribution. */
.quest-excerpt {
  margin: 0 0 20px;
  padding: 0;
}
.quest-excerpt-text {
  margin: 0;
  padding: 8px 0 8px 2ch;
  border-left: 2px solid var(--ink-tertiary);
  font-family: var(--serif);
  font-size: 20px;
  line-height: 1.5;
  font-style: italic;
  color: var(--ink);
}
.quest-excerpt figcaption {
  margin: 10px 0 0 calc(2ch + 2px);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-secondary);
}
.quest-excerpt figcaption .excerpt-cite a {
  color: inherit;
  border-bottom: 1px solid var(--hairline);
  text-decoration: none;
}
.quest-excerpt figcaption .excerpt-cite a:hover {
  border-bottom-color: var(--ink);
}
.quest-excerpt-note {
  margin: 10px 0 0 calc(2ch + 2px);
  font-family: var(--serif);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-secondary);
  font-style: italic;
}

/* Voice barges have spatial identity per character — like
   Disco-Elysium's skill voices, each one comes from a recognisable
   position. Stacked vertically still, but offset and sized so the
   eye learns where each voice lives. */
.quest-voices {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 4px;
}
/* Voice's ::before bar uses absolute positioning relative to .voice.
   The base rule sets .voice { position: relative } — we MUST preserve
   that here. An earlier version forced position: static which made
   the ::before escape its host and stretch to a higher positioned
   ancestor, painting a 700px-tall oxblood line down the side of the
   active scene. */
/* Default — full width, paper-warm bg, colored bar at left.
   Overridden per voice below. */
.quest-voices .voice {
  margin: 0;
  width: 100%;
}

/* The four voices each pull to their own position on wide viewports,
   so the eye learns where each one lives. With the new marginalia
   register (no fill, just a hairline + small italic line), the pulls
   read as actual side-notes rather than as boxed callouts. */
@media (min-width: 880px) {
  /* Skeptic pulls right — the doubting voice from the wings. */
  .quest-voices .voice-skeptic {
    margin-left: 38%;
    width: 62%;
  }
  /* Historian sits inset, slightly indented — the steady citation. */
  .quest-voices .voice-historian {
    margin-left: 6%;
    width: 88%;
  }
  /* Aesthete pulls left, narrow column — quiet noticing from the wings. */
  .quest-voices .voice-aesthete {
    width: 56%;
  }
  /* Operator flush bottom-right, the tightest. */
  .quest-voices .voice-operator {
    margin-left: 46%;
    width: 54%;
  }
}

/* ----------------- options ----------------- */
/* Disco-Elysium-style numbered picker at the tail of the active scene.
   Each option appears as a numbered row. Options stagger in via JS
   setting animation-delay. */
.quest-options {
  list-style: none;
  padding: 0;
  margin: 12px 0 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  border-top: 1px solid var(--hairline);
  padding-top: 20px;
}
.quest-option {
  margin: 0;
  animation: quest-fade-in 280ms cubic-bezier(0.16, 0.84, 0.44, 1) both;
}
.quest-option-btn {
  width: 100%;
  text-align: left;
  background: transparent;
  border: 1px solid transparent;
  padding: 10px 14px 10px 12px;
  font: inherit;
  color: var(--ink);
  font-family: var(--serif);
  font-size: 16px;
  line-height: 1.5;
  display: grid;
  grid-template-columns: 20px 1fr;
  column-gap: 14px;
  row-gap: 4px;
  align-items: baseline;
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease, transform 120ms ease, color 120ms ease;
}
.quest-option-btn:hover {
  background: rgba(28, 26, 23, 0.04);
  border-color: var(--hairline);
  transform: translateX(3px);
}
.quest-option-btn:focus-visible {
  outline: none;
  border-color: var(--ink);
  background: rgba(28, 26, 23, 0.04);
  /* Match hover's slide so keyboard and pointer feel consistent. */
  transform: translateX(3px);
}
.quest-option-btn:focus-visible .quest-option-number {
  color: var(--ink);
  transform: translateX(-2px);
}
/* The number lives in the grid's first column. Slightly more weight
   than before so the keyboard mapping reads at a glance. */
.quest-option-number {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
  text-align: right;
  align-self: baseline;
  user-select: none;
  transition: color 120ms ease, transform 120ms ease;
}
.quest-option-btn:hover .quest-option-number {
  color: var(--ink);
  transform: translateX(-2px);
}
.quest-option-text { grid-column: 2; align-self: baseline; }
.quest-option--naive .quest-option-btn {
  color: var(--ink-secondary);
  font-style: italic;
}
.quest-option--walked .quest-option-btn {
  color: var(--ink-secondary);
}
.quest-option-walked-mark {
  grid-column: 2;
  display: inline-flex;
  align-self: flex-start;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  font-style: normal;
}

/* Side-quest invocation: the left hairline tells the eye it's a
   deliberate detour. The tag below was previously a heavy bordered
   badge with paper-warm fill — too much for what it says. Replaced
   with a small monospaced LABEL prefixed by a directional glyph
   (↗ for "leads outward", ✓ for walked). Reads as a quiet caption
   rather than a UI chip. */
.quest-option-btn--side {
  border-left: 2px solid var(--ink-tertiary);
}
.quest-option-side-tag {
  grid-column: 2;
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  align-self: flex-start;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  font-style: normal;
  padding: 0;
  margin-top: -2px;
  border: none;
  background: transparent;
  transition: color 150ms ease;
}
.quest-option-side-tag::before {
  content: '↗';
  display: inline-block;
  font-size: 11px;
  letter-spacing: 0;
  color: var(--ink-tertiary);
  transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1), color 150ms ease;
}
.quest-option-btn--side:hover .quest-option-side-tag,
.quest-option-btn--side:focus-visible .quest-option-side-tag {
  color: var(--ink);
}
.quest-option-btn--side:hover .quest-option-side-tag::before,
.quest-option-btn--side:focus-visible .quest-option-side-tag::before {
  color: var(--ink);
  transform: translateX(2px) translateY(-1px);
}

/* Return-to-caller link on a side-quest's ending screen. */
.quest-return-to-caller {
  text-decoration: none;
  color: var(--ink);
  border-bottom: 1px solid var(--ink-tertiary);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.quest-return-to-caller:hover {
  border-bottom-color: var(--ink);
}
/* (Locked-option styling removed — gating mechanic was dropped. Every
   option is always available; the conversation is the conversation.) */

/* Reading-position rail — vertical column of dots, one per scene
   passed, pinned to the left edge of the viewport. The active scene
   gets a larger filled mark. Clickable for direct jumps to any
   prior scene. Quiet enough to read peripherally. */
.quest-reading-rail {
  position: fixed;
  top: 50%;
  left: 24px;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  z-index: 10;
  animation: quest-fade-in 320ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
.quest-reading-rail[hidden] { display: none; }
.quest-rail-mark {
  background: none;
  border: none;
  padding: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
}
.quest-rail-mark:focus-visible {
  outline: none;
  /* The mark already centres its dot via flex; expand the focus
     ring on the wrapper so it reads as a target without disturbing
     the dot itself. */
  box-shadow: 0 0 0 1px var(--ink);
}
.quest-rail-dot {
  display: block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  border: 1px solid var(--ink-tertiary);
  background: transparent;
  transition: background 200ms ease, transform 200ms ease, border-color 200ms ease;
}
.quest-rail-mark:hover .quest-rail-dot,
.quest-rail-mark:focus-visible .quest-rail-dot {
  background: var(--ink-tertiary);
  transform: scale(1.3);
}
.quest-rail-mark--active .quest-rail-dot {
  width: 10px;
  height: 10px;
  background: var(--ink);
  border-color: var(--ink);
}
.quest-rail-mark--active:hover .quest-rail-dot {
  transform: scale(1.2);
}
/* Hairline connecting line between marks */
.quest-reading-rail::before {
  content: '';
  position: absolute;
  top: 12px;
  bottom: 12px;
  left: 50%;
  width: 1px;
  background: var(--hairline);
  z-index: -1;
}
/* Hide rail on narrow viewports — would crowd the content */
@media (max-width: 1100px) {
  .quest-reading-rail { display: none; }
}

/* Floating "↓ current scene" button — pinned bottom-right when the
   user has scrolled away from the active scene. Returns them to the
   picker. */
.quest-jump-current {
  position: fixed;
  bottom: 24px;
  right: 24px;
  z-index: 20;
  background: var(--ink);
  color: var(--paper);
  border: none;
  padding: 10px 16px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  box-shadow: 0 4px 16px rgba(28, 26, 23, 0.18);
  animation: quest-jump-in 240ms cubic-bezier(0.16, 0.84, 0.44, 1) both;
}
.quest-jump-current[hidden] {
  display: none;
}
.quest-jump-current:hover {
  background: rgba(28, 26, 23, 0.92);
}
.quest-jump-current span[aria-hidden] {
  font-family: var(--serif);
  font-size: 14px;
  line-height: 1;
}
@keyframes quest-jump-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ----------------- ending ----------------- */
/* The transition from "the conversation" to "the written record"
   wants a beat — a small typographic ornament between the active
   stream and the ending memo. ❦ (a fleuron) on a thin pair of
   hairlines, fade-in slower than the scene itself. */
.quest-scene--ending {
  border-top: 1px solid var(--ink-tertiary);
  border-bottom: 1px solid var(--ink-tertiary);
  padding: 56px 28px 32px;
  background: var(--paper-warm);
  margin: 32px -8px 16px;
  position: relative;
}
/* Animated reveal of the concept constellation. Edges trace first
   (the relationship lines drawing themselves), then nodes fade in
   on top. Reads as the territory composing itself in front of the
   reader. */
.concept-constellation .cc-edge {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: cc-edge-trace 800ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
@keyframes cc-edge-trace {
  to { stroke-dashoffset: 0; }
}
.concept-constellation .cc-node {
  opacity: 0;
  animation: cc-node-fade 600ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
@keyframes cc-node-fade {
  to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .concept-constellation .cc-edge {
    animation: none;
    stroke-dashoffset: 0;
  }
  .concept-constellation .cc-node {
    animation: none;
    opacity: 1;
  }
}

/* Concept constellation — sunflower-spiral graph of the concepts
   the quest's relates_to.concepts touches. Rendered after the
   ending memo body, before the "if you want to keep reading"
   block. */
.quest-constellation {
  margin: 36px 0 0;
  padding: 24px 0 0;
  border-top: 1px solid var(--hairline);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}
.quest-constellation-cap {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.quest-constellation svg {
  display: block;
  width: 100%;
  max-width: 480px;
  height: auto;
  align-self: center;
}

/* The per-ending algorithmic mark. A Lissajous-family closed curve
   seeded from `${questId}:${endingId}` — every ending of every
   quest carries a unique flourish. Positioned where the static ❦
   used to sit: centred over the top border of the ending memo,
   on a paper-fill rectangle so it cuts the hairline. */
.quest-ending-mark-wrap {
  position: absolute;
  top: -22px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--paper);
  padding: 0 12px;
  animation: quest-ending-mark-in 1100ms cubic-bezier(0.22, 1, 0.36, 1) 240ms both;
}
.quest-ending-mark-wrap svg {
  display: block;
  width: 64px;
  height: 40px;
}
/* The Lissajous curve traces itself in. Path has pathLength="1"
   so dash math is normalised — stroke-dashoffset 1→0 over the
   animation traces the full loop. */
.quest-ending-mark path {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: quest-ending-trace 2200ms cubic-bezier(0.22, 1, 0.36, 1) 240ms forwards;
}
@keyframes quest-ending-trace {
  to { stroke-dashoffset: 0; }
}
@keyframes quest-ending-mark-in {
  from { opacity: 0; transform: translate(-50%, -8px); }
  to   { opacity: 0.95; transform: translate(-50%, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .quest-ending-mark path {
    animation: none;
    stroke-dashoffset: 0;
  }
}
.quest-ending-title {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0 0 24px;
  font-weight: 500;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  /* The title now sits above the memo body with a hairline rule
     beneath it — like a section sign in a printed memo. */
  padding-bottom: 14px;
  border-bottom: 1px solid var(--hairline);
}
.quest-ending-meta {
  color: var(--ink-tertiary);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.06em;
}
.quest-card-paths {
  color: var(--ink-tertiary);
}
/* Quest-card breadcrumb runs as a flex row so the signature
   can pull right while kind-mark + status + dots stay left. */
.quest-card .breadcrumb {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
/* The algorithmic quest signature — small hairline tree
   visualization sitting at the far right of the breadcrumb.
   Deterministic per quest from the YAML's branching graph.
   Reads as a density signal: sparse = side detour, busy = long
   branching quest. */
.quest-card-signature {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.quest-card-signature svg {
  display: block;
}

/* Quest-signature edges trace in on first paint. Deeper edges
   start later (depth × 80ms), so the tree grows outward from
   the root over ~1s. */
.quest-signature .qs-edge {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: qs-edge-trace 1000ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
@keyframes qs-edge-trace {
  to { stroke-dashoffset: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .quest-signature .qs-edge {
    animation: none;
    stroke-dashoffset: 0;
  }
}

/* Quest-signature node classes. The SVG carries every node as a
   small circle with data-node. JS adds .qs-node--walked once the
   engagement substrate confirms a visit. The signature becomes a
   live progress visualization: nodes you have walked light up. */
.qs-node {
  fill: none;
  stroke: var(--ink);
  stroke-width: 0.4;
  stroke-opacity: 0.25;
  transition: stroke-opacity 220ms ease, fill 220ms ease, fill-opacity 220ms ease;
}
.qs-node--root {
  stroke-width: 0.6;
  stroke-opacity: 0.65;
}
.qs-node--ending {
  fill: var(--ink);
  fill-opacity: 0.7;
  stroke: none;
}
/* Walked: any non-ending node becomes a small filled ink dot;
   ending nodes brighten and gain a tiny faint stroke ring. */
.qs-node--walked {
  fill: var(--ink);
  fill-opacity: 0.85;
  stroke-opacity: 0;
}
.qs-node--walked.qs-node--ending {
  fill-opacity: 1;
}
.qs-node--walked.qs-node--root {
  fill: var(--ink);
  fill-opacity: 0.85;
  stroke-opacity: 0;
}
/* Path-walked dots: one glyph per declared ending. ● walked, ○ not.
   Sized down to sit on the same line as the kind-mark and status,
   tracked a bit wider so the dots feel like positions on a ruler
   rather than a contiguous word. */
.quest-card-dots {
  display: inline-block;
  margin-left: 2px;
  letter-spacing: 0.18em;
  font-size: 9px;
  line-height: 1;
  color: var(--ink-tertiary);
  vertical-align: 1px;
}
.quest-card--touched .quest-card-dots {
  color: var(--ink-secondary);
}
.quest-card--exhausted .quest-card-dots {
  color: var(--ink);
}
.quest-card--exhausted {
  border-top-color: var(--ink);
}
.quest-ending-controls {
  display: flex;
  gap: 24px;
  align-items: baseline;
  margin-top: 32px;
  padding-top: 24px;
  border-top: 1px solid var(--hairline);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.quest-restart {
  background: none;
  border: 1px solid var(--ink-tertiary);
  padding: 8px 14px;
  cursor: pointer;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: var(--ink);
}

/* "If you want to keep reading" — next-quest suggestions on endings */
.quest-next {
  margin-top: 32px;
  padding-top: 24px;
  border-top: 1px solid var(--hairline);
}
.quest-next-heading {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  font-weight: 500;
  margin: 0 0 14px;
}
.quest-next-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}
@media (min-width: 720px) {
  .quest-next-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.quest-next-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px 16px;
  background: var(--paper);
  border: 1px solid var(--hairline);
  text-decoration: none;
  color: inherit;
  transition: border-color 120ms ease;
}
.quest-next-item:hover {
  border-color: var(--ink-tertiary);
}
.quest-next-title {
  font-family: var(--serif);
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
}
.quest-next-item:hover .quest-next-title {
  text-decoration: underline;
  text-decoration-color: var(--ink-tertiary);
  text-underline-offset: 3px;
}
.quest-next-meta {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.quest-restart:hover {
  background: var(--paper);
}
.quest-restart:focus-visible {
  outline: none;
  border-color: var(--ink);
  background: var(--paper);
  box-shadow: 0 0 0 1px var(--ink) inset;
}
.quest-back-to-list {
  text-decoration: none;
  color: var(--ink-tertiary);
  border-bottom: 1px solid var(--hairline);
  padding: 2px 4px 2px 2px;
  transition: color 150ms ease, border-bottom-color 150ms ease;
}
.quest-back-to-list:hover {
  color: var(--ink);
  border-bottom-color: var(--ink);
}
.quest-back-to-list:focus-visible {
  outline: none;
  color: var(--ink);
  border-bottom-color: var(--ink);
  box-shadow: -8px 0 0 -6px var(--ink);
}

/* ----------------- quests index page ----------------- */
.quest-card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}
@media (min-width: 880px) {
  .quest-card-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.quest-card {
  background: transparent;
  border-top: 1px solid var(--ink-tertiary);
  border-bottom: 1px solid var(--hairline);
  padding: 24px 0 22px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  color: inherit;
  transition: border-color 120ms ease;
  /* Card-as-link: the <article> wrapper is the positioning context
     for the title's invisible click-overlay. */
  position: relative;
}
@media (min-width: 880px) {
  .quest-card { padding: 24px 24px 22px; }
}
.quest-card:hover {
  border-top-color: var(--ink);
}
/* The title's anchor expands to cover the whole card via ::before
   absolute overlay. xrefs inside the pitch still work because they
   sit on a higher z-index — the click target chain is:
   - whole-card click  → title anchor (overlay)
   - xref click        → xref anchor (above overlay, beats it) */
.quest-card-title-link {
  text-decoration: none;
  color: inherit;
}
.quest-card-title-link::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
}
.quest-card-title-link:focus-visible {
  outline: none;
}
.quest-card:focus-within {
  border-top-color: var(--ink);
}
/* Pitch xrefs need to lift above the title overlay so they're
   independently clickable; otherwise the overlay swallows the click. */
.quest-card-pitch .xref {
  position: relative;
  z-index: 2;
}
.quest-card-status {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-tertiary);
}
.quest-card-progress {
  color: var(--ink-secondary);
}
.quest-card--touched {
  border-top-color: var(--ink);
}
.quest-card-title {
  font-family: var(--serif);
  font-size: 22px;
  line-height: 1.2;
  font-weight: 500;
  margin: 0;
  color: var(--ink);
  letter-spacing: -0.005em;
}
.quest-card:hover .quest-card-title-link {
  text-decoration: underline;
  text-decoration-color: var(--ink-tertiary);
  text-underline-offset: 3px;
}
.quest-card-pitch {
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink-secondary);
}
.quest-card-pitch p { margin: 0 0 8px; }
.quest-card-pitch p:last-child { margin-bottom: 0; }

/* A representative voice-line from the opening scene, pulled onto
   the card. Same hairline-bar register as in-quest voices but more
   compact: one row, small caps voice-name, then the line. Tells
   the reader at a glance whether they're stepping into a Skeptic's
   doubt or an Operator's pragmatism. */
.quest-card-voice {
  display: grid;
  grid-template-columns: 3px auto 1fr;
  align-items: baseline;
  gap: 0 10px;
  margin-top: 4px;
  padding: 8px 0 0;
  border-top: 1px solid var(--hairline);
}
.quest-card-voice::before {
  content: '';
  grid-column: 1;
  align-self: stretch;
  background: var(--voice-color, var(--ink-tertiary));
  width: 3px;
  margin-top: 2px;
  margin-bottom: 2px;
  opacity: 0.85;
}
.quest-card-voice-name {
  grid-column: 2;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--voice-color, var(--ink-tertiary));
  font-weight: 500;
  white-space: nowrap;
}
.quest-card-voice-line {
  grid-column: 3;
  font-family: var(--serif);
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink-secondary);
  font-style: italic;
  /* Clamp to two lines so cards stay grid-friendly across quests
     with longer barges. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* When the line actually overflows past two lines, JS adds the
   --clipped flag. Soft fade-to-paper on the bottom: the text
   trails off rather than being severed by a hard line edge.
   Whisper-quiet — looks like ink soaking into paper, just at
   the cut. */
.quest-card-voice-line--clipped {
  -webkit-mask-image: linear-gradient(to bottom, black 72%, transparent 100%);
          mask-image: linear-gradient(to bottom, black 72%, transparent 100%);
}
.quest-card-voice-line em { font-style: normal; }
.quest-card-voice-line strong { font-weight: 600; }

.empty-note {
  font-family: var(--serif);
  font-style: italic;
  color: var(--ink-tertiary);
  padding: 32px 0;
}

/* ============================================================
   read-toggle pill (entity pages)
   ============================================================ */
/* Lives in the article-header. Shows whether the system thinks the
   user has read this entity, with one-click toggle. Honest about
   state, correctable when wrong. */
.read-toggle {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  margin-top: 16px;
  padding: 6px 12px;
  background: transparent;
  border: 1px solid var(--hairline);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-secondary);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.read-toggle:hover {
  border-color: var(--ink-tertiary);
  color: var(--ink);
}
.read-toggle--on {
  background: rgba(28, 26, 23, 0.06);
  border-color: var(--ink-tertiary);
  color: var(--ink);
}
.read-toggle--on:hover {
  background: transparent;
  border-color: var(--ink);
}
.read-toggle-mark {
  font-family: var(--serif);
  font-size: 14px;
  line-height: 1;
  color: var(--ink-tertiary);
}
.read-toggle--on .read-toggle-mark {
  color: var(--ink);
}
.read-toggle-hint {
  font-size: 9px;
  letter-spacing: 0.04em;
  color: var(--ink-tertiary);
  opacity: 0;
  transition: opacity 120ms ease;
  text-transform: lowercase;
  font-style: italic;
}
.read-toggle--on:hover .read-toggle-hint { opacity: 1; }

/* ============================================================
   site footer
   ============================================================ */
.site-footer {
  margin-top: auto;
  padding: 24px 24px 20px;
  border-top: 1px solid var(--hairline);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.site-footer-mark {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--ink-secondary);
}
/* ============================================================
   misc
   ============================================================ */
.status {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* ============================================================
   bridges page — /bridges.html
   Every cross-topic edge in the corpus, grouped by topic-pair.
   Hairline motif: the two concepts with a short ochre dashed
   rule between them, same warm-ochre vocabulary as the map.
   ============================================================ */
.bridges-page {
  max-width: 860px;
  margin: 0 auto;
  padding: 32px 24px 72px;
}
.bridges-header {
  margin-bottom: 40px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--hairline);
}
.bridges-title {
  font-family: var(--serif);
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 500;
  letter-spacing: -0.018em;
  margin: 0 0 10px;
}
.bridges-standfirst {
  font-family: var(--serif);
  font-size: 16.5px;
  line-height: 1.5;
  color: var(--ink-secondary);
  max-width: 56ch;
  margin: 0 0 8px;
}
.bridges-meta {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0;
}

/* Topic-pair group */
.bridges-group {
  margin: 0 0 48px;
}
.bridges-group-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 18px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--ochre-1);
}
.bridges-group-pair {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex: 1;
}
.bridges-group-topic {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--chip-color, var(--ink));
}
.bridges-group-dash {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink-tertiary);
}
.bridges-group-count {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.06em;
  color: var(--ink-tertiary);
  white-space: nowrap;
}

/* Bridge entries list */
.bridges-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.bridges-entry {
  padding: 18px 0;
  border-bottom: 1px solid var(--hairline);
}
.bridges-entry:last-child {
  border-bottom: none;
}

/* The pair row: left concept — ochre dashed motif — right concept */
.bridges-pair {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

/* The typographic bridge motif: a short ochre dashed hairline rule */
.bridges-join {
  flex: 0 0 auto;
  display: block;
  width: 32px;
  height: 0;
  border-top: 1px dashed var(--ochre-2);
  opacity: 0.7;
}

/* Each concept in the pair */
.bridges-concept {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.bridges-concept-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  text-decoration: none;
  color: var(--ink);
  border-bottom: 1px solid var(--hairline);
  transition: border-color 120ms ease;
}
.bridges-concept-link:hover {
  border-bottom-color: var(--ink);
}
.bridges-concept-glyph svg {
  width: 20px;
  height: 12px;
  display: block;
}
.bridges-concept-title {
  font-family: var(--serif);
  font-size: 15.5px;
  font-weight: 500;
  line-height: 1.2;
}
.bridges-concept-topic {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--chip-color, var(--ink-tertiary));
}

/* Bridge caption: authored text in serif, fallback dimmer */
.bridges-caption {
  font-family: var(--serif);
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--ink-secondary);
  max-width: 64ch;
  margin: 0;
  padding-left: 0;
}
.bridges-caption--fallback {
  color: var(--ink-tertiary);
  font-style: italic;
}

/* Human bridges section — reuses territory-human-bridge* classes for
   the list items; only needs a distinct header wrapper. */
.bridges-human {
  margin-top: 56px;
  padding-top: 22px;
  border-top: 1px solid var(--hairline);
}
.bridges-human-head {
  text-align: center;
  margin-bottom: 20px;
}
.bridges-human-eyebrow {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0 0 6px;
}
.bridges-human-title {
  font-family: var(--serif);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.012em;
  margin: 0;
}
.bridges-human-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  border-top: 1px solid var(--hairline);
  border-left: 1px solid var(--hairline);
}
/* Override territory-human-bridge borders for the bridges-page grid */
.bridges-human-list .territory-human-bridge {
  border-right: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}

/* empty state */
.bridges-empty {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-tertiary);
  letter-spacing: 0.06em;
}

/* territory header: ochre tint for the bridges tertiary link */
.territory-expedition-link--bridges {
  color: var(--ochre-2);
}

@media (max-width: 600px) {
  .bridges-page { padding: 20px 16px 56px; }
  .bridges-pair { gap: 8px; }
  .bridges-join { width: 20px; }
  .bridges-human-list { grid-template-columns: 1fr; }
}
