/* ==========================================================================
   lore.css — ShadowDark GM · Lore (in-app rules browser)

   Two-pane reading view: TOC tree (left rail / drawer) + reading pane (right).
   Reuses the OSR theme tokens from base.css (no new colours, no new fonts).
   Embedded shared tables (.rt-table) and monster stat blocks (.enc-sheet) keep
   their own styling from tables.css / encounter.css — this file only scopes the
   reading column around them.
   ========================================================================== */

.lore {
  max-width: 78rem;
  margin: 0 auto 0 0;   /* #5 — left-anchored reading column */
  /* WHY no top padding: #app-content already supplies the standard top offset
     every page shares. A top padding here stacked on top of it, dropping the
     Lore title ~25px lower than other pages' headers (the stray header gap). */
  padding: 0 var(--space-md) var(--space-2xl);
}

/* --- Page head ---------------------------------------------------------- */
/* Title + tagline share ONE row (left-aligned), reclaiming the vertical band
   the stacked layout used to spend. Baseline-aligned so the italic tagline
   sits on the title's baseline; wraps under the title only when the row is too
   narrow to hold both. */
.lore__page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: var(--space-sm);
  row-gap: 0;
  margin-bottom: var(--space-md);
}

.lore__title {
  font-family: var(--font-heading);
  margin: 0;
}

.lore__subtitle {
  font-family: var(--font-body);
  font-style: italic;
  color: var(--color-text-muted);
  margin: 0;
}

/* --- Search bar --------------------------------------------------------- */
.lore__searchbar {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
  padding-bottom: var(--space-md);
  border-bottom: 1px solid var(--color-border);
}

.lore__search-input {
  flex: 1 1 18rem;
  min-width: 0;
  font-family: var(--font-body);
  font-size: 1rem;
  padding: 0.5rem 0.75rem;
  background: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 3px;
}

.lore__search-input:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.lore__search-scope {
  flex: 0 0 auto;
  font-family: var(--font-heading-ui);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* The Contents toggle is a drawer affordance — hidden on desktop. */
.lore__toc-toggle {
  display: none;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-heading-ui);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.45rem 0.7rem;
  background: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 3px;
  cursor: pointer;
}

.lore__toc-toggle svg {
  width: 14px;
  height: 14px;
}

.lore__toc-toggle:hover,
.lore__toc-toggle:focus-visible {
  border-color: var(--color-accent);
  color: var(--color-accent);
}

/* --- Layout: TOC rail + reading pane ------------------------------------ */
.lore__layout {
  display: grid;
  grid-template-columns: minmax(15rem, 18rem) minmax(0, 1fr);
  gap: var(--space-lg);
  align-items: start;
}

.lore__toc {
  position: sticky;
  top: var(--space-md);
  max-height: calc(100vh - 6rem);
  overflow-y: auto;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.18);
  padding: var(--space-sm);
}

.lore__reading {
  min-width: 0;
  /* Constrain the prose to a comfortable reading measure (~66ch) while letting
     embedded tables / stat blocks use the full pane width. */
}

.lore__loading {
  font-style: italic;
  padding: var(--space-sm);
}

/* --- TOC tree ----------------------------------------------------------- */
.lore-toc__source + .lore-toc__source {
  margin-top: var(--space-md);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--color-border);
}

.lore-toc__source-title {
  font-family: var(--font-heading-ui);
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin: 0 0 var(--space-xs);
  padding: 0 0.25rem;
}

.lore-toc__chapters,
.lore-toc__sections {
  list-style: none;
  margin: 0;
  padding: 0;
}

.lore-toc__chapter-btn {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  text-align: left;
  font-family: var(--font-heading-ui);
  font-size: 0.9rem;
  color: var(--color-text);
  background: none;
  border: none;
  padding: 0.35rem 0.25rem;
  cursor: pointer;
  border-radius: 3px;
}

.lore-toc__chapter-btn:hover,
.lore-toc__chapter-btn:focus-visible {
  color: var(--color-accent);
  background: rgba(100, 149, 237, 0.08);
  outline: none;
}

.lore-toc__chapter-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.lore-toc__caret {
  display: inline-block;
  font-size: 0.7rem;
  transition: transform 120ms ease;
  color: var(--color-text-muted);
}

.lore-toc__chapter-btn.is-open .lore-toc__caret {
  transform: rotate(90deg);
}

.lore-toc__chapter-title {
  flex: 1 1 auto;
}

.lore-toc__sections {
  margin: 0 0 0.35rem;
  padding-left: 0.85rem;
  border-left: 1px solid var(--color-border);
  margin-left: 0.5rem;
}

