/* ============================================================
 * lucid-lint — layout + spatial rhythm
 *
 * Content measure, skip-link, breadcrumbs, right-rail page TOC,
 * and sidebar rhythm adjustments. Loaded after colors and
 * typography; complements them rather than overriding.
 * ============================================================ */

/* ---------- Spacing scale ---------- */

:root {
  --lucid-space-2xs: 0.25rem;
  --lucid-space-xs:  0.5rem;
  --lucid-space-sm:  0.75rem;
  --lucid-space-md:  1rem;
  --lucid-space-lg:  1.5rem;
  --lucid-space-xl:  2rem;
  --lucid-space-2xl: 3rem;
  --lucid-space-3xl: 4.5rem;
}

/* ---------- Container ----------
 * mdBook's stock --content-max-width is 750px. We widen it to
 * 840px so tables, code blocks, and the future reading demo
 * have breathing room, while keeping the body prose capped at
 * 70ch in lucid-typography.css. The two constraints cooperate:
 * the narrower one wins per element.
 * -------------------------------- */

:root {
  --content-max-width: 840px;
}

/* ---------- Skip link ----------
 * Absolutely positioned off-screen; slides in on focus. Must be
 * the first interactive element in the tab order — server-rendered
 * as the first two elements inside <body> in `theme/index.hbs`
 * (F35a). Uses the palette tokens so it works in both Lucid light
 * and Lucid dark.
 *
 * Two variants are rendered (EN + FR) and gated by `html[lang]`,
 * which `theme/head.hbs` sets synchronously before first paint on
 * `/fr/` pages. The wrong-language skip link is removed from the
 * accessibility tree entirely, not merely hidden, so screen readers
 * announce only one bypass-blocks target.
 * -------------------------------- */

html:not([lang="fr"]) [data-lucid-lang="fr"],
html[lang="fr"]      [data-lucid-lang="en"] {
  display: none !important;
}

/* -------------------------------- *
 * Sidebar locale filter
 * -------------------------------- *
 * The mdBook sidebar is a single flat <ol class="chapter"> emitted
 * from SUMMARY.md, with EN entries then a "Version française"
 * part-title and its FR entries. We want each locale to see only
 * its own tree. F35a set the precedent for `html[lang]`-driven
 * visibility; this extends it to the sidebar via :has() matching
 * the href prefix of each entry's anchor.
 *
 * Pair-wise visibility matrix:
 *   EN viewer (html[lang!="fr"]): hide all FR entries (href starts
 *     with or contains "fr/") and the "Version française" part-title
 *     that introduces them.
 *   FR viewer (html[lang="fr"]):  hide all EN entries (chapter-items
 *     whose anchor has neither "fr/" nor an external scheme) and
 *     every part-title that is NOT the Version française one.
 *
 * This is 1.A of the FR parallel-version plan. Alternatives (split
 * SUMMARY per locale, or a multi-book mdBook setup) are filed on
 * the roadmap as F90 / F91. -------------------------------- */

/* EN viewer: hide FR chapter-items. */
html:not([lang="fr"]) .chapter li.chapter-item:has(> span > a[href^="fr/"]),
html:not([lang="fr"]) .chapter li.chapter-item:has(> span > a[href*="/fr/"]) {
  display: none;
}
/* EN viewer: hide the "Version française" part-title — it is the
 * part-title whose next chapter-item sibling targets an FR page. */
html:not([lang="fr"]) .chapter li.part-title:has(+ li.chapter-item span > a[href^="fr/"]),
html:not([lang="fr"]) .chapter li.part-title:has(+ li.chapter-item span > a[href*="/fr/"]) {
  display: none;
}

/* FR viewer: hide EN chapter-items. An EN item is a chapter-item
 * whose anchor href is an internal, non-FR target. */
html[lang="fr"] .chapter li.chapter-item:has(> span > a[href]):not(:has(> span > a[href^="fr/"])):not(:has(> span > a[href*="/fr/"])):not(:has(> span > a[href^="http"])):not(:has(> span > a[href^="#"])) {
  display: none;
}
/* FR viewer: hide every part-title whose next chapter-item is an EN
 * entry. The Version française part-title is the one that sits just
 * before an FR entry and is therefore kept. */
html[lang="fr"] .chapter li.part-title:not(:has(+ li.chapter-item span > a[href^="fr/"])):not(:has(+ li.chapter-item span > a[href*="/fr/"])) {
  display: none;
}

.lucid-skip {
  position: fixed;
  top: var(--lucid-space-sm);
  inset-inline-start: var(--lucid-space-sm);
  z-index: 2000;
  padding: var(--lucid-space-sm) var(--lucid-space-md);
  background: var(--links);
  color: var(--bg);
  font-family: var(--font-display, var(--reading-font-body));
  font-weight: var(--weight-semibold, 600);
  text-decoration: none;
  border-radius: 4px;
  transform: translateY(-150%);
  transition: transform 150ms cubic-bezier(0.2, 0, 0, 1);
}
.lucid-skip:focus,
.lucid-skip:focus-visible {
  transform: translateY(0);
  outline: 3px solid var(--bg);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .lucid-skip { transition: none; }
}

/* ---------- Breadcrumbs ---------- */

