/* =============================================
   Fian Ilchruinne — Main Stylesheet
   Accessible, readable without JS; JS enhances.
   ============================================= */

/* --- Custom Properties --- */
:root {
  --color-bg: #0d0f1a;
  --color-surface: #13162a;
  --color-surface-2: #1c2040;
  --color-border: #2e3460;
  --color-text: #e8eaf6;
  --color-text-muted: #8890bb;
  --color-accent: #7c9ef5;
  --color-accent-hover: #a8beff;
  --color-canon: #ffd54f;
  --color-pov-active: #7c9ef5;
  --color-pov-bg: #1a1e36;
  --color-skip-bg: #7c9ef5;
  --color-skip-text: #0d0f1a;
  --color-title: var(--color-accent);
  --font-body: "Georgia", "Times New Roman", serif;
  --font-ui: system-ui, "Segoe UI", Roboto, sans-serif;
  --font-mono: "Courier New", Courier, monospace;
  --font-size-root: 18px;
  --font-size-root-sm: 16px;
  --line-height-prose: 1.7;
  --max-prose: 72ch;
  --measure-ui: 70ch;
  --prose-gap: 1rem;
  --hero-height: 320px;
  --hero-height-sm: 180px;
  --radius: 4px;
  --radius-lg: 8px;
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 2rem;
  --spacing-xl: 3rem;
  --transition: 200ms ease;
}

/* --- Presentation modes ---------------------------------------------------

   THE THIRD AXIS. A build's look is decided by three independent things:

     EDITION      who this domain is           lib/editions.js
     THEME        what colour it is            theme-<name>.css (generated)
     PRESENTATION how it wants to be read      this section

   Palette and posture were one axis until now, and that conflated two
   different questions the same way `theme` once conflated palette and
   identity (see lib/editions.js's "WHY NOT KEEP USING theme FOR THIS"). A
   contemplative domain and a children's domain can want the same colours and
   still need different measure, leading and tap targets; the codex site wants
   the fellowship palette and an index's density. So the mode lives on
   :root as a data attribute (src/_includes/base.njk writes it from
   edition.presentation) and moves ONLY custom properties.

   WHY IT MOVES ONLY PROPERTIES, and this is the rule that keeps it cheap:
   every theme-<name>.css is main.css with a swapped :root
   (scripts/generate-themes.js), so anything written here ships to all nine
   themes by construction. If a mode changed selectors instead of values, the
   themes and the modes would multiply - 9 x 4 combinations to keep straight
   rather than 9 palettes and 4 posture overlays that compose. The two
   structural rules below are the deliberate exceptions, each noted with why a
   property could not carry it - and a third, the "From the Archive" aside's
   placement by posture, sits with that aside's own styles further down.

   A MODE IS A POSTURE, NOT A TIER. The audience tiers (children -> young adult
   -> general -> contemplative, story-bible-summary.md) are about register and
   block length. These are about what the reader's hands and eyes are doing:
   reading a story straight through, being helped to finish a block, sitting
   with a page, or looking something up. That is why the young-adult and
   general tiers share `story` - same posture, different prose - while the
   codex site gets `archive` despite sharing the fellowship's palette and
   tier. Add a mode when a posture is genuinely unserved, not when a domain
   wants to feel different; that is what the palette and the copy are for. */

/* PRIMER - the children's domain (undercover-pets.com).
   Bigger type, a shorter line, more air between paragraphs: the constraint the
   tier note names is COMPLETABILITY, and a block that looks finishable is
   finished. The measure is the load-bearing value - ~58ch is roughly nine or
   ten words, which is where a developing reader stops losing the line. */
:root[data-presentation="primer"] {
  --font-size-root: 20px;
  --font-size-root-sm: 17px;
  --line-height-prose: 1.75;
  --max-prose: 58ch;
  --measure-ui: 60ch;
  --prose-gap: 1.15rem;
  --hero-height: 280px;
}
/* Structural, because the padding is in px and would not scale with the root
   size: the POV buttons are the site's one real tap target and a child on a
   tablet gets the 44px-ish box the WCAG target-size guidance asks for. */
:root[data-presentation="primer"] .pov-btn {
  padding: 9px 18px;
}

/* ARCHIVE - a filed record, browsed rather than read (fellowshipoflight.site).
   Denser and wider: the reader arrived looking for a document, so more of the
   index should be on screen at once, and codex prose is reference prose. */
:root[data-presentation="archive"] {
  --font-size-root: 17px;
  --font-size-root-sm: 16px;
  --line-height-prose: 1.6;
  --max-prose: 78ch;
  --measure-ui: 76ch;
  --prose-gap: 0.85rem;
  --hero-height: 200px;
  --hero-height-sm: 150px;
}
/* Structural, because alignment is not a value any token here holds: an
   archive's front door is a desk, not a poster. Everything below the hero on
   every other page is already left-aligned, so this is the homepage falling
   into line rather than a new look. */
:root[data-presentation="archive"] .home-hero {
  text-align: left;
}

/* CONTEMPLATIVE - the devotional domains (church-space.site,
   fellowshipoflight.org). Slow it down: the longest leading on the site, a
   narrow measure, and quiet titles. The heading colour is the one departure
   worth naming - a contemplative page reads better when nothing on it is
   competing for the eye, so titles take the body colour and the accent is left
   to do only what accents must (links, focus, the POV rail). */
:root[data-presentation="contemplative"] {
  --font-size-root: 19px;
  --line-height-prose: 1.9;
  --max-prose: 62ch;
  --measure-ui: 62ch;
  --prose-gap: 1.3rem;
  --color-title: var(--color-text);
  --hero-height: 260px;
}

/* --- Reset --- */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* --- Base --- */
html {
  font-size: var(--font-size-root);
  scroll-behavior: smooth;
}

body {
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  line-height: var(--line-height-prose);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Fallback for links with no component-specific rule (e.g. inline links in
   hero/intro prose) so they never fall back to the browser's default blue. */
a {
  color: var(--color-accent);
}
a:hover,
a:focus {
  color: var(--color-accent-hover);
}

/* --- Accessibility: Skip Link --- */
.skip-link {
  position: absolute;
  top: -100%;
  left: var(--spacing-md);
  background: var(--color-skip-bg);
  color: var(--color-skip-text);
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--radius);
  font-family: var(--font-ui);
  font-weight: 700;
  text-decoration: none;
  z-index: 9999;
  transition: top var(--transition);
}
.skip-link:focus {
  top: var(--spacing-sm);
}

/* --- Header --- */
.site-header {
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
  padding: var(--spacing-md) var(--spacing-lg);
  position: sticky;
  top: 0;
  z-index: 100;
}

.site-header__inner {
  max-width: 1100px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-md);
  flex-wrap: wrap;
}