.lore-toc__section-btn {
  display: block;
  width: 100%;
  text-align: left;
  font-family: var(--font-body);
  font-size: 0.92rem;
  line-height: 1.35;
  color: var(--color-text-muted);
  background: none;
  border: none;
  padding: 0.3rem 0.4rem;
  cursor: pointer;
  border-radius: 3px;
}

.lore-toc__section-btn:hover,
.lore-toc__section-btn:focus-visible {
  color: var(--color-text);
  background: rgba(100, 149, 237, 0.08);
  outline: none;
}

.lore-toc__section-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.lore-toc__section.is-active .lore-toc__section-btn {
  color: var(--color-accent);
  background: rgba(100, 149, 237, 0.14);
  font-weight: 600;
}

/* #2a — Single-child chapter rendered as a clickable LEAF (no caret, no child
   list). It is a chapter-level entry that opens its lone section directly. The
   title aligns with multi-section chapter titles: the caret slot (0.7rem glyph +
   0.4rem gap) is reserved as left padding so leaf titles line up under expandable
   chapter titles rather than jumping left. Active state mirrors a section's. */
.lore-toc__chapter-btn--leaf {
  padding-left: calc(0.25rem + 0.7rem + 0.4rem);
}

.lore-toc__chapter--leaf.is-active .lore-toc__chapter-btn--leaf {
  color: var(--color-accent);
  background: rgba(100, 149, 237, 0.14);
}

/* ==========================================================================
   Reading pane content — the "rulebook page" (KDL 2026-06-27: lore_rulebook_styling.md).

   The article reads as a leaf resting on the dungeon table: a Stone Wall page
   surface with the card lamplight gradient, a 1px Mortar Line inset on all four
   sides (no radius — system rule), generous asymmetric padding, and a single
   hairline column rule down the left inner margin as the book's gutter cue.
   The prose measure caps at ~62ch; embedded tables / stat blocks opt out to the
   full page width. All values are existing base.css tokens.
   ========================================================================== */

/* --- The leaf (page surface) -------------------------------------------- */
.lore-read--leaf {
  /* Two-measure system (KDL 2026-06-27 §7.2): prose holds the comfortable reading
     measure; tables/figure embeds opt out to the wider one. Both are caps, so
     they self-disable on narrow viewports — no media query needed. */
  --measure: 62ch;
  --measure-wide: 86ch;
  background: radial-gradient(ellipse at 30% 20%, var(--color-surface-hover) 0%, var(--color-surface) 70%);
  border: 1px solid var(--color-border);
  padding: var(--space-xl) var(--space-lg) var(--space-xl) var(--space-xl);
  /* The gutter cue: a hairline rule set in from the left edge, evoking the
     book's column gutter without splitting the single-column text. Toggle off
     by removing this border-left declaration (KDL 2026-06-27 marked it opt-in). */
  border-left: 1px solid var(--color-border);
  box-shadow: inset var(--space-md) 0 0 calc(-1 * var(--space-md) + 1px) var(--color-border);
}

/* --- Chapter eyebrow (running header / "you are here") ------------------ */
.lore-read__crumb {
  font-family: var(--font-heading-ui);
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin: 0 0 var(--space-xs);
}

/* --- Section banner (Blackletter) + folio ------------------------------- */
.lore-read__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  flex-wrap: wrap;
  margin-bottom: var(--space-lg);
  padding-bottom: var(--space-sm);
  /* Section-divider rule — the book's banner underline. */
  border-bottom: 2px solid var(--color-border);
}

.lore-read__title {
  font-family: var(--font-heading);
  font-size: var(--text-h2);
  line-height: var(--lh-heading);
  color: var(--color-heading);
  margin: 0;
  flex: 1 1 auto;
}

/* Page number as a printed folio — aligned to the outer (right) edge. */
.lore-read__page {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  flex: 0 0 auto;
}

/* --- Run-in subhead — the Cinzel-caps workhorse that opens a topic ------- */
.lore-read__subhead {
  font-family: var(--font-heading-ui);
  font-size: var(--text-h5);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  /* Accent rubric (KDL 2026-06-27 §7.1) — the book's run-in head colour. Caps + weight +
     tick + position all carry the distinction too, so this is safe for CVD. */
  color: var(--color-accent);
  line-height: var(--lh-heading);
  /* Opens a topic: more air above than below so it binds to its body. */
  margin: var(--space-lg) 0 var(--space-xs);
  max-width: var(--measure);
  /* Room for the left accent tick without indenting the text or fighting margin. */
  position: relative;
  padding-left: var(--space-md);
}

/* Left accent tick — a short hard-edged stripe in the gutter that anchors the
   head, echoing the callout/stat-block left-stripe family (KDL 2026-06-27 §7.1). Inset
   top/bottom to roughly cap height so it reads as a tick, not a tall bar. */