.lucid-breadcrumbs {
  margin-block-end: var(--lucid-space-md);
  font-family: var(--font-display, var(--reading-font-body));
  font-size: var(--text-caption);
  color: var(--lucid-fog);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.lucid-breadcrumbs ol {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--lucid-space-xs);
  max-width: none; /* escape the 70ch body cap */
}
.lucid-breadcrumbs li {
  margin: 0;
}
.lucid-breadcrumbs li + li::before {
  content: "/";
  margin-inline-end: var(--lucid-space-xs);
  color: var(--lucid-mist, var(--lucid-fog));
}
.lucid-breadcrumbs li[aria-current="page"] {
  color: var(--fg);
  text-transform: none;
  font-weight: var(--weight-semibold, 600);
  letter-spacing: 0.02em;
}
.coal .lucid-breadcrumbs li + li::before,
.navy .lucid-breadcrumbs li + li::before,
.ayu  .lucid-breadcrumbs li + li::before {
  color: var(--lucid-mist-dark, var(--lucid-fog-dark));
}

/* ---------- Page TOC (right rail) ----------
 * Positioned absolutely to the right of the centered <main> on
 * viewports ≥ 1360px. On narrower screens it collapses above
 * the content (a dismissible "on this page" box), and on small
 * phones it hides entirely — /adapt will revisit.
 * ---------------------------------------- */

.lucid-pagetoc {
  /* Distinct surface from the audience callout and blockquote — a
     1px top rule instead of a tinted block. Reads as navigation,
     not content. Keeps --quote-bg reserved for pull-quotes. */
  max-width: none;
  margin-block: 0 var(--lucid-space-lg);
  padding-block: var(--lucid-space-md) 0;
  background: transparent;
  border-block-start: 1px solid var(--lucid-paper-edge);
  font-family: var(--reading-font-body);
  font-size: var(--text-small);
  line-height: 1.5;
}
.coal .lucid-pagetoc,
.navy .lucid-pagetoc,
.ayu  .lucid-pagetoc {
  border-block-start-color: var(--lucid-paper-dark-edge);
}
.lucid-pagetoc__label {
  margin: 0 0 var(--lucid-space-sm);
  font-family: var(--font-display, var(--reading-font-body));
  font-size: var(--text-caption);
  font-weight: var(--weight-semibold, 600);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--lucid-fog);
}
.coal .lucid-pagetoc__label,
.navy .lucid-pagetoc__label,
.ayu  .lucid-pagetoc__label { color: var(--lucid-fog-dark); }

.lucid-pagetoc ul {
  list-style: none;
  padding: 0;
  margin: 0;
  max-width: none; /* escape the 70ch cap */
}
.lucid-pagetoc li {
  margin: 0;
  padding-block: var(--lucid-space-2xs);
}
.lucid-pagetoc__nested {
  padding-inline-start: var(--lucid-space-md);
}
.lucid-pagetoc a {
  color: var(--fg);
  text-decoration: none;
  display: inline-block;
  padding-inline: var(--lucid-space-2xs);
  border-radius: 2px;
}
.lucid-pagetoc a:hover { color: var(--links); }
.lucid-pagetoc a[aria-current="location"] {
  color: var(--links);
  font-weight: var(--weight-semibold, 600);
}

/* 1360px: the minimum viewport at which a 220px TOC + xl gap fits
   in the right gutter past a centered 840px <main>. Below this, the
   TOC stays in-flow above the content (top-ruled box) — see the
   default .lucid-pagetoc block above. */
@media (min-width: 1360px) {
  .content { position: relative; }
  .lucid-pagetoc {
    position: absolute;
    top: 0;
    /* Anchor to the right edge of the centered <main> (840px cap),
       not the wide .content box. .content spans the full reading
       area (~1410px at a 1440 viewport); anchoring to 100% of that
       pushed the TOC off-screen. 50% + half the content-max-width
       lands exactly at main's right edge, + xl gap. */
    inset-inline-start: calc(50% + var(--content-max-width) / 2 + var(--lucid-space-xl));
    width: 220px;
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
    background: transparent;
    border-block-start: none;
    padding: 0;
  }
  /* Sticky applies to the inner list only. The .lucid-pagetoc
     container must stay position: absolute (set above) so it
     floats into the right rail and does NOT reserve vertical
     space inside .content — otherwise the first heading gets
     pushed down by the full height of the TOC. */
  .lucid-pagetoc ul {
    position: sticky;
    top: var(--lucid-space-xl);
  }
}
@media (max-width: 700px) {
  /* Tight viewports: hide the inline TOC; the sidebar is the
     primary nav anyway. Revisit in /adapt. */
  .lucid-pagetoc { display: none; }
}

/* ---------- Sidebar rhythm ----------
 * Tighter section titles, a bit more air between chapter items,
 * clearer hover affordance. /typeset already handled type sizes. */

.sidebar .chapter { padding-block: var(--lucid-space-sm); }
.sidebar .chapter li { margin: 0; }
.sidebar .chapter li a,
.sidebar .chapter li .chapter-item {
  padding-block: var(--lucid-space-2xs);
  padding-inline: var(--lucid-space-md);
  border-radius: 3px;
}
.sidebar .chapter li a:hover {
  background: var(--theme-hover);
}
.sidebar .chapter li.part-title {
  margin-block-start: var(--lucid-space-lg);
  margin-block-end: var(--lucid-space-xs);
  padding-inline: var(--lucid-space-md);
  color: var(--lucid-fog);
}
.coal .sidebar .chapter li.part-title,
.navy .sidebar .chapter li.part-title,
.ayu  .sidebar .chapter li.part-title {
  color: var(--lucid-fog-dark);
}
.sidebar .chapter li.part-title:first-child { margin-block-start: 0; }