.site-logo {
  color: var(--color-accent);
  text-decoration: none;
  font-family: var(--font-ui);
  font-size: 1.2rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
}
.site-logo:hover,
.site-logo:focus {
  color: var(--color-accent-hover);
}
.site-logo__icon {
  font-size: 1.5rem;
}

.site-nav__list {
  list-style: none;
  display: flex;
  gap: var(--spacing-lg);
  flex-wrap: wrap;
}

.site-nav__list a {
  color: var(--color-text-muted);
  text-decoration: none;
  font-family: var(--font-ui);
  font-size: 0.9rem;
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
  transition: color var(--transition), border-color var(--transition);
}
.site-nav__list a:hover,
.site-nav__list a:focus {
  color: var(--color-text);
  border-bottom-color: var(--color-accent);
}
.site-nav__list a[aria-current="page"] {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}

/* --- Search --- */
.site-search {
  position: relative;
}
.site-search__input {
  width: 12rem;
  max-width: 100%;
  padding: 6px var(--spacing-sm);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  color: var(--color-text);
  font-family: var(--font-ui);
  font-size: 0.9rem;
}
.site-search__input::placeholder {
  color: var(--color-text-muted);
}
.site-search__input:focus {
  outline: none;
  border-color: var(--color-accent);
}
.site-search__results {
  position: absolute;
  top: calc(100% + var(--spacing-xs));
  left: 0;
  z-index: 20;
  width: 20rem;
  max-width: min(90vw, 20rem);
  max-height: 70vh;
  overflow-y: auto;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.site-search__empty {
  margin: 0;
  padding: var(--spacing-sm) var(--spacing-md);
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  font-size: 0.85rem;
}
.site-search__list {
  list-style: none;
}
.site-search__result + .site-search__result {
  border-top: 1px solid var(--color-border);
}
.site-search__result-link {
  display: block;
  padding: var(--spacing-sm) var(--spacing-md);
  text-decoration: none;
}
.site-search__result-link:hover,
.site-search__result-link:focus {
  background: var(--color-surface-2);
}
.site-search__result-title {
  display: block;
  color: var(--color-accent);
  font-family: var(--font-ui);
  font-size: 0.9rem;
  margin-bottom: 2px;
}
.site-search__result-excerpt {
  display: block;
  color: var(--color-text-muted);
  font-size: 0.8rem;
  line-height: 1.4;
}
.site-search__result-excerpt mark {
  background: none;
  color: var(--color-text);
  font-weight: 700;
}

/* --- Reading-mode switcher -------------------------------------------------

   The reader-side control for the presentation modes above (base.njk renders
   it, src/js/presentation.js drives it). Every value here is a token, so the
   control inherits whichever palette the domain ships and needs no per-theme
   rule of its own - and it scales with --font-size-root like everything else,
   which means picking "Large print" visibly enlarges the control that just
   set it. That is feedback, not a bug.

   Colour choices are the ones scripts/check-contrast.js already checks: muted
   text on --color-surface for a resting option, --color-bg on
   --color-pov-active for the chosen one. Nothing new to measure. */
.reading-mode {
  position: relative;
}
.reading-mode__details {
  position: relative;
}
/* Matches .site-nav__list a, so the control reads as part of the header rather
   than as a widget dropped into it. */
.reading-mode__summary {
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  font-size: 0.9rem;
  padding: 2px 0;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  list-style: none;
  transition: color var(--transition), border-color var(--transition);
}
.reading-mode__summary::-webkit-details-marker {
  display: none;
}
.reading-mode__summary:hover,
.reading-mode__summary:focus-visible {
  color: var(--color-text);
  border-bottom-color: var(--color-accent);
}
.reading-mode__details[open] .reading-mode__summary {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}
.reading-mode__panel {
  position: absolute;
  top: calc(100% + var(--spacing-xs));
  right: 0;
  z-index: 20;
  width: 17rem;
  max-width: min(90vw, 17rem);
  padding: var(--spacing-md);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.reading-mode__note {
  margin-bottom: var(--spacing-sm);
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  font-size: 0.8rem;
  line-height: 1.5;
}
.reading-mode__options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
}
/* Deliberately the .pov-btn shape: the site already has one idiom for "pick one
   of these and the page changes", and a reader who has used the POV controls
   has met this control before. */
.reading-mode__btn {
  font-family: var(--font-ui);
  font-size: 0.85rem;
  padding: 6px 14px;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.reading-mode__btn:hover,
.reading-mode__btn:focus {
  background: var(--color-surface-2);
  color: var(--color-text);
  border-color: var(--color-accent);
  outline: none;
}
.reading-mode__btn[aria-pressed="true"] {
  background: var(--color-pov-active);
  color: var(--color-bg);
  border-color: var(--color-pov-active);
  font-weight: 700;
}
/* The "this site's setting" option takes a row of its own. Two reasons, and the
   second is the real one: its label is the longest here and wraps inside the
   pill at this panel width, and it is not a peer of the other three - it is
   what the domain chose, with the overrides beneath it. */
.reading-mode__btn[data-presentation-mode=""] {
  flex-basis: 100%;
}
.reading-mode__btn-note {
  font-weight: 400;
  opacity: 0.85;
}

/* --- Main Content --- */
.main-content {
  flex: 1;
  padding: var(--spacing-xl) var(--spacing-lg);
  max-width: 1100px;
  margin: 0 auto;
  width: 100%;
}

/* --- Footer --- */
.site-footer {
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
  padding: var(--spacing-lg);
  text-align: center;
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--color-text-muted);
}
.site-footer a {
  color: var(--color-accent);
}
.site-lang-note {
  max-width: 62ch;
  margin: var(--spacing-sm) auto 0;
  font-size: 0.8rem;
  opacity: 0.85;
}
.site-theme-audio {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-sm);
  margin-bottom: var(--spacing-md);
}
.site-theme-audio__label {
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.site-theme-audio__player {
  max-width: 320px;
  width: 100%;
}

/* --- Breadcrumbs --- */
.breadcrumb {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-xs);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--color-text-muted);
  margin-bottom: var(--spacing-lg);
}
.breadcrumb li::after {
  content: " /";
  margin-left: var(--spacing-xs);
}
.breadcrumb li:last-child::after {
  content: "";
}
.breadcrumb a {
  color: var(--color-accent);
  text-decoration: none;
}
.breadcrumb a:hover {
  text-decoration: underline;
}

/* --- Chapter --- */
.chapter-header {
  margin-bottom: var(--spacing-xl);
  border-bottom: 1px solid var(--color-border);
  padding-bottom: var(--spacing-lg);
}

.chapter-meta {
  margin-bottom: var(--spacing-sm);
}

.chapter-label {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--color-text-muted);
  letter-spacing: 0.1em;
  background: var(--color-surface-2);
  padding: 2px 8px;
  border-radius: var(--radius);
}