.lore-read__subhead::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.15em;
  bottom: 0.15em;
  width: 3px;
  background: var(--color-accent);
}

/* --- Body prose --------------------------------------------------------- */
.lore-read__p {
  font-family: var(--font-body);
  font-size: 1.05rem;
  line-height: var(--lh-body);
  max-width: var(--measure);
  margin: 0 0 var(--space-md);
}

/* Drop cap on the first prose paragraph of a section only (gated in JS). */
.lore-read__p--dropcap::first-letter {
  font-family: var(--font-heading);
  font-size: 3.2rem;
  line-height: 0.8;
  color: var(--color-accent);
  float: left;
  margin: 0.05em var(--space-sm) 0 0;
}

.lore-read__empty {
  font-style: italic;
}

/* --- Lists -------------------------------------------------------------- */
.lore-read__list {
  font-family: var(--font-body);
  font-size: 1.05rem;
  line-height: var(--lh-body);
  max-width: var(--measure);
  margin: 0 0 var(--space-md);
  padding-left: var(--space-lg);
  list-style: none;
}

.lore-read__li {
  position: relative;
  margin: 0 0 var(--space-xs);
  padding-left: var(--space-sm);
}

/* Hard-edged accent square marker (no rounded bullets — system rule). */
.lore-read__list:not(.lore-read__list--ordered) .lore-read__li::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--space-sm));
  top: 0.62em;
  width: 0.4em;
  height: 0.4em;
  background: var(--color-accent);
}

.lore-read__list--ordered {
  list-style: decimal;
  padding-left: var(--space-xl);
}

.lore-read__list--ordered .lore-read__li {
  padding-left: var(--space-xs);
}

.lore-read__list--ordered .lore-read__li::marker {
  font-family: var(--font-mono);
  color: var(--color-accent);
}

/* --- Boxed callout / sidebar (parchment-within-stone) ------------------- */
.lore-read__callout {
  max-width: var(--measure);
  margin: var(--space-lg) 0;
  padding: var(--space-md) var(--space-lg);
  background: var(--color-oracle-bg);
  border-left: 3px solid var(--color-accent);
}

.lore-read__callout-label {
  font-family: var(--font-heading-ui);
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0 0 var(--space-xs);
}

.lore-read__callout-body .lore-read__p:last-child {
  margin-bottom: 0;
}

/* --- Recovered/unstructured raw block ----------------------------------- */
/* Honest "we couldn't structure this" signal — dashed border stays, but the body
   is paragraph-split (in JS) and softened so it does not shout louder than prose. */
.lore-read__raw {
  max-width: var(--measure);
  margin: 0 0 var(--space-md);
  border: 1px dashed var(--color-border);
  padding: var(--space-sm) var(--space-md);
  background: rgba(0, 0, 0, 0.2);
}

.lore-read__raw-flag {
  font-family: var(--font-heading-ui);
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0 0 var(--space-xs);
}

.lore-read__raw-p {
  font-family: var(--font-mono);
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--color-text);
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0 0 var(--space-sm);
}

.lore-read__raw-p:last-child {
  margin-bottom: 0;
}

/* --- Embedded shared components (page-set reference objects) ------------- */
/* Tables and stat blocks bring their own styling; we frame them as printed
   reference objects set into the text (KDL 2026-06-27 §3). They use the full page width. */
/* Wide measure (KDL 2026-06-27 §7.2): tables and figure embeds get ~38% more room than
   prose, flush-left under the prose so the reading axis stays single. The cap is
   in `ch`, so on narrow viewports it exceeds the leaf and the inner constraints
   (max-width:100% on the wrap, the table's own width) take over — no overflow. */
.lore-read__embed,
.lore-read__embed--data,
.lore-read__embed--table {
  margin: var(--space-lg) 0;
  max-width: var(--measure-wide);
  margin-left: 0;
  margin-right: auto;
}

/* Table embed: manuscript double-rule above (1px + offset shadow), single rule
   below — the divider treatment from the design direction. */
.lore-read__embed--table {
  padding-top: var(--space-md);
  padding-bottom: var(--space-md);
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
  box-shadow: 0 2px 0 0 var(--color-border);
}

.lore-read__embed-caption {
  font-family: var(--font-heading-ui);
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0 0 var(--space-sm);
}

/* Stat-block embed: the accent left-stripe reference-object signal (matches the
   callout family), replacing the old full 1px box. scroll-margin keeps search
   jumps landing cleanly below any sticky chrome. */
.lore-read__embed--monster .enc-sheet {
  border: none;
  border-left: 3px solid var(--color-accent);
  padding: var(--space-md) var(--space-md) var(--space-md) var(--space-lg);
  background: rgba(0, 0, 0, 0.18);
}

.lore-read__embed--monster {
  scroll-margin-top: var(--space-lg);
}