/* Active item — subtle left-rail dot rather than the stock block
   background; matches the reading-companion voice and stays under
   the side-stripe ban (1px, semantic, not a "colored accent" card
   stripe). */
.sidebar .chapter li a.active {
  color: var(--links);
  font-weight: var(--weight-semibold, 600);
  background: transparent;
  position: relative;
}
.sidebar .chapter li a.active::before {
  content: "";
  position: absolute;
  inset-block: 0.3em;
  inset-inline-start: 0;
  width: 2px;
  background: var(--links);
  border-radius: 2px;
}

/* ---------- .content tightening ----------
 * mdBook occasionally ships with large heading margins that fight
 * our 1.5em heading margin-top. Reconfirm here, and lift the first
 * heading's margin so it doesn't push below the breadcrumb. */

/* :first-of-type (not :first-child) so the rule survives the pageToc
   injection: lucid-navigation.js inserts .lucid-pagetoc as first child
   of .content, which would otherwise disqualify the heading from the
   reset and let its default top margin push the H1 down the page. */
.content h1:first-of-type,
.content h2:first-of-type,
.content h3:first-of-type { margin-block-start: 0; }

.lucid-breadcrumbs + h1 { margin-block-start: 0; }

/* ---------- Tables + code: escape the 70ch body cap ----------
 * Typography caps .content p/ul/ol/blockquote. Tables, pre,
 * figures, and our reading-demo grid should be allowed to use
 * the full --content-max-width. */

.content :where(table, pre, figure, .reading-demo) {
  max-width: none;
  width: 100%;
}

/* ---------- Landing hero ----------
 * Introduction page only. The lens mark (ring + clean line) is the
 * "post-lens" counterpart to the 404's pre-lens wave — together they
 * bookend the brand's clarity metaphor. Scope is carried entirely by
 * the .lucid-landing__title class on the H1; sibling selectors handle
 * the rest so rule pages are never touched. */

.lucid-landing__mark {
  display: block;
  width: clamp(96px, 12vw, 128px);
  height: auto;
  margin-block: var(--lucid-space-lg) var(--lucid-space-md);
  color: var(--color-lucid);
  overflow: visible;
}
.lucid-landing__mark-line {
  stroke: var(--color-ink);
}

/* Hero rhythm: the first audience pill sits closer to the H1, then
   the lede paragraph gets its normal rhythm, then the stance panel
   (Before/After) is the first block-level content — the reading
   demo has been demoted below "Where to next". */
.lucid-landing__title + .lucid-audience {
  margin-block-start: var(--lucid-space-md);
}
.lucid-landing__title ~ .lucid-stance {
  margin-block-start: var(--lucid-space-xl);
}

/* Hide the auto-injected breadcrumbs on the landing: the display H1
   is its own anchor, and "Introduction" stacked above "Introduction"
   is noise. Scope is carried by the landing title presence. */
.content:has(.lucid-landing__title) .lucid-breadcrumbs {
  display: none;
}

/* ---------- 404 page ----------
 * "Pre-lens" empty state from the brand spec: the messy wave that
 * hasn't yet been clarified. Single glyph, single line of copy, one
 * link back. Bilingual via :lang() — head.hbs sets <html lang> for
 * /fr/* paths before paint, so the right block shows on first render. */

.lucid-404 {
  max-width: none;
  margin-block: var(--lucid-space-2xl) var(--lucid-space-xl);
  text-align: center;
  color: var(--fg);
}

/* The 404's right-rail ToC surfaces both EN and FR headings regardless
   of :lang(), because lucid-navigation.js generates it from the raw
   DOM headings. The page has one heading per locale so the ToC adds
   no navigation value — hide it entirely on this surface. */
.content:has(.lucid-404) .lucid-pagetoc {
  display: none;
}
.lucid-404__glyph {
  display: block;
  margin-inline: auto;
  color: var(--lucid-fog);
  width: clamp(220px, 40vw, 360px);
  height: auto;
}
.coal .lucid-404__glyph,
.navy .lucid-404__glyph,
.ayu  .lucid-404__glyph { color: var(--lucid-fog-dark); }

.lucid-404__copy {
  max-width: 48ch;
  margin: var(--lucid-space-lg) auto 0;
}
.lucid-404__copy h2 {
  margin-block: 0 var(--lucid-space-sm);
  font-size: var(--text-h2, 1.5rem);
}
.lucid-404__copy p {
  color: var(--lucid-fog);
  margin: 0;
}
.coal .lucid-404__copy p,
.navy .lucid-404__copy p,
.ayu  .lucid-404__copy p { color: var(--lucid-fog-dark); }

/* Show only the copy block matching <html lang>. Default is EN;
   :lang(fr) path swaps to FR. */
.lucid-404__copy--fr { display: none; }
:root:lang(fr) .lucid-404__copy--en { display: none; }
:root:lang(fr) .lucid-404__copy--fr { display: block; }

/* ---------- FR sidebar cue ----------
 * Injected by lucid-navigation.js when location is under /fr/*.
 * Frames the English chapter list honestly rather than pretending
 * the sidebar is French. Tone matches "en chantier": calm, not
 * apologetic. */