.chapter-title {
  font-size: 2rem;
  line-height: 1.2;
  color: var(--color-text);
  margin: var(--spacing-md) 0;
}

.chapter-details {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-md) var(--spacing-lg);
  font-family: var(--font-ui);
  font-size: 0.9rem;
}
.chapter-detail {
  display: flex;
  gap: var(--spacing-sm);
}
.chapter-detail dt {
  color: var(--color-text-muted);
}

.chapter-detail dd {
  color: var(--color-text);
  font-style: italic;
}

.journal-entry__date {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--color-text-muted);
  margin-top: var(--spacing-sm);
}

.chapter-tags {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
  margin-top: var(--spacing-md);
}
.chapter-tag {
  font-family: var(--font-ui);
  font-size: 0.78rem;
  background: var(--color-surface-2);
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
  padding: 2px 8px;
  border-radius: 999px;
}

/* --- POV Controls --- */
.pov-controls {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  flex-wrap: wrap;
  margin-bottom: var(--spacing-lg);
  padding: var(--spacing-md);
  background: var(--color-surface-2);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
}
.pov-controls__label {
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--color-text-muted);
  white-space: nowrap;
}
.pov-controls__buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
}

.pov-btn {
  font-family: var(--font-ui);
  font-size: 0.85rem;
  padding: 4px 14px;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.pov-btn:hover,
.pov-btn:focus {
  background: var(--color-surface);
  color: var(--color-text);
  border-color: var(--color-accent);
  outline: none;
}
.pov-btn--active,
.pov-btn[aria-pressed="true"] {
  background: var(--color-pov-active);
  color: var(--color-bg);
  border-color: var(--color-pov-active);
  font-weight: 700;
}

/* --- POV Blocks --- */
.pov-block {
  margin: var(--spacing-xl) 0;
  padding: var(--spacing-lg);
  border-left: 3px solid var(--color-border);
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
  background: var(--color-surface);
  transition: opacity var(--transition);
}
.pov-block[data-pov-hidden="true"] {
  display: none;
}

.pov-header {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  margin-bottom: var(--spacing-md);
  padding-bottom: var(--spacing-sm);
  border-bottom: 1px solid var(--color-border);
}
.pov-header__name {
  margin: 0;
  font-family: var(--font-ui);
  font-size: 0.85rem;
  line-height: inherit;
  font-weight: 700;
  color: var(--color-accent);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* Text for assistive readers only: the scene label the chapter body never
   showed visually, kept out of sight without leaving the accessibility tree
   (display:none would). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.pov-header__label {
  font-family: var(--font-ui);
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

.pov-block--galahad { border-left-color: #7c9ef5; }
.pov-block--elvira  { border-left-color: #f57c9e; }
.pov-block--aldera  { border-left-color: #9ef5a0; }
.pov-block--krenyi  { border-left-color: #f5d67c; }
.pov-block--thorn   { border-left-color: #f57c7c; }

/* The scene label in the chapter body: the same caption the scene-by-scene
   list below the chapter uses, so a scene is named once in the prose and
   once in the index in one voice. Visible since 2026-09-30 so a read-aloud
   that speaks visible text only announces the boundary. */
.scene__label {
  font-family: var(--font-ui);
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: var(--spacing-xl) 0 var(--spacing-sm);
}

/* --- Chapter Body Prose --- */
.chapter-body {
  max-width: var(--max-prose);
}

.chapter-body p {
  margin-bottom: var(--prose-gap);
}

.chapter-body h2 {
  font-size: 1.4rem;
  color: var(--color-accent);
  margin: var(--spacing-xl) 0 var(--spacing-md);
}

.chapter-body h3 {
  font-size: 1.1rem;
  color: var(--color-text);
  margin: var(--spacing-lg) 0 var(--spacing-sm);
}

.chapter-body em {
  color: var(--color-text-muted);
}

.chapter-body a {
  color: var(--color-accent);
  text-decoration: underline;
}
.chapter-body a:hover,
.chapter-body a:focus {
  color: var(--color-accent-hover);
}

.chapter-body blockquote {
  border-left: 3px solid var(--color-accent);
  padding-left: var(--spacing-md);
  margin: var(--spacing-lg) 0;
  font-style: italic;
  color: var(--color-text-muted);
}

/* --- Canon Facts --- */
.canon-facts {
  margin-top: var(--spacing-xl);
  padding: var(--spacing-md);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  max-width: var(--max-prose);
}
.canon-facts__heading {
  font-size: 1rem;
  font-family: var(--font-ui);
}
.canon-facts__toggle {
  background: none;
  border: none;
  color: var(--color-canon);
  font-family: var(--font-ui);
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
}
.canon-facts__toggle:hover,
.canon-facts__toggle:focus {
  text-decoration: underline;
  outline: none;
}
.canon-facts__list {
  margin-top: var(--spacing-md);
  padding-left: var(--spacing-lg);
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  font-size: 0.9rem;
  line-height: 1.6;
}
.canon-facts__list li + li {
  margin-top: var(--spacing-sm);
}

/* --- "In the Archive" (chapter backlinks) ---
   Borrows the .canon-facts box: the shape a reader already knows as "the
   Archive talking about the text rather than in it". Derived list, never
   authored; see lib/archive-backlinks.js. */
.archive-links {
  margin-top: var(--spacing-lg);
  padding: var(--spacing-md);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  max-width: var(--max-prose);
}
.archive-links__heading {
  font-family: var(--font-ui);
  font-size: 1rem;
  color: var(--color-text);
}
.archive-links__lede {
  margin-top: var(--spacing-xs);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--color-text-muted);
}
.archive-links__list {
  list-style: none;
  margin-top: var(--spacing-md);
  padding: 0;
  font-family: var(--font-ui);
  font-size: 0.9rem;
  line-height: 1.6;
}
.archive-links__list li + li {
  margin-top: var(--spacing-xs);
}
.archive-links__kind {
  margin-left: var(--spacing-sm);
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

/* --- "From the Archive" (the entries that accompany a chapter) ---
   The Archive beside the narrative: the entries for what the chapter meets,
   each with its own one-line account (lib/archive-companions.js; Dermot's
   direction of 2026-10-05, the Archive as the Book that accompanies the
   story). Same box as .canon-facts and .archive-links, since it is the same
   voice - the Archive talking about the text rather than in it. */
.chapter-reading {
  display: block;
}
.archive-companions {
  margin-top: var(--spacing-lg);
  padding: var(--spacing-md);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  max-width: var(--max-prose);
}
.archive-companions__heading {
  font-family: var(--font-ui);
  font-size: 1rem;
  color: var(--color-text);
}
.archive-companions__lede {
  margin-top: var(--spacing-xs);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--color-text-muted);
}
.archive-companions__list {
  list-style: none;
  margin-top: var(--spacing-md);
  padding: 0;
  font-family: var(--font-ui);
  font-size: 0.9rem;
  line-height: 1.6;
}
.archive-companions__item + .archive-companions__item {
  margin-top: var(--spacing-md);
}
.archive-companions__kind {
  margin-left: var(--spacing-sm);
  font-size: 0.8rem;
  color: var(--color-text-muted);
}
.archive-companions__text {
  margin-top: var(--spacing-xs);
  font-size: 0.875rem;
  color: var(--color-text);
}
/* Structural, and the third deliberate exception to "a mode moves only
   properties" (see the presentation-mode note at the top of this file):
   WHERE the aside sits is not a value any token holds. In the archive and
   contemplative postures, reference density and sitting with a page, the
   Archive accompanies the prose in a margin column once the viewport has
   room for both; in story and primer, reading straight through, it stays
   under the text. The DOM order is body then aside, so the two layouts show
   the same page, and the grid only ever adds a column. Dermot's choice,
   2026-10-05, re-grounding the 2026-10-02 placement: reasons stay under the
   text; vocabulary may sit beside it, where the posture asks. */
@media (min-width: 1100px) {
  :root[data-presentation="archive"] .chapter-reading,
  :root[data-presentation="contemplative"] .chapter-reading {
    display: grid;
    grid-template-columns: minmax(0, var(--max-prose)) minmax(14rem, 18rem);
    column-gap: var(--spacing-xl);
    align-items: start;
  }
  :root[data-presentation="archive"] .archive-companions,
  :root[data-presentation="contemplative"] .archive-companions {
    margin-top: 0;
    position: sticky;
    top: var(--spacing-md);
    max-height: calc(100vh - 2 * var(--spacing-md));
    overflow-y: auto;
  }
}

/* --- Scene-by-scene links (chapter overview) --- */
.chapter-scenes {
  margin: var(--spacing-xl) 0;
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--color-border);
}
.chapter-scenes__heading {
  font-size: 1.1rem;
  color: var(--color-accent);
  margin-bottom: var(--spacing-md);
}
.chapter-scene {
  margin-bottom: var(--spacing-md);
}
.chapter-scene__label {
  font-family: var(--font-ui);
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: var(--spacing-sm);
}
.chapter-scene__povs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
}
.chapter-scene__povs li a {
  display: inline-block;
  padding: 4px 14px;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  color: var(--color-text);
  text-decoration: none;
  font-family: var(--font-ui);
  font-size: 0.85rem;
}
.chapter-scene__povs li a:hover,
.chapter-scene__povs li a:focus {
  border-color: var(--color-accent);
  color: var(--color-accent);
}

/* --- Individual scene/POV pages --- */
.scene-pov-switch {
  margin: var(--spacing-lg) 0;
  font-family: var(--font-ui);
  font-size: 0.9rem;
}
.scene-pov-switch__label {
  color: var(--color-text-muted);
  margin-bottom: var(--spacing-sm);
}
.scene-pov-switch__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
}
.scene-pov-switch__list a {
  color: var(--color-accent);
  text-decoration: underline;
}
.scene-pov-switch__link {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-xs);
}
.scene-pov-switch__portrait {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  object-position: top;
  border: 1px solid var(--color-border);
}

/* Scene-POV heading: the POV character's portrait beside their name, a
   visual anchor for whose viewpoint the page holds. Renders only when the
   character page exists and carries an image - no portrait, no gap. */
.scene-pov-heading {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
}
.scene-pov-heading__portrait {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  object-fit: cover;
  object-position: top;
  border: 2px solid var(--color-border);
  display: block;
}
.scene-pov-heading__portrait-link {
  flex-shrink: 0;
  line-height: 0;
}

/* --- Chapter Pagination --- */
.chapter-pagination {
  display: flex;
  justify-content: space-between;
  margin-top: var(--spacing-xl);
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--color-border);
  font-family: var(--font-ui);
  font-size: 0.9rem;
  gap: var(--spacing-md);
}
.chapter-pagination a {
  color: var(--color-accent);
  text-decoration: none;
  max-width: 48%;
}
.chapter-pagination a:hover {
  text-decoration: underline;
}
.chapter-pagination__next {
  margin-left: auto;
  text-align: right;
}

/* --- Home Page --- */
.home-hero {
  text-align: center;
  padding: var(--spacing-xl) 0;
  margin-bottom: var(--spacing-xl);
}
/* Auto-advancing crossfade of curated character portraits (src/index.md's
   heroCharacters, per-theme). CSS-only: every slide shares one keyframe
   animation and is staggered purely via animation-delay (loop.index0 * 6s in
   the template's :nth-child order below), so no JS is needed to drive it.
   Each slide's fade-OUT window deliberately overlaps the next slide's
   fade-IN window (the outgoing slide fades out over [slot, slot+fade] while
   the incoming one fades in over the same interval) so the transition is a
   true crossfade rather than a dip to the background between portraits.

   ONE KEYFRAME VARIANT PER SLIDE COUNT, and the range deliberately runs wider
   than the two counts lib/editions.js currently lists (6 by default, 5 for
   undercover-pets.com). The count is not a property of the edition: src/index.md
   resolves heroCharacterIds against the FILTERED characters collection, so a
   narrowed deploy (deploy.conf's CHARACTERS/TOPICS/THREADS) drops any hero
   character its filter excludes, and one missing tag on one character page
   silently changes N. With a rule only for 5 and 6, any other N left every
   slide with no animation-name at all - so all of them kept the `opacity: 0`
   below and the whole hero went blank, losing every image rather than the one
   character that was filtered out. That is the opposite of the graceful
   degradation lib/editions.js's heroCharacterIds comment promises, and it fails
   on the domain most likely to hit it. The :first-child fallback below covers
   N beyond this range: one still portrait rather than an empty box. */
.home-hero__slideshow {
  position: relative;
  display: block;
  width: 100%;
  height: var(--hero-height);
  overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  margin-bottom: var(--spacing-lg);
  background: var(--color-surface);
}
.home-hero__slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  opacity: 0;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}
/* Shows the first portrait when no --nN rule matches: a single slide, or a
   count past the range below. Overridden by the animation whenever one runs,
   since an animated value beats the declared one for the whole active period
   (delay 0s + infinite iteration = always, for :first-child). */
.home-hero__slide:first-child {
  opacity: 1;
}
.home-hero__slideshow--n8 .home-hero__slide {
  animation-name: home-hero-crossfade-8;
  animation-duration: 48s;
}
.home-hero__slideshow--n7 .home-hero__slide {
  animation-name: home-hero-crossfade-7;
  animation-duration: 42s;
}
.home-hero__slideshow--n6 .home-hero__slide {
  animation-name: home-hero-crossfade-6;
  animation-duration: 36s;
}
.home-hero__slideshow--n5 .home-hero__slide {
  animation-name: home-hero-crossfade-5;
  animation-duration: 30s;
}
.home-hero__slideshow--n4 .home-hero__slide {
  animation-name: home-hero-crossfade-4;
  animation-duration: 24s;
}
.home-hero__slideshow--n3 .home-hero__slide {
  animation-name: home-hero-crossfade-3;
  animation-duration: 18s;
}
.home-hero__slideshow--n2 .home-hero__slide {
  animation-name: home-hero-crossfade-2;
  animation-duration: 12s;
}
.home-hero__slide:nth-child(1) { animation-delay: 0s; }
.home-hero__slide:nth-child(2) { animation-delay: 6s; }
.home-hero__slide:nth-child(3) { animation-delay: 12s; }
.home-hero__slide:nth-child(4) { animation-delay: 18s; }
.home-hero__slide:nth-child(5) { animation-delay: 24s; }
.home-hero__slide:nth-child(6) { animation-delay: 30s; }
.home-hero__slide:nth-child(7) { animation-delay: 36s; }
.home-hero__slide:nth-child(8) { animation-delay: 42s; }
@keyframes home-hero-crossfade-8 {
  0% { opacity: 0; }
  2.08% { opacity: 1; }
  12.5% { opacity: 1; }
  14.58% { opacity: 0; }
  100% { opacity: 0; }
}
@keyframes home-hero-crossfade-7 {
  0% { opacity: 0; }
  2.38% { opacity: 1; }
  14.29% { opacity: 1; }
  16.67% { opacity: 0; }
  100% { opacity: 0; }
}
@keyframes home-hero-crossfade-6 {
  0% { opacity: 0; }
  2.78% { opacity: 1; }
  16.67% { opacity: 1; }
  19.45% { opacity: 0; }
  100% { opacity: 0; }
}
@keyframes home-hero-crossfade-5 {
  0% { opacity: 0; }
  3.33% { opacity: 1; }
  20% { opacity: 1; }
  23.33% { opacity: 0; }
  100% { opacity: 0; }
}
@keyframes home-hero-crossfade-4 {
  0% { opacity: 0; }
  4.17% { opacity: 1; }
  25% { opacity: 1; }
  29.17% { opacity: 0; }
  100% { opacity: 0; }
}
@keyframes home-hero-crossfade-3 {
  0% { opacity: 0; }
  5.56% { opacity: 1; }
  33.33% { opacity: 1; }
  38.89% { opacity: 0; }
  100% { opacity: 0; }
}
@keyframes home-hero-crossfade-2 {
  0% { opacity: 0; }
  8.33% { opacity: 1; }
  50% { opacity: 1; }
  58.33% { opacity: 0; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .home-hero__slide {
    animation: none;
    opacity: 0;
  }
  .home-hero__slide:first-child {
    opacity: 1;
  }
}
.home-hero__title {
  font-size: 3rem;
  color: var(--color-accent);
  margin-bottom: var(--spacing-md);
  line-height: 1.1;
}
.home-hero__subtitle {
  font-size: 1.1rem;
  color: var(--color-text-muted);
  max-width: 55ch;
  margin: 0 auto var(--spacing-lg);
}
.home-hero__cta {
  display: inline-block;
  background: var(--color-accent);
  color: var(--color-bg);
  text-decoration: none;
  padding: var(--spacing-sm) var(--spacing-xl);
  border-radius: 999px;
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 1rem;
  transition: background var(--transition);
}
.home-hero__cta:hover,
.home-hero__cta:focus {
  background: var(--color-accent-hover);
}
/* Two doors: chapter one, and the newest entry in the record. They wrap to a
   column on a narrow screen rather than shrinking, so neither hit area gets
   smaller than a thumb. */
.home-hero__ctas {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--spacing-md);
}
/* The secondary door is outlined rather than filled, and the outline is an
   inset shadow rather than a border so both buttons keep the same box: a
   2px border on one of them would make it two pixels taller than its
   neighbour for no reason a reader could name. */
.home-hero__cta--secondary {
  background: transparent;
  color: var(--color-accent);
  box-shadow: inset 0 0 0 2px var(--color-accent);
}
.home-hero__cta--secondary:hover,
.home-hero__cta--secondary:focus {
  background: var(--color-surface-2);
  color: var(--color-accent-hover);
  box-shadow: inset 0 0 0 2px var(--color-accent-hover);
}
/* Windows high contrast, and any other forced-colours mode, drops both the
   fill and the inset shadow - which left the site's call to action reading as
   ordinary link text, because the shape was carrying all of the affordance.
   A real border is drawn in the user's own colours, so both doors keep their
   outline without the stylesheet choosing a colour the mode has overridden. */
@media (forced-colors: active) {
  .home-hero__cta {
    border: 2px solid currentColor;
  }
}

.home-sections {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--spacing-lg);
  margin-top: var(--spacing-xl);
}