/* Consecutive stat blocks (bestiary appendix) get the double-rule divider so a
   run of them does not merge into one slab. */
.lore-read__embed--monster + .lore-read__embed--monster {
  padding-top: var(--space-lg);
  border-top: 1px solid var(--color-border);
  box-shadow: 0 -2px 0 0 var(--color-border) inset;
}

.lore-read__embed--missing {
  margin: var(--space-md) 0;
  border: 1px dashed var(--color-border);
  padding: var(--space-sm) var(--space-md);
  background: rgba(0, 0, 0, 0.2);
}

.lore-read__missing-flag {
  font-family: var(--font-body);
  font-style: italic;
  color: var(--color-text-muted);
  margin: 0;
}

/* --- Interactive roll-table embed (#6) ---------------------------------- */
/* A tableRef is a REAL roll table, embedded INTERACTIVE (Roll dN button + live
   readout + bouncing-row landing) via the shared instance-safe renderer. It
   keeps the tableRef double-rule framing (.lore-read__embed--table above); we
   only scope the roll controls so the button + readout sit cleanly inside the
   reading column. The .rt-roll / .rt-table / .rt-display__* styling comes from
   tables.css (one source of truth — no duplication here). */
.lore-read__embed--roll .rt-roll {
  /* The instance wrapper is a plain flow container inside the framed figure. */
  margin: 0;
}

.lore-read__embed--roll .rt-display__header {
  /* The embed caption already names the table; keep the inner header compact. */
  margin-top: 0;
}

/* The roll button + readout row, contained to the embed (already flex in
   tables.css); nudge spacing so it reads as part of the framed object. */
.lore-read__embed--roll .rt-display__actions {
  margin-top: var(--space-sm);
}

/* ==========================================================================
   Example-of-Play dialogue (#17). Back-and-forth turns ("GM: …", "PC (Jorbin):
   …") rendered as distinct dialogue lines. The SPEAKER is a Cinzel-caps chip set
   off from the speech; each recurring speaker carries a CONSISTENT accent keyed
   off data-speaker so you can tell who is talking at a glance. GM is pinned to
   slot 0 (the accent), players cycle through a small distinct-but-on-theme set.
   Scoped to Example-of-Play passages only (gated in lore.js) — ordinary prose is
   never restyled. Built on existing tokens; the per-speaker hues are alpha tints
   layered on the accent/text tokens so they stay within the OSR system and shift
   correctly if the palette token swaps.
   ========================================================================== */
.lore-read__playscript {
  max-width: var(--measure);
  margin: var(--space-lg) 0;
  /* A faint left gutter rule binds the exchange together as one transcript. */
  border-left: 1px solid var(--color-border);
  padding-left: var(--space-md);
}

.lore-read__dialogue {
  font-family: var(--font-body);
  font-size: 1.05rem;
  line-height: var(--lh-body);
  margin: 0 0 var(--space-sm);
  /* Hanging indent: the speaker chip sits at the left margin, the speech indents
     under it so multi-line turns align cleanly under the speech, not the chip. */
  padding-left: var(--space-lg);
  text-indent: calc(-1 * var(--space-lg));
}

.lore-read__dialogue:last-child {
  margin-bottom: 0;
}

/* Speaker chip — Cinzel caps, an accent the speaker keeps across the scene. The
   chip carries a left tick (box-shadow inset) echoing the callout/subhead family.
   The default (and GM, slot 0) is the system accent. */
.lore-read__speaker {
  font-family: var(--font-heading-ui);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-right: var(--space-sm);
  white-space: nowrap;
}

/* The speech runs in body prose; the chip + colon read as the label. */
.lore-read__speaker::after {
  content: ":";
  color: var(--color-text-muted);
}

.lore-read__speech {
  color: var(--color-text);
}

/* Per-speaker identity. Slot 0 = GM = accent (above). Slots 1..N are the players,
   each a distinct, on-theme hue. These are the only literal colours in this file;
   they are deliberately muted parchment/ink tones that sit beside the cornflower/
   dried-blood accent without clashing, and each is paired with the chip so colour
   is never the SOLE cue (the name text differs too — CVD-safe). */