.lucid-fr-cue {
  padding: var(--lucid-space-md) var(--lucid-space-md) var(--lucid-space-sm);
  border-block-end: 1px solid var(--sidebar-header-border-color, var(--lucid-paper-edge));
  margin-block-end: var(--lucid-space-md);
}
.lucid-fr-cue__title {
  margin: 0 0 var(--lucid-space-xs);
  font-weight: var(--weight-semibold, 600);
  color: var(--links);
  font-size: 0.95rem;
}
.lucid-fr-cue__note {
  margin: 0;
  color: var(--lucid-fog);
  font-size: 0.82rem;
  line-height: 1.5;
}
.coal .lucid-fr-cue__note,
.navy .lucid-fr-cue__note,
.ayu  .lucid-fr-cue__note {
  color: var(--lucid-fog-dark);
}
.lucid-fr-cue__divider {
  margin: 0 0 var(--lucid-space-xs);
  padding-inline: var(--lucid-space-md);
  color: var(--lucid-fog);
  font-size: 0.78rem;
  font-weight: var(--weight-semibold, 600);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.coal .lucid-fr-cue__divider,
.navy .lucid-fr-cue__divider,
.ayu  .lucid-fr-cue__divider {
  color: var(--lucid-fog-dark);
}

/* ---------- Audience callout ---------- */

.lucid-audience {
  /* Distinct from both blockquote (tinted) and pagetoc (top-ruled):
     a framed callout on the paper-soft neutral, with a 1px ink-edge
     rule. Sized at --text-lead (~1.125em of body) so the first
     sentence carries more presence than the body paragraph that
     follows — the lede should lead, not whisper. */
  max-width: none;
  margin-block: var(--lucid-space-xs) var(--lucid-space-xl);
  padding: var(--lucid-space-md) var(--lucid-space-lg);
  background: var(--lucid-paper-soft);
  border: 1px solid var(--lucid-paper-edge);
  border-radius: 6px;
  font-size: var(--text-lead);
  line-height: var(--line-height-lead);
  color: var(--fg);
  font-weight: var(--weight-regular, 400);
}
.coal .lucid-audience,
.navy .lucid-audience,
.ayu  .lucid-audience {
  background: var(--lucid-paper-dark-soft);
  border-color: var(--lucid-paper-dark-edge);
}

/* ---------- Stance panel ----------
 * Introduction page only. Two stacked prose blocks (Before / After)
 * that *show* what lucid-lint does before any feature list. Literal-
 * over-decorative: typography does the work; no side-tab accents,
 * no tint blocks, no icons. The label sits above each side in
 * caption-case so the hierarchy is obvious without color. */

.lucid-stance {
  max-width: none;
  margin-block: var(--lucid-space-xl) var(--lucid-space-2xl);
  padding: 0;
  background: transparent;
  border: none;
}
.lucid-stance__pair {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--lucid-space-lg);
}
@media (min-width: 720px) {
  .lucid-stance__pair {
    grid-template-columns: 1fr 1fr;
    gap: var(--lucid-space-xl);
  }
}
.lucid-stance__side {
  padding: var(--lucid-space-md) 0;
  border-block-start: 1px solid var(--lucid-paper-edge);
}
.coal .lucid-stance__side,
.navy .lucid-stance__side,
.ayu  .lucid-stance__side {
  border-block-start-color: var(--lucid-paper-dark-edge);
}
.lucid-stance__label {
  margin: 0 0 var(--lucid-space-sm);
  font-family: var(--reading-font-body);
  font-size: var(--text-caption);
  font-weight: var(--weight-semibold, 600);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--lucid-fog);
}
.coal .lucid-stance__label,
.navy .lucid-stance__label,
.ayu  .lucid-stance__label { color: var(--lucid-fog-dark); }
.lucid-stance__side[data-stance-side="after"] .lucid-stance__label {
  color: var(--links);
}
.lucid-stance__prose {
  max-width: none;
  margin: 0;
  font-size: var(--text-body);
  line-height: var(--line-height-body);
  color: var(--fg);
}
.lucid-stance__side[data-stance-side="before"] .lucid-stance__prose {
  color: var(--lucid-fog);
}
.coal .lucid-stance__side[data-stance-side="before"] .lucid-stance__prose,
.navy .lucid-stance__side[data-stance-side="before"] .lucid-stance__prose,
.ayu  .lucid-stance__side[data-stance-side="before"] .lucid-stance__prose {
  color: var(--lucid-fog-dark);
}
/* Idea-preservation highlights: one colour per idea, matched on both
 * sides so readers see that the rewrite shortens sentences without
 * losing any. Restrained on purpose — a ~95% lightness tint, no
 * underline, no border, no icon. Text contrast on ink stays ≥ 14:1
 * (AAA). Colour is reinforcement, not information: position already
 * encodes "same idea". */