.home-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--spacing-lg);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--transition), transform var(--transition);
  display: block;
}
.home-card:hover,
.home-card:focus {
  border-color: var(--color-accent);
  transform: translateY(-2px);
  outline: none;
}
.home-card__icon {
  font-size: 2rem;
  margin-bottom: var(--spacing-md);
  display: block;
}
.home-card__title {
  font-size: 1.2rem;
  font-family: var(--font-ui);
  font-weight: 700;
  color: var(--color-accent);
  margin-bottom: var(--spacing-sm);
}
.home-card__desc {
  font-size: 0.9rem;
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  line-height: 1.5;
}

/* --- Seasons / Listing Pages --- */
.page-hero-image {
  display: block;
  width: 100%;
  height: var(--hero-height);
  object-fit: cover;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  margin-bottom: var(--spacing-lg);
}
.page-title {
  font-size: 2rem;
  color: var(--color-title);
  margin-bottom: var(--spacing-lg);
}
.page-intro {
  font-size: 1rem;
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  max-width: var(--measure-ui);
  margin-bottom: var(--spacing-xl);
}

.thread-section {
  margin-bottom: var(--spacing-xl);
}
.thread-section__title {
  font-size: 1.8rem;
  font-family: var(--font-ui);
  color: var(--color-accent);
  margin-bottom: var(--spacing-sm);
}
.thread-section__title a {
  color: inherit;
  text-decoration: none;
}
.thread-section__title a:hover,
.thread-section__title a:focus {
  text-decoration: underline;
}
.thread-section__description {
  font-size: 0.95rem;
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  max-width: var(--measure-ui);
  margin-bottom: var(--spacing-lg);
}
.thread-badge {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  margin-bottom: var(--spacing-xl);
}
.thread-badge a {
  color: var(--color-accent);
}