.lore-read__dialogue[data-speaker="1"] .lore-read__speaker { color: #c9a23f; } /* warm ochre */
.lore-read__dialogue[data-speaker="2"] .lore-read__speaker { color: #6fae8f; } /* sage green */
.lore-read__dialogue[data-speaker="3"] .lore-read__speaker { color: #b07cc6; } /* muted violet */
.lore-read__dialogue[data-speaker="4"] .lore-read__speaker { color: #d18a6a; } /* terracotta */
.lore-read__dialogue[data-speaker="5"] .lore-read__speaker { color: #7fa6cf; } /* slate blue */

/* ==========================================================================
   Static reference tables (.sd-dtable) — the shared renderDataTable output.
   PDF-style ruled reference tables (weapons, armor, gear, ability mods, class/
   ancestry/deity summaries, spell lists). Mirrors the Fortunes .rt-table family
   (KDL 2026-06-27 §3): Cinzel-caps muted headers, ruled rows, mono numerics, accent group
   sub-heads. Reuses existing tokens only — no new colours/fonts/radius.

   QB-CP-01: a wide table NEVER overflows the leaf. The .sd-dtable__wrap is the
   horizontal-scroll region — the wrap scrolls, the page/leaf does not break.
   The data-table figure carries the same double-rule framing as tableRef.
   ========================================================================== */
.lore-read__embed--data {
  /* Inherits .lore-read__embed--table double-rule framing; nothing extra needed,
     but keep a hook for future data-table-specific tuning. */
}

/* Horizontal-scroll wrapper — now a LAST-RESORT scroller (KDL 2026-06-27 §7.2). It fills
   the wider embed (var(--measure-wide)); most tables fit there without scrolling.
   overflow-x stays as the genuine safety net for a d100 / very wide matrix. */
.sd-dtable__wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--color-border);
  max-width: 100%;
}

.sd-dtable__wrap:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.sd-dtable {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-body);
}

.sd-dtable thead th {
  font-family: var(--font-heading-ui);
  font-size: var(--text-sm);
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-align: left;
  padding: var(--space-sm) var(--space-md);
  background-color: var(--color-surface);
  border-bottom: 2px solid var(--color-border);
  /* Sticky header within the wrap's scroll so labels stay readable on long lists. */
  position: sticky;
  top: 0;
  z-index: 1;
  white-space: nowrap;
}

.sd-dtable__td {
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--color-border);
  vertical-align: top;
  line-height: var(--lh-body);
  font-size: 0.95rem;
  color: var(--color-text);
}

.sd-dtable__row:last-child .sd-dtable__td {
  border-bottom: none;
}

/* Zebra striping for readability on long lists (matches .rt-table). */
.sd-dtable__row:nth-of-type(even) .sd-dtable__td {
  background-color: rgba(255, 255, 255, 0.02);
}

/* CANONICAL DATA-TABLE ROW-HOVER (2026-06-24, KDL 2026-06-27 — consistency consolidation).
   ONE row-hover signal across the whole .sd-dtable family (lore data tables, roll
   tables (.rt-table), the admin/bug board (.admin-table), the feedback board) so
   they read identically to the locations stock/menu rows: a faint surface tint +
   a 2px accent BOTTOM border. This is the established locations idiom
   (.loc-menu__item / .shop-stock__table, locations.css) promoted to the shared
   base — it was previously re-invented per table (admin used a left-rail inset,
   the char roster used surface-tint-only, the roll tables had none). Scoped to
   `tbody tr` so it applies regardless of each table's per-row class
   (.sd-dtable__row / .rt-table__row / .admin-row / .feedback-row). The accent
   border rides on the cells, not the <tr>, because border-collapse won't animate
   a per-row border; this keeps any per-line action buttons fully clickable. */
.sd-dtable tbody tr {
  transition: background-color var(--transition-fast);
}

.sd-dtable tbody tr:hover,
.sd-dtable tbody tr:focus-within {
  background-color: var(--color-surface-hover);
}

.sd-dtable tbody tr:hover > td,
.sd-dtable tbody tr:hover > th,
.sd-dtable tbody tr:focus-within > td,
.sd-dtable tbody tr:focus-within > th {
  border-bottom: 2px solid var(--color-accent);
}

.sd-dtable__td--mono {
  font-family: var(--font-mono);
  color: var(--color-accent);
}

.sd-dtable__td--num {
  font-variant-numeric: tabular-nums;
}

.sd-dtable__td--nowrap {
  white-space: nowrap;
}

/* Opt-in leading icon column (equipment views only). Narrow, blank header, the
   glyph vertically centred so it does not grow the row. The sliced PNGs are
   light-on-transparent and read on the dark rows as-is. */
.sd-dtable__th--icon {
  width: 2.75rem;
  padding-right: 0;
}

.sd-dtable__td--icon {
  width: 2.75rem;
  padding-right: 0;
  vertical-align: middle;
  text-align: center;
}

.dtable__icon {
  display: inline-block;
  width: 28px;
  height: 28px;
  object-fit: contain;
  vertical-align: middle;
}

/* Inline-SVG fallback (armor): currentColor stroke, no fill, matching KDL 2026-06-27's
   icon style; muted so it reads as a placeholder, not a sliced glyph. */
.dtable__icon--svg {
  fill: none;
  stroke: var(--color-text-muted);
  stroke-width: 1.5;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

/* Wide prose cells (descriptions, properties, traits) wrap and take the slack
   so the table breathes; narrow numeric columns stay tight. A sensible min keeps
   long descriptions from collapsing to a sliver before horizontal scroll kicks in. */
.sd-dtable__td--wide {
  min-width: 16rem;
  white-space: normal;
}

/* The widest core table (Weapons, 7 cols) lands ~32px past the 86ch embed cap
   solely because its "Properties" cell carries this 16rem (256px) wide-cell min.
   In the Lore wide scope (layout.css raises #app-content to 1360px for the Lore
   section), trim that one min by 2rem so Properties packs to ~14rem and the table
   sits inside the 86ch embed with no .sd-dtable__wrap scroll. This does NOT raise
   KDL 2026-06-27's --measure-wide token or touch the shared data_table renderer; the other
   --wide tables (Titles, Spells, Armor, Gear) already fit and are unaffected
   visually since their wide cell never reaches the min. Genuinely huge tables
   (d100) still hit the overflow-x scroller — the last-resort net is intact. */
body[data-section="lore"] .sd-dtable__td--wide {
  min-width: 14rem;
}

/* Grouped sub-header row (spells by tier) — accent-tinted band spanning all
   columns, Cinzel caps, set apart from the data rows above and below. */
.sd-dtable__group th {
  font-family: var(--font-heading-ui);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: left;
  color: var(--color-heading);
  padding: var(--space-sm) var(--space-md);
  background-color: var(--color-oracle-bg);
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
  box-shadow: inset 3px 0 0 0 var(--color-accent);
}

/* First group header sits flush under the column head — drop the doubled top rule. */
.sd-dtable tbody tr:first-child.sd-dtable__group th {
  border-top: none;
}

/* ==========================================================================
   Section-opener plate (Feature B, anchored only). A page-matched rulebook
   plate rendered full reading-column width below the section head. Atmospheric,
   never load-bearing; the rules text stands alone if the image fails to load.
   Only the 4 page-anchored plates are ever placed (QB-CP-02).
   ========================================================================== */
.lore-read__plate {
  margin: 0 0 var(--space-lg);
  /* Cap to the prose measure so a tall plate does not dominate the leaf; it
     stays within the reading column rather than overflowing it (QB-CP-01). */
  max-width: var(--measure);
}

.lore-read__plate-img {
  display: block;
  width: 100%;
  height: auto;       /* preserve intrinsic ratio from the width/height hint */
  max-height: 70vh;   /* never let a tall portrait-orientation plate run the full pane */
  object-fit: contain;
  border: 1px solid var(--color-border);
  background: rgba(0, 0, 0, 0.2);
}

/* --- Image attribution caption (#15) ------------------------------------ */
/* Every Lore image (plate + figure) carries the required Arcane Library notice.
   Small, muted, Cinzel UI caps so it reads as a printed credit line, not body
   prose. Inside the <figure> so it is programmatically tied to the image. Sits
   tight under the image. */
.lore-read__credit {
  font-family: var(--font-heading-ui);
  font-size: 0.65rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: var(--space-xs) 0 0;
  line-height: 1.3;
}

/* ==========================================================================
   Standalone COVER plate (#1). A Core section whose only block is a cover image.
   It opens the library like the book's front cover: a large, centered image that
   fills the reading column and caps to ~viewport height. No eyebrow/folio/banner/
   prose chrome competes (the cover section renders chrome-free in lore.js). Only a
   small, clean credit line beneath. It is its OWN block type — never floated, never
   subject to the figure-float or image source-gate logic.
   ========================================================================== */
.lore-read--cover {
  /* Center the lone cover within the leaf; no body-prose measure constraint. */
  display: flex;
  flex-direction: column;
  align-items: center;
}

.lore-read__cover {
  margin: 0 auto;
  /* Roomier than the prose measure so the cover reads as a full plate, but still
     bounded so it never overruns the leaf. */
  max-width: min(100%, 34rem);
  text-align: center;
}

.lore-read__cover-img {
  display: block;
  width: 100%;
  height: auto;
  /* Cap to the viewport so the whole cover reads at a glance on open. */
  max-height: 82vh;
  object-fit: contain;
  margin: 0 auto;
  border: 1px solid var(--color-border);
  background: rgba(0, 0, 0, 0.2);
}

/* The cover's credit sits centered and quiet beneath the plate. */
.lore-read__cover .lore-read__credit {
  text-align: center;
}

/* ==========================================================================
   Combined-roll widget (#2b — rollGroup). A single button that rolls several
   component tables at once and reveals the assembled phrase ("Citadel of the
   Haunted Idol"). The individual component tables render as their own tableRef
   embeds; this control sits above/among them as the one-click "give me the whole
   name/prompt" affordance. Reuses the primary button + the muted readout family
   so it reads as part of the rulebook reference object. Clears any preceding float.
   ========================================================================== */
.lore-rollgroup {
  clear: both;
  margin: var(--space-lg) 0;
  padding: var(--space-md);
  border: 1px solid var(--color-border);
  background: rgba(0, 0, 0, 0.12);
  border-radius: 3px;
}

.lore-rollgroup__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-sm) var(--space-md);
}