.lucid-stance__idea, .lucid-idea {
  background-color: var(--lucid-idea-tint, transparent);
  padding: 0.05em 0.22em;
  border-radius: 3px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.lucid-stance__idea[data-idea="1"] { --lucid-idea-tint: oklch(95% 0.03 255); }
.lucid-stance__idea[data-idea="2"] { --lucid-idea-tint: oklch(95% 0.035 300); }
.lucid-stance__idea[data-idea="3"] { --lucid-idea-tint: oklch(94% 0.04 70);  }

/* `.lucid-idea` is the generic variant, usable outside the stance
 * figure. Rule-page blockquotes already sit on a light-blue surface,
 * so idea #1 swaps hue 255 (blue) for 185 (teal) to stay distinct
 * from the blockquote background. Ideas #2 (violet) and #3 (ochre)
 * carry through unchanged. */
.lucid-idea[data-idea="1"] { --lucid-idea-tint: oklch(95% 0.04 185); }
.lucid-idea[data-idea="2"] { --lucid-idea-tint: oklch(95% 0.035 300); }
.lucid-idea[data-idea="3"] { --lucid-idea-tint: oklch(94% 0.04 70);  }
.lucid-idea[data-idea="4"] { --lucid-idea-tint: oklch(95% 0.035 15); }
.lucid-idea[data-idea="5"] { --lucid-idea-tint: oklch(94% 0.04 140); }

/* Dark themes: same hues, inverted lightness. Keep chroma low so the
 * highlight never steals focus from the prose. */
.coal .lucid-stance__idea[data-idea="1"],
.navy .lucid-stance__idea[data-idea="1"],
.ayu  .lucid-stance__idea[data-idea="1"] { --lucid-idea-tint: oklch(32% 0.05 255); }
.coal .lucid-stance__idea[data-idea="2"],
.navy .lucid-stance__idea[data-idea="2"],
.ayu  .lucid-stance__idea[data-idea="2"] { --lucid-idea-tint: oklch(32% 0.06 300); }
.coal .lucid-stance__idea[data-idea="3"],
.navy .lucid-stance__idea[data-idea="3"],
.ayu  .lucid-stance__idea[data-idea="3"] { --lucid-idea-tint: oklch(32% 0.05 70);  }

.coal .lucid-idea[data-idea="1"],
.navy .lucid-idea[data-idea="1"],
.ayu  .lucid-idea[data-idea="1"] { --lucid-idea-tint: oklch(32% 0.05 185); }
.coal .lucid-idea[data-idea="2"],
.navy .lucid-idea[data-idea="2"],
.ayu  .lucid-idea[data-idea="2"] { --lucid-idea-tint: oklch(32% 0.06 300); }
.coal .lucid-idea[data-idea="3"],
.navy .lucid-idea[data-idea="3"],
.ayu  .lucid-idea[data-idea="3"] { --lucid-idea-tint: oklch(32% 0.05 70);  }
.coal .lucid-idea[data-idea="4"],
.navy .lucid-idea[data-idea="4"],
.ayu  .lucid-idea[data-idea="4"] { --lucid-idea-tint: oklch(32% 0.06 15); }
.coal .lucid-idea[data-idea="5"],
.navy .lucid-idea[data-idea="5"],
.ayu  .lucid-idea[data-idea="5"] { --lucid-idea-tint: oklch(32% 0.05 140); }

/* Windows / forced-colors: drop the decorative tint and let the OS
 * palette take over. The three ideas remain legible because the
 * position-based pairing survives without colour. */
@media (forced-colors: active) {
  .lucid-stance__idea, .lucid-idea { background-color: transparent; }
}

.lucid-stance__caption {
  max-width: none;
  margin-block: var(--lucid-space-md) 0;
  font-size: var(--text-small);
  color: var(--lucid-fog);
  line-height: 1.5;
}
.coal .lucid-stance__caption,
.navy .lucid-stance__caption,
.ayu  .lucid-stance__caption { color: var(--lucid-fog-dark); }
.lucid-stance__caption code { font-size: 0.92em; }

/* ---------- Reading demonstrator ----------
 * Chip-selector variant (Introduction page): one live preview card
 * + a segmented chip row. The previous three-card grid moved below
 * the product-stance IA; keep the three-card rules below for any
 * future surface that wants the parallel-compare treatment. */

.reading-demo--chips {
  padding: var(--lucid-space-lg);
  background: var(--sidebar-bg);
  border-radius: 8px;
}
.reading-demo__preview {
  padding: var(--lucid-space-md);
  background: var(--bg);
  border-radius: 6px;
  margin-block: var(--lucid-space-md);
}
.reading-demo__preview .reading-demo__sample {
  margin-block-start: var(--lucid-space-sm);
}
.reading-demo__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--lucid-space-xs);
  margin-block: var(--lucid-space-md) 0;
}
.reading-demo__chip {
  padding: var(--lucid-space-xs) var(--lucid-space-md);
  min-height: 44px;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--reading-font-body);
  font-size: var(--text-small);
  font-weight: var(--weight-semibold, 600);
  border: 1px solid var(--lucid-paper-edge);
  border-radius: 999px;
  cursor: pointer;
  transition: background 150ms, color 150ms, border-color 150ms;
}
.coal .reading-demo__chip,
.navy .reading-demo__chip,
.ayu  .reading-demo__chip {
  border-color: var(--lucid-paper-dark-edge);
}
.reading-demo__chip:hover {
  border-color: var(--links);
  color: var(--links);
}
.reading-demo__chip[aria-pressed="true"] {
  background: var(--links);
  color: var(--bg);
  border-color: var(--links);
  cursor: default;
}
.reading-demo__chip[aria-pressed="true"] .reading-demo__default {
  background: var(--bg);
  color: var(--lucid-fog);
}
@media (prefers-reduced-motion: reduce) {
  .reading-demo__chip { transition: none; }
}

/* ---------- Reading demonstrator (legacy 3-card) ----------
 * Three-column font demo. Each card renders the same paragraph in
 * one of the three presets. The "Use this" buttons are wired in
 * lucid-navigation.js. Still used for the chips preview swap. */