.season-block {
  margin-bottom: var(--spacing-xl);
}
.season-block__title {
  font-size: 1.4rem;
  font-family: var(--font-ui);
  color: var(--color-text);
  margin-bottom: var(--spacing-md);
  padding-bottom: var(--spacing-sm);
  border-bottom: 1px solid var(--color-border);
}

.chapter-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-sm);
}
.chapter-list__item a {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--spacing-md);
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--radius);
  border: 1px solid transparent;
  text-decoration: none;
  transition: background var(--transition), border-color var(--transition);
}
.chapter-list__item a:hover,
.chapter-list__item a:focus {
  background: var(--color-surface-2);
  border-color: var(--color-border);
  outline: none;
}
.chapter-list__code {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-text-muted);
  white-space: nowrap;
}
.chapter-list__title {
  color: var(--color-text);
  font-size: 0.95rem;
}
.chapter-list__loc {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  font-style: italic;
  margin-left: auto;
}
/* The chapter's own one-paragraph summary (front-matter `description`),
   shown where a reader chooses a chapter and nowhere on the chapter itself
   (2026-10-02): the summary belongs before the choice, not beside the prose. */
.chapter-list__desc {
  flex-basis: 100%;
  font-family: var(--font-ui);
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--color-text-muted);
}

/* --- Codex --- */
.codex-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--spacing-md);
  margin-top: var(--spacing-lg);
}
.codex-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--spacing-md);
  text-decoration: none;
  color: inherit;
  display: block;
  transition: border-color var(--transition);
}
.codex-card:hover,
.codex-card:focus {
  border-color: var(--color-accent);
  outline: none;
}
.codex-card__thumb {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--radius-lg);
  margin-bottom: var(--spacing-sm);
}
.codex-card__category {
  font-family: var(--font-ui);
  font-size: 0.75rem;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: var(--spacing-xs);
}
.codex-card__title {
  font-family: var(--font-ui);
  font-size: 1rem;
  color: var(--color-accent);
  font-weight: 700;
}
.codex-card__institution {
  font-family: var(--font-ui);
  font-size: 0.8rem;
  color: var(--color-text-muted);
  margin-top: var(--spacing-xs);
}