/* The assembled-phrase readout — the payoff ("Citadel of the Haunted Idol"). Body
   font (legibility over decoration: a multi-word generated phrase must read
   cleanly), accent colour + a slightly larger size to set it apart from the
   component table results. aria-live announces it when it lands. */
.lore-rollgroup__readout {
  font-family: var(--font-body);
  font-size: 1.15rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--color-accent);
  min-height: 1.3em; /* reserve space so the layout doesn't jump on reveal */
}

.lore-rollgroup__note {
  margin: var(--space-sm) 0 0;
  font-size: var(--text-sm);
}

/* ==========================================================================
   Inline page figure (Item C — Ken #4 / #2). A page-matched `kind:'figure'`
   illustration. By default centered within the reading column; the `--float`
   variant (the standard now, #2) floats it LEFT so body prose wraps to its right
   in the classic rulebook layout. Only manifest images are rendered; an onerror
   handler hides a figure whose image fails so no broken-image icon ever shows
   (QB-CP-02).
   ========================================================================== */
.lore-read__figure {
  margin: var(--space-lg) auto;
  max-width: var(--measure);
  text-align: center;
}

.lore-read__figure-img {
  display: block;
  width: auto;
  max-width: 100%;
  height: auto;
  margin: 0 auto;
  max-height: 60vh;       /* tall column-figures never run the whole pane */
  object-fit: contain;
  border: 1px solid var(--color-border);
  background: rgba(0, 0, 0, 0.2);
}