.reading-demo {
  margin-block: var(--lucid-space-xl) var(--lucid-space-2xl);
  padding: var(--lucid-space-lg);
  background: var(--sidebar-bg);
  border-radius: 8px;
}
.reading-demo__note,
.reading-demo__caption {
  max-width: none;
  margin-block: 0 var(--lucid-space-md);
  color: var(--lucid-fog);
  font-size: var(--text-small);
}
.coal .reading-demo__note,
.coal .reading-demo__caption,
.navy .reading-demo__note,
.navy .reading-demo__caption,
.ayu  .reading-demo__note,
.ayu  .reading-demo__caption { color: var(--lucid-fog-dark); }
.reading-demo__caption { margin-block: var(--lucid-space-md) 0; }

.reading-demo__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22ch), 1fr));
  gap: var(--lucid-space-lg);
}

.reading-demo__card {
  display: flex;
  flex-direction: column;
  gap: var(--lucid-space-sm);
  padding: var(--lucid-space-md);
  background: var(--bg);
  border-radius: 6px;
  transition: box-shadow 150ms ease-out;
}
.reading-demo__card:focus-within {
  box-shadow: 0 0 0 1px var(--lucid-fog);
}
@media (prefers-reduced-motion: reduce) {
  .reading-demo__card { transition: none; }
}

.reading-demo__label {
  margin: 0;
  font-family: var(--reading-font-body);
  font-size: var(--text-caption);
  font-weight: var(--weight-semibold, 600);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--lucid-fog);
}
.coal .reading-demo__label,
.navy .reading-demo__label,
.ayu  .reading-demo__label { color: var(--lucid-fog-dark); }

.reading-demo__sample {
  margin: 0;
  max-width: none;
  font-size: var(--text-body);
  line-height: 1.7;
  color: var(--fg);
  flex: 1;
}
.reading-demo__sample[data-demo="atkinson"]  { font-family: "Atkinson Hyperlegible Next", ui-sans-serif, system-ui, sans-serif; }
.reading-demo__sample[data-demo="standard"]  { font-family: "Literata", Charter, Cambria, Georgia, serif; }
.reading-demo__sample[data-demo="dyslexic"]  { font-family: "OpenDyslexic", "Atkinson Hyperlegible Next", ui-sans-serif, sans-serif; }

.reading-demo__apply {
  align-self: flex-start;
  padding: var(--lucid-space-xs) var(--lucid-space-md);
  background: var(--bg);
  color: var(--links);
  font-family: var(--reading-font-body);
  font-size: var(--text-small);
  font-weight: var(--weight-semibold, 600);
  border: 1px solid currentColor;
  border-radius: 4px;
  cursor: pointer;
  min-height: 44px;  /* AAA touch target */
  transition: background 150ms, color 150ms;
}
.reading-demo__apply:hover {
  background: var(--links);
  color: var(--bg);
}
/* Tighter focus offset inside the card — 3px+3px from /typeset
   would clip against the 6px card radius; 2px keeps the ring
   cleanly around the button. */
.reading-demo__apply:focus-visible {
  outline-offset: 2px;
}
.reading-demo__apply[aria-pressed="true"] {
  background: var(--links);
  color: var(--bg);
  cursor: default;
}
.reading-demo__default {
  /* Self-contained chip — opts out of the apply-button inversion so
     "default" reads as meta-label in every state, not as echoed
     button copy. */
  display: inline-block;
  padding: 0.05em 0.5em;
  margin-inline-start: 0.4em;
  background: var(--lucid-paper-sink);
  color: var(--lucid-fog);
  border-radius: 999px;
  font-weight: var(--weight-regular, 400);
  font-size: 0.72em;
  letter-spacing: 0.02em;
  text-transform: lowercase;
  vertical-align: 0.1em;
}
.coal .reading-demo__default,
.navy .reading-demo__default,
.ayu  .reading-demo__default {
  background: var(--lucid-paper-dark-sink);
  color: var(--lucid-fog-dark);
}
/* When the button is pressed (active preset), keep the chip on its
   own neutral surface rather than following the inverted button. */
.reading-demo__apply[aria-pressed="true"] .reading-demo__default {
  background: var(--lucid-paper);
  color: var(--lucid-fog);
}
.coal .reading-demo__apply[aria-pressed="true"] .reading-demo__default,
.navy .reading-demo__apply[aria-pressed="true"] .reading-demo__default,
.ayu  .reading-demo__apply[aria-pressed="true"] .reading-demo__default {
  background: var(--lucid-paper-dark);
  color: var(--lucid-fog-dark);
}
@media (prefers-reduced-motion: reduce) {
  .reading-demo__apply { transition: none; }
}

/* ---------- Toast ---------- */

.lucid-toast {
  position: fixed;
  inset-inline-start: 50%;
  inset-block-end: var(--lucid-space-xl);
  transform: translate(-50%, 150%);
  padding: var(--lucid-space-sm) var(--lucid-space-lg);
  background: var(--fg);
  color: var(--bg);
  font-family: var(--reading-font-body);
  font-size: var(--text-small);
  border-radius: 6px;
  box-shadow: 0 4px 12px oklch(0% 0 0 / 0.15);
  opacity: 0;
  pointer-events: none;
  transition: transform 200ms cubic-bezier(0.2, 0, 0, 1), opacity 200ms;
  z-index: 1500;
}
/* Dark theme: a weightier shadow so the toast doesn't float
   indistinct against the near-black background. */