.codex-entry__header {
  margin-bottom: var(--spacing-xl);
  padding-bottom: var(--spacing-lg);
  border-bottom: 1px solid var(--color-border);
}
.codex-entry__title {
  font-size: 2rem;
  color: var(--color-text);
  margin-bottom: var(--spacing-sm);
}
.codex-entry__category {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--color-text-muted);
}
.codex-entry__byline {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--color-text-muted);
}
/* Borrows the .canon-facts box treatment deliberately: a reader who has met
   one of those on a chapter already reads this shape as "the Archive talking
   about the text rather than in it". */
.codex-entry__status {
  margin: var(--spacing-md) 0;
  padding: var(--spacing-md);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-lg);
  max-width: var(--max-prose);
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--color-text-muted);
}
.codex-entry__status p { margin: 0; }
.codex-entry__status strong { color: var(--color-text); }
/* Permanent citation link at the foot of a chapter. Quiet by design - it is
   reference apparatus for someone who came to cite, not something to compete
   with the prose. */
.chapter-permalink {
  margin: var(--spacing-lg) 0 0;
  padding-top: var(--spacing-md);
  border-top: 1px solid var(--color-border);
  max-width: var(--max-prose);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--color-text-muted);
}
.chapter-permalink code { font-size: 0.95em; }
.codex-entry__body {
  max-width: var(--max-prose);
}
.codex-entry__body p { margin-bottom: var(--spacing-md); }
.codex-entry__body h2 {
  font-size: 1.3rem;
  color: var(--color-accent);
  margin: var(--spacing-xl) 0 var(--spacing-md);
}
.codex-entry__body h3 {
  font-size: 1.1rem;
  color: var(--color-text);
  margin: var(--spacing-lg) 0 var(--spacing-sm);
}
.codex-entry__body ul,
.codex-entry__body ol {
  padding-left: var(--spacing-lg);
  margin-bottom: var(--spacing-md);
}
.codex-entry__body li { margin-bottom: var(--spacing-xs); }
.codex-entry__body strong { color: var(--color-text); }
.codex-entry__body em { color: var(--color-text-muted); }
.codex-entry__body a {
  color: var(--color-accent);
  text-decoration: underline;
}
.codex-entry__body a:hover,
.codex-entry__body a:focus {
  color: var(--color-accent-hover);
}
.codex-entry__footer {
  margin-top: var(--spacing-xl);
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--color-border);
  font-family: var(--font-ui);
  font-size: 0.9rem;
}
.codex-entry__footer a {
  color: var(--color-accent);
  text-decoration: none;
}
.codex-entry__footer a:hover { text-decoration: underline; }

/* --- Timeline --- */
.timeline {
  max-width: var(--max-prose);
  position: relative;
}
.timeline::before {
  content: "";
  position: absolute;
  left: 1rem;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--color-border);
}
.timeline-event {
  display: flex;
  gap: var(--spacing-lg);
  padding: var(--spacing-md) 0;
  padding-left: 3rem;
  position: relative;
}
.timeline-event::before {
  content: "";
  position: absolute;
  left: calc(1rem - 4px);
  top: calc(var(--spacing-md) + 6px);
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--color-accent);
  border: 2px solid var(--color-bg);
}
.timeline-event__time {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--color-text-muted);
  white-space: nowrap;
  flex-shrink: 0;
}
.timeline-event__content {}
.timeline-event__title {
  font-family: var(--font-ui);
  font-size: 0.95rem;
  color: var(--color-text);
  font-weight: 700;
  margin-bottom: var(--spacing-xs);
}
.timeline-event__desc {
  font-size: 0.9rem;
  color: var(--color-text-muted);
  font-family: var(--font-ui);
}

/* --- Responsive --- */
@media (max-width: 640px) {
  html { font-size: var(--font-size-root-sm); }
  .home-hero__title { font-size: 2rem; }
  .site-header { padding: var(--spacing-sm) var(--spacing-md); }
  .main-content { padding: var(--spacing-lg) var(--spacing-md); }
  .pov-controls { flex-direction: column; align-items: flex-start; }
  .page-hero-image { height: var(--hero-height-sm); }
  .home-hero__slideshow { height: var(--hero-height-sm); }
  .site-search { width: 100%; }
  .site-search__input { width: 100%; }
  .site-search__results { width: 100%; max-width: 100%; }
  .reading-mode__panel { right: auto; left: 0; }
}

/* The character header stacks earlier than the rest of the site. The portrait
   is a fixed 420px that cannot shrink, so between the 640px breakpoint and
   about 880px the text column beside it collapsed to ~100px: the role badge
   became a six-line pill, the name broke mid-word, and each tag took its own
   line. Stacking at 900px keeps the two-column header only where the text
   column still has a real measure. */