/* --- Floated illustration (#2) ------------------------------------------ */
/* Float left; body text wraps to the right (classic rulebook wrap). Width is a
   sensible band of the reading column with a min-width floor so it never shrinks
   to a stamp, and a max so a wide figure cannot swallow the measure. The figure
   sits at the START of the body flow (placed there in lore.js) so paragraphs run
   beside it. The float is cleared at subhead/section boundaries (below) and the
   body clearfixes, so a figure never bleeds into the next topic and a short
   section can never leave it hanging (QB-CP-01). */
.lore-read__figure--float {
  float: left;
  /* ~38-45% of the reading column; clamp keeps a floor and a ceiling. */
  width: clamp(11rem, 40%, 18rem);
  max-width: 45%;
  margin: 0 var(--space-md) var(--space-sm) 0;
  text-align: left;
}

.lore-read__figure--float .lore-read__figure-img {
  width: 100%;
  margin: 0;
  max-height: none;       /* width-bounded now, so no vh clamp needed */
}

/* Float containment + clearing. The body establishes a block formatting context
   so the floated figure is fully contained (a short section cannot leave a tall
   figure spilling past the pager). Subheads, lists, callouts, embeds, plates and
   the dialogue playscript clear the float so a figure stays with its own topic
   and never bleeds under the next subhead. Plain paragraphs deliberately do NOT
   clear — they are what wrap beside the figure. */
.lore-read__body::after {
  content: "";
  display: block;
  clear: both;
}

.lore-read__subhead,
.lore-read__list,
.lore-read__callout,
.lore-read__embed,
.lore-read__embed--table,
.lore-read__embed--data,
.lore-read__embed--monster,
.lore-read__plate,
.lore-read__playscript,
.lore-read__raw {
  clear: both;
}

/* On a narrow leaf a floated figure beside a short measure crowds the text to a
   sliver. Below the TOC-drawer breakpoint, stop floating: the figure becomes a
   centered block illustration (its non-float default) so prose keeps full width. */
@media (max-width: 880px) {
  .lore-read__figure--float {
    float: none;
    width: auto;
    max-width: var(--measure);
    margin: var(--space-lg) auto;
    text-align: center;
  }
  .lore-read__figure--float .lore-read__figure-img {
    width: auto;
    margin: 0 auto;
    max-height: 60vh;
  }
}

/* --- Inline page reference (in-app jump from body prose) ----------------- */
/* A "pg. N" that resolves to a real section becomes a button styled as an
   inline manuscript cross-reference: accent text, a hairline dotted underline
   to signal "follow me" without shouting. Reads inline with the prose — no box,
   no baseline shift. Self/unresolved refs stay plain text (no class applied). */
.lore-pageref {
  font: inherit;
  color: var(--color-accent);
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 0.15em;
}