.coal .lucid-toast,
.navy .lucid-toast,
.ayu  .lucid-toast {
  box-shadow: 0 6px 18px oklch(0% 0 0 / 0.45);
}
.lucid-toast.is-visible {
  transform: translate(-50%, 0);
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .lucid-toast { transition: none; }
}

/* ---------- Brand mark (sidebar) ---------- */

.menu-title { display: flex; align-items: center; gap: var(--lucid-space-sm); }
.lucid-brand__mark {
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
}

/* ---------- Language switch (header) ----------
 * Segmented pill: "Bilingual from day one" is principle #4 — the
 * switch has to read as a visible control, not an afterthought.
 * The pill's border tracks --lucid-paper-edge so it sits quietly
 * among the stock icon-buttons without adding visual weight. */

.lucid-lang {
  display: inline-flex;
  align-items: center;
  margin-inline-start: var(--lucid-space-sm);
  padding: 2px;
  gap: 0;
  border: 1px solid var(--lucid-paper-edge);
  border-radius: 999px;
  font-family: var(--reading-font-body);
  font-size: var(--text-small);
  font-weight: var(--weight-semibold, 600);
}
.coal .lucid-lang,
.navy .lucid-lang,
.ayu  .lucid-lang {
  border-color: var(--lucid-paper-dark-edge);
}
.lucid-lang__link {
  color: var(--lucid-fog);
  text-decoration: none;
  padding: var(--lucid-space-2xs) var(--lucid-space-md);
  border-radius: 999px;
  min-height: 32px;
  display: inline-flex;
  align-items: center;
  transition: background 150ms, color 150ms;
}
.coal .lucid-lang__link,
.navy .lucid-lang__link,
.ayu  .lucid-lang__link { color: var(--lucid-fog-dark); }
.lucid-lang__link.is-active {
  background: var(--links);
  color: var(--bg);
}
.lucid-lang__link:hover:not(.is-active),
.lucid-lang__link:focus-visible:not(.is-active) {
  color: var(--links);
}
.lucid-lang__sep {
  display: none;
}
@media (prefers-reduced-motion: reduce) {
  .lucid-lang__link { transition: none; }
}

/* ---------- Page-nav chevrons ----------
 * mdBook's default chrome renders ‹ › arrows as fixed elements
 * along the page edges (.nav-chapters.previous / .nav-chapters.next).
 * On desktop they float over the content and read as decorative.
 * The sidebar + the bottom "Next chapter" link already cover the
 * need. Hide the floating arrows at ≥ 700px; keep mobile where the
 * sidebar collapses. */

@media (min-width: 700px) {
  .nav-chapters.previous,
  .nav-chapters.next,
  .nav-wrapper .previous,
  .nav-wrapper .next,
  .mobile-nav-chapters {
    display: none !important;
  }
}

/* ---------- GFM alert side-stripe cap ----------
 * mdBook stock theme stamps a 4-px colored border on GitHub-flavored
 * alert blockquotes (> [!NOTE]). That's the same side-tab pattern the
 * brand explicitly bans. Cap it at 1 px across all alert flavors; the
 * colored flavor-token still carries the semantic, the stripe just
 * stops shouting. */

.content blockquote.alert,
.content blockquote.markdown-alert,
.content blockquote[class*="alert-"] {
  border-inline-start-width: 1px !important;
  padding-inline-start: 1em;
}

/* ---------- Version-gated callout (.since-version) ----------
 * Marks a paragraph that describes behavior introduced in a specific
 * release (e.g. "Since v0.3 — 3–4-word Oxford items now exempt").
 * Shipped to prod ahead of the release so readers see what changed
 * the moment a sub-slice lands. Conceptually the version is the
 * moment a change came into focus, so the badge echoes the brand's
 * lens motif (ring + focal dot) before the tag.
 *
 * Markup contract (see CONTRIBUTING.md):
 *   <aside class="since-version" aria-label="New in v0.3">
 *     <span class="since-version__tag">Since v0.3</span> — One-line
 *     summary in body voice.
 *   </aside>
 *
 * Why <aside> and not <blockquote>: blockquote is reserved for
 * italic pull-quotes in this theme (typography.css:301). A version
 * callout is informative, not literary.
 *
 * No border-left stripe: brand-banned and the most recognisable
 * AI-doc tell. The tint + lens glyph carry the affordance instead.
 * ---------------------------------------- */

.content .since-version {
  /* Soft warm tint — same family as the audience callout, calmer. */
  background: color-mix(in oklch, var(--color-lucid) 4%, var(--color-paper));
  padding: var(--lucid-space-md) var(--lucid-space-lg);
  border-radius: 2px;
  margin-block: var(--lucid-space-lg);
  max-width: var(--measure);
}
.content .since-version > p {
  margin: 0;
}
.content .since-version > p + p {
  margin-block-start: 0.5em;
}

.since-version__tag {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5em;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  /* AAA on small text — color-fog (4.9:1) would fail the bar.
     Recede via size + weight + uppercase, not via contrast loss. */
  color: var(--color-ink);
  white-space: nowrap;
}

/* Lens glyph — pure-CSS ring + focal dot in --color-lucid.
   Sized in em so it scales with the tag's font-size. */
.since-version__tag::before {
  content: "";
  width: 0.65em;
  height: 0.65em;
  border-radius: 50%;
  border: 1.5px solid var(--color-lucid);
  background: radial-gradient(
    circle at 50% 50%,
    var(--color-lucid) 0 18%,
    transparent 19%
  );
  align-self: center;
  flex-shrink: 0;
}