@media (max-width: 900px) {
  .character-header__inner { flex-direction: column; }
  .character-header__text { width: 100%; }
  .character-portrait { max-width: 100%; }
}

/* --- Focus Visible --- */
:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* --- Print --- */
@media print {
  .site-header, .site-footer, .pov-controls, .chapter-pagination { display: none; }
  body { background: white; color: black; }
  .pov-block[data-pov-hidden="true"] { display: block !important; }
}

/* --- Optional entry images (lore/codex/glossary entries, scenes) --- */
.entry-image {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  margin-top: var(--spacing-lg);
}

/* Inline images embedded directly in prose body content (markdown ![]())
   have no other size constraint - without this, a full-resolution source
   image (e.g. 1600px wide) overflows the ~72ch prose column instead of
   scaling down to fit it. */
.character-body img,
.codex-entry__body img,
.glossary-entry__body img,
.lore-entry__body img,
.chapter-body img {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  margin: var(--spacing-lg) 0;
}

/* --- Character Profiles --- */
.character-header {
  margin-bottom: var(--spacing-xl);
  padding-bottom: var(--spacing-lg);
  border-bottom: 1px solid var(--color-border);
}
.character-header__inner {
  display: flex;
  gap: var(--spacing-xl);
  align-items: flex-start;
  justify-content: space-between;
}
.character-header__text {
  flex: 1;
  min-width: 0;
}
.character-meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
  margin-bottom: var(--spacing-md);
}
.character-badge {
  font-family: var(--font-ui);
  font-size: 0.78rem;
  background: var(--color-surface-2);
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
  padding: 2px 10px;
  border-radius: 999px;
}
.character-badge--role {
  color: var(--color-accent);
  border-color: var(--color-accent);
}
.character-badge--status-active {
  color: #9ef5a0;
  border-color: #9ef5a0;
}
.character-badge--status-unknown {
  color: var(--color-text-muted);
}
/* Historical and Retired are the two other statuses the corpus uses more than
   once (six pages and one, at 2026-09-06). Both take tokens the contrast
   checker already measures against --color-surface-2 in every palette, so
   they need no per-theme literal of their own: Historical borrows the canon
   colour, since a historical figure is the record's own past, and Retired
   keeps the muted text but draws its outline in that colour, dashed, so it
   reads as out of service rather than unclassified. */
.character-badge--status-historical {
  color: var(--color-canon);
  border-color: var(--color-canon);
}
.character-badge--status-retired {
  color: var(--color-text-muted);
  border-color: var(--color-text-muted);
  border-style: dashed;
}
/* At large is the record's colour with the outline left open: known to the
   record, not where anyone last put it. Multi-word statuses reach here since
   character.njk slugifies the status into the class name (2026-09-06); before
   that "At large" emitted a class with a space in it and matched nothing. */
.character-badge--status-at-large {
  color: var(--color-canon);
  border-color: var(--color-canon);
  border-style: dashed;
}
/* Contained is At large's inversion: the same colour, filled instead of left
   open. Page-background text on the canon colour clears the 4.5:1 floor on
   every non-exempt palette (5.20:1 at the tightest, theme-light) and
   check-contrast.js measures the pair. The class comes from the status's head
   clause - "Contained — Survey Corps custody, ..." keys as `contained` - see
   lib/status-key.js. */
.character-badge--status-contained {
  background: var(--color-canon);
  color: var(--color-bg);
  border-color: var(--color-canon);
}
/* Every character portrait in the corpus is generated to 1200x675 to match the
   ratio below — see story-bible/images.md. Changing this aspect-ratio silently
   re-crops all of them, and a taller source delivered into a wider slot loses
   its edges rather than scaling. If this ever changes, the images change with
   it, not the other way round. */
.character-portrait {
  display: block;
  width: 100%;
  max-width: 420px;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: center 30%;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  flex-shrink: 0;
  margin-bottom: var(--spacing-md);
}
.character-name {
  font-size: 2rem;
  color: var(--color-text);
  margin-bottom: var(--spacing-sm);
}
.character-aliases {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--color-text-muted);
  margin-bottom: var(--spacing-md);
}
.character-body {
  max-width: var(--max-prose);
}
.character-body p { margin-bottom: var(--spacing-md); }
.character-body h2 {
  font-size: 1.3rem;
  color: var(--color-accent);
  margin: var(--spacing-xl) 0 var(--spacing-md);
}
.character-body h3 {
  font-size: 1.05rem;
  color: var(--color-text);
  margin: var(--spacing-lg) 0 var(--spacing-sm);
}
.character-body ul, .character-body ol {
  padding-left: var(--spacing-lg);
  margin-bottom: var(--spacing-md);
}
.character-body li { margin-bottom: var(--spacing-xs); }
.character-body strong { color: var(--color-text); }
.character-body em { color: var(--color-text-muted); }
.character-body a {
  color: var(--color-accent);
  text-decoration: underline;
}
.character-body a:hover,
.character-body a:focus {
  color: var(--color-accent-hover);
}
.character-footer {
  margin-top: var(--spacing-xl);
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--color-border);
  font-family: var(--font-ui);
  font-size: 0.9rem;
}
.character-footer a {
  color: var(--color-accent);
  text-decoration: none;
}
.character-footer a:hover { text-decoration: underline; }

.character-gallery {
  margin-top: var(--spacing-xl);
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--color-border);
  max-width: var(--max-prose);
}
.character-gallery__title {
  font-size: 1.3rem;
  color: var(--color-accent);
  margin-bottom: var(--spacing-md);
}
.character-gallery__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--spacing-md);
}
.character-gallery__figure {
  margin: 0;
}
.character-gallery__figure .character-gallery__item {
  /* A captioned item is a portrait, not a field snapshot: keep the 16:9
     frame the portraits are filed at rather than cropping it square. */
  aspect-ratio: 16 / 9;
}
.character-gallery__caption {
  margin-top: var(--spacing-xs);
  font-size: 0.85rem;
  color: var(--color-text-muted);
  text-align: center;
}
.character-gallery__item {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
}
.character-known-codex {
  margin-top: var(--spacing-xl);
  padding: var(--spacing-md);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  max-width: var(--max-prose);
}
.character-known-codex__title {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--color-text-muted);
  margin-bottom: var(--spacing-sm);
}
.character-known-codex__list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
}
.character-known-codex__list a {
  color: var(--color-accent);
  text-decoration: none;
  font-family: var(--font-ui);
  font-size: 0.85rem;
}
.character-known-codex__list a:hover { text-decoration: underline; }