.lore-pageref:hover {
  text-decoration-style: solid;
}

.lore-pageref:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
  border-radius: 2px;
  text-decoration-style: solid;
}

/* --- Reading pager (Previous / Next) ------------------------------------ */
/* Closes the leaf: a divider rule, then a balanced two-up row. Previous sits
   left, Next right; an inert spacer holds the slot when a control is omitted at
   a boundary so the surviving control keeps its edge. */
.lore-pager {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-top: var(--space-xl);
  padding-top: var(--space-md);
  border-top: 2px solid var(--color-border);
}

.lore-pager__spacer {
  flex: 1 1 0;
}

.lore-pager__btn {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  flex: 1 1 0;
  min-width: 0;
  text-align: left;
  background: rgba(0, 0, 0, 0.18);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  padding: var(--space-sm) var(--space-md);
  cursor: pointer;
}

.lore-pager__btn--next {
  text-align: right;
  justify-content: flex-end;
}

.lore-pager__btn:hover,
.lore-pager__btn:focus-visible {
  border-color: var(--color-accent);
  outline: none;
}

.lore-pager__btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.lore-pager__arrow {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 1.1rem;
  color: var(--color-accent);
}

.lore-pager__text {
  display: flex;
  flex-direction: column;
  min-width: 0; /* allow the title to ellipsize, not overflow (QB-CP-01) */
}

.lore-pager__lead {
  font-family: var(--font-heading-ui);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.lore-pager__title {
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.3;
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- Search results ----------------------------------------------------- */
.lore-search__summary {
  font-family: var(--font-body);
  font-size: 1.05rem;
  margin: 0 0 var(--space-sm);
}

.lore-search__load-all {
  margin-bottom: var(--space-md);
}

.lore-search__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.lore-search__hit {
  display: block;
  width: 100%;
  text-align: left;
  background: rgba(0, 0, 0, 0.18);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  padding: var(--space-sm) var(--space-md);
  cursor: pointer;
}

.lore-search__hit:hover,
.lore-search__hit:focus-visible {
  border-color: var(--color-accent);
  outline: none;
}

.lore-search__hit:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.lore-search__hit-title {
  display: block;
  font-family: var(--font-heading-ui);
  font-size: 1rem;
  color: var(--color-text);
}

.lore-search__hit-crumb {
  display: block;
  font-family: var(--font-heading-ui);
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  margin: 0.1rem 0 0.35rem;
}

.lore-search__hit-snippet {
  display: block;
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--color-text-muted);
}

.lore-search__none {
  font-style: italic;
}

.lore-search__mark {
  background: rgba(100, 149, 237, 0.32);
  color: var(--color-text);
  border-radius: 2px;
  padding: 0 1px;
}

/* ==========================================================================
   Responsive — below 880px the TOC rail collapses into a drawer toggled by the
   Contents button (mirrors the Bestiary filter-drawer pattern). The reading pane
   becomes full width; the prose measure caps naturally.
   ========================================================================== */
@media (max-width: 880px) {
  .lore__layout {
    grid-template-columns: 1fr;
  }

  .lore__toc-toggle {
    display: inline-flex;
  }

  .lore__toc {
    position: static;
    max-height: none;
    display: none;
    margin-bottom: var(--space-md);
  }

  .lore--toc-open .lore__toc {
    display: block;
  }

  .lore__search-scope {
    display: none; /* the drawer toggle + summary already convey scope on narrow */
  }
}

@media (max-width: 880px) {
  /* Collapse the leaf's asymmetric padding so the page does not crowd the
     narrow viewport or the open TOC drawer (QB-CP-01). */
  .lore-read--leaf {
    padding: var(--space-lg) var(--space-md);
  }
}

@media (max-width: 480px) {
  .lore {
    padding: var(--space-md) var(--space-sm) var(--space-2xl);
  }

  .lore-read--leaf {
    padding: var(--space-md) var(--space-sm);
  }

  .lore-read__p,
  .lore-read__list {
    font-size: 1rem;
  }

  /* Smaller drop cap on phones so it does not crowd a short measure. */
  .lore-read__p--dropcap::first-letter {
    font-size: 2.6rem;
  }

  /* Stack the pager so two long section titles never collide on a phone
     (QB-CP-01). Next moves below Previous, both full-width and left-aligned. */
  .lore-pager {
    flex-direction: column;
  }

  .lore-pager__spacer {
    display: none;
  }

  .lore-pager__btn--next {
    text-align: left;
    justify-content: flex-start;
    flex-direction: row-reverse;
  }
}

/* Reduced motion — kill the caret transition and any smooth scroll. */
@media (prefers-reduced-motion: reduce) {
  .lore-toc__caret {
    transition: none;
  }
}