/* ============================================================
 * Responsive adaptations — /adapt pass
 *
 * Breakpoints:
 *   ≤ 1200px — page TOC collapses to the narrow inline state
 *              (already handled above; pagetoc hides at 700px).
 *   ≤  900px — tablet portrait / narrow-laptop tightening.
 *   ≤  700px — phone: stack, tighten, hide secondary chrome.
 *   ≤  480px — small phone: drop the header language switch;
 *              the sidebar drawer is still the primary route.
 *
 * mdBook handles the sidebar-drawer toggle itself on narrow
 * viewports; we only tune what our theme adds on top.
 * ============================================================ */

@media (max-width: 900px) {
  .lucid-audience {
    padding: var(--lucid-space-sm) var(--lucid-space-md);
    margin-block: var(--lucid-space-xs) var(--lucid-space-lg);
  }
  .reading-demo {
    padding: var(--lucid-space-md);
    margin-block: var(--lucid-space-lg) var(--lucid-space-xl);
  }
  .reading-demo__grid {
    gap: var(--lucid-space-md);
  }
}

@media (max-width: 700px) {
  /* Body reading metrics: keep typography's 70ch cap but let
     mdBook's padding breathe on small screens. */
  .content {
    padding-inline: var(--lucid-space-sm);
  }

  /* Audience callout: a shade smaller; still readable. */
  .lucid-audience {
    font-size: var(--text-body);
  }

  /* Reading demonstrator: single column, full-width apply button
     for a comfortable thumb target. The grid already stacks via
     minmax(min(100%, 22ch), 1fr) — we just widen the buttons. */
  .reading-demo__card {
    padding: var(--lucid-space-md);
  }
  .reading-demo__apply {
    align-self: stretch;
    text-align: center;
    justify-content: center;
  }

  /* Breadcrumbs: already flex-wrap; tighten caption size a touch. */
  .lucid-breadcrumbs { font-size: 0.72rem; }

  /* Toast: edge-to-edge card instead of a centered pill. Easier to
     spot, never clipped by a narrow viewport. */
  .lucid-toast {
    inset-inline: var(--lucid-space-md);
    inset-block-end: var(--lucid-space-md);
    left: auto;
    transform: translateY(150%);
    text-align: center;
  }
  .lucid-toast.is-visible {
    transform: translateY(0);
  }

  /* Sidebar brand mark: stay in one line; ellipsize the wordmark
     rather than wrapping or overflowing the drawer. */
  .menu-title {
    min-width: 0;
  }
  .menu-title > :not(.lucid-brand__mark) {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Skip link: fill the top edge so it's obvious on focus. */
  .lucid-skip {
    inset-inline: var(--lucid-space-sm);
    text-align: center;
  }
}

@media (max-width: 480px) {
  /* Drop the header language switch on the smallest viewports —
     mdBook's menu row runs out of room and the FR stub is still
     reachable from the sidebar Project section. */
  .lucid-lang { display: none; }

  /* Page TOC is already hidden ≤ 700px; this is a safety net for
     any overflow on very small phones. */
  .lucid-pagetoc { display: none !important; }
}

/* ---------- Forced-colors (Windows HC mode) ----------
 * OKLCH surfaces can flatten to system colors in forced-colors
 * mode, which is desirable — but we lose the 1px "active" rail
 * and the 1px borders on demo buttons. Reassert them with the
 * system keywords so the interactive structure survives. */
@media (forced-colors: active) {
  .sidebar .chapter li a.active::before {
    background: CanvasText;
  }
  .reading-demo__apply {
    border: 1px solid ButtonText;
    forced-color-adjust: none;
    color: ButtonText;
    background: ButtonFace;
  }
  .reading-demo__apply[aria-pressed="true"] {
    background: Highlight;
    color: HighlightText;
  }
  .lucid-skip {
    outline: 2px solid CanvasText;
  }
  .lucid-lang__link.is-active {
    text-decoration: underline;
  }
}

/* Tablet / narrow-desktop (≥ 700px, < 1360px): the TOC is inline
   above content — the right gutter isn't wide enough to host a
   220px rail past a centered 840px <main>. Constrain its width so
   it doesn't stretch the full content measure — easier to scan. */
@media (min-width: 700px) and (max-width: 1359px) {
  .lucid-pagetoc {
    max-width: 48ch;
  }
}

/* ---------- Reading-progress line ----------
 * A 2px --color-lucid line pinned to the viewport top, scaled by
 * scroll depth via CSS `animation-timeline: scroll()`. Zero JS,
 * hardware-accelerated, degrades cleanly in browsers that don't
 * support the property (the element stays at scaleX(0) and is
 * invisible). Gated off on the landing (its own H1 is the anchor)
 * and the 404 (content fits the viewport). Hidden entirely under
 * prefers-reduced-motion — the progress signal is worth less than
 * honoring the user's motion preference. */

@keyframes lucid-reading-progress {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

body::before {
  content: "";
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  height: 2px;
  background: var(--color-lucid);
  transform: scaleX(0);
  transform-origin: left center;
  pointer-events: none;
  z-index: 50;
  animation: lucid-reading-progress linear both;
  animation-timeline: scroll(root);
}

/* Suppress on surfaces that carry their own attention: the landing
   has the display H1 + lens mark; the 404 is a one-screen dead-end. */
body:has(.lucid-landing__title)::before,
body:has(.lucid-404)::before {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  body::before { display: none; }
}