/* --- Glossary --- */
.glossary-alpha {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
  margin-bottom: var(--spacing-xl);
  font-family: var(--font-ui);
  font-size: 0.9rem;
}
.glossary-alpha a {
  color: var(--color-accent);
  text-decoration: none;
  padding: 2px 8px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
.glossary-alpha a:hover { background: var(--color-surface-2); }

.glossary-list {
  max-width: var(--max-prose);
}
.glossary-letter-anchor {
  margin-top: var(--spacing-xl);
}
.glossary-letter {
  font-family: var(--font-ui);
  font-size: 1.3rem;
  color: var(--color-accent);
  padding-bottom: var(--spacing-sm);
  border-bottom: 1px solid var(--color-border);
  margin-bottom: var(--spacing-md);
}
.glossary-list__item {
  margin-bottom: var(--spacing-lg);
}
.glossary-list__term {
  font-family: var(--font-ui);
  font-size: 1rem;
  font-weight: 700;
  margin-bottom: var(--spacing-xs);
}
.glossary-list__term a {
  color: var(--color-text);
  text-decoration: none;
}
.glossary-list__term a:hover { color: var(--color-accent); text-decoration: underline; }
.glossary-list__def {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--color-text-muted);
  line-height: 1.5;
}

.glossary-entry__header {
  margin-bottom: var(--spacing-xl);
  padding-bottom: var(--spacing-lg);
  border-bottom: 1px solid var(--color-border);
}
.glossary-entry__term {
  font-size: 2rem;
  color: var(--color-text);
  margin: var(--spacing-sm) 0;
}
.glossary-entry__category { margin-bottom: var(--spacing-sm); }
.glossary-entry__irish {
  color: var(--color-text-muted);
  margin: 0 0 var(--spacing-sm);
}
.glossary-entry__irish [lang="ga"] {
  color: var(--color-text);
  font-style: italic;
}
.glossary-entry__body {
  max-width: var(--max-prose);
}
.glossary-entry__body p { margin-bottom: var(--spacing-md); }
.glossary-entry__body strong { color: var(--color-text); }
.glossary-entry__body em { color: var(--color-text-muted); }
.glossary-entry__body a {
  color: var(--color-accent);
  text-decoration: underline;
}
.glossary-entry__body a:hover,
.glossary-entry__body a:focus {
  color: var(--color-accent-hover);
}
.glossary-entry__body ul { padding-left: var(--spacing-lg); margin-bottom: var(--spacing-md); }
.glossary-entry__body li { margin-bottom: var(--spacing-xs); }
.glossary-entry__footer {
  margin-top: var(--spacing-xl);
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--color-border);
  font-family: var(--font-ui);
  font-size: 0.9rem;
}
.glossary-entry__footer a { color: var(--color-accent); text-decoration: none; }
.glossary-entry__footer a:hover { text-decoration: underline; }
.glossary-related {
  margin-top: var(--spacing-xl);
  padding: var(--spacing-md);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  max-width: var(--max-prose);
}
.glossary-related__heading {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--color-text-muted);
  margin-bottom: var(--spacing-sm);
}
.glossary-related__list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
}
.glossary-related__list a {
  color: var(--color-accent);
  text-decoration: none;
  font-family: var(--font-ui);
  font-size: 0.85rem;
}
.glossary-related__list a:hover { text-decoration: underline; }

/* Plain-language "In short" summary block, shared by lore and glossary
   entry pages - rendered only when the entry sets `plain` front matter. */
.plain-summary {
  margin-bottom: var(--spacing-lg);
  padding: var(--spacing-md);
  background: var(--color-surface-2);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-lg);
  max-width: var(--max-prose);
}
.plain-summary__heading {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--color-text-muted);
  margin-bottom: var(--spacing-sm);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.plain-summary__text {
  margin: 0;
  font-size: 1.05rem;
  line-height: 1.6;
}

/* --- Lore --- */
.lore-category-section {
  margin-bottom: var(--spacing-xl);
}
.lore-entry__header {
  margin-bottom: var(--spacing-xl);
  padding-bottom: var(--spacing-lg);
  border-bottom: 1px solid var(--color-border);
}
.lore-entry__title {
  font-size: 2rem;
  color: var(--color-text);
  margin: var(--spacing-sm) 0;
}
.lore-entry__body {
  max-width: var(--max-prose);
}
.lore-entry__body p { margin-bottom: var(--spacing-md); }
.lore-entry__body h2 {
  font-size: 1.3rem;
  color: var(--color-accent);
  margin: var(--spacing-xl) 0 var(--spacing-md);
}
.lore-entry__body h3 {
  font-size: 1.05rem;
  color: var(--color-text);
  margin: var(--spacing-lg) 0 var(--spacing-sm);
}
.lore-entry__body ul, .lore-entry__body ol {
  padding-left: var(--spacing-lg);
  margin-bottom: var(--spacing-md);
}
.lore-entry__body li { margin-bottom: var(--spacing-xs); }
.lore-entry__body strong { color: var(--color-text); }

/* --- Comments (giscus) --- */
.site-comments {
  margin-top: var(--spacing-xl);
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--color-border);
}
.site-comments__heading {
  font-size: 1.1rem;
  color: var(--color-accent);
  margin-bottom: var(--spacing-md);
}
/* The discussion is collapsed by default (base.njk): a reader opens it, and
   until then no comment is on the page. The summary is styled as the one
   control it is - a real button-sized target, in the accent, with the
   disclosure marker kept - and the note beneath it says what moderation
   means here. Palette-only tokens, so generate-themes carries it verbatim. */
.site-comments__toggle {
  border: 1px solid var(--color-border);
  border-radius: 6px;
  padding: var(--spacing-sm) var(--spacing-md);
}
.site-comments__summary {
  cursor: pointer;
  color: var(--color-accent);
  font-weight: 600;
  padding: var(--spacing-xs) 0;
  min-height: 2.75rem;
  display: flex;
  align-items: center;
}
.site-comments__summary:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.site-comments__toggle[open] .site-comments__summary {
  margin-bottom: var(--spacing-sm);
}
.site-comments__note {
  color: var(--color-text-muted);
  font-size: 0.95rem;
  margin: 0 0 var(--spacing-md);
}
.lore-entry__body em { color: var(--color-text-muted); }
.lore-entry__body a {
  color: var(--color-accent);
  text-decoration: underline;
}
.lore-entry__body a:hover,
.lore-entry__body a:focus {
  color: var(--color-accent-hover);
}
.lore-entry__body blockquote {
  border-left: 3px solid var(--color-accent);
  padding-left: var(--spacing-md);
  margin: var(--spacing-lg) 0;
  font-style: italic;
  color: var(--color-text-muted);
}
.lore-entry__footer {
  margin-top: var(--spacing-xl);
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--color-border);
  font-family: var(--font-ui);
  font-size: 0.9rem;
}
.lore-entry__footer a { color: var(--color-accent); text-decoration: none; }
.lore-entry__footer a:hover { text-decoration: underline; }
