/* ============================================================
   Alex M. Chong — Article Templates Stylesheet
   File: style-articles.css (renamed from style.css for clarity)

   Scope: the article-style content templates only — the rendering
   surfaces for article, journal-entry, live-session, experiment,
   and experiment-html. Page-style chrome (landing, about, resume,
   etc.) lives in style-pages.css.

   When a new layout introduces a token or component, extend the
   relevant section below — don't pull from the design system
   wholesale. Every rule here should be reachable from a page.
   ------------------------------------------------------------
   Sections:
     1. Reset
     2. Tokens
     3. Base shell
     4. Layout chrome (.layout-nav, .layout-footer)
     5. Article layout
   ============================================================ */


/* ── 1. Reset ───────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { height: 100%; }
a { color: inherit; }


/* ── 2. Tokens ──────────────────────────────────────────── */


/* ── DS v2.1 (Phase 22.4) ───────────────────────────────────
   Blocks slice of the design system. Copied VERBATIM from
   _templates/style-articles.css, MINUS its :root token block
   (tokens now come from tokens.css, loaded ahead of this file).

   Two exceptions to "verbatim":
     1. The single self-containment :root below — --grain is the
        only local token not yet in tokens.css (asset URL; → 22.6).
     2. .event-format-tag colours swapped from raw hex to --c-*
        tokens (decision 22.1 #3 / audit D8) — see those rules.

   Linked DIRECTLY on master-layout.php (not via the system-public
   barrel) to avoid pages.css marketing chrome bleeding onto
   articles. Old style-articles.css still loads as a safety net
   until Phase 22.6.
   ──────────────────────────────────────────────────────────── */
:root {
  --grain: url("/_layout/background.png");
}



/* ── 3. Base shell ──────────────────────────────────────── */

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  font-family: var(--font);
  font-weight: 500;
  color: var(--primary);
  background-color: var(--neutral);
  background-image: var(--grain);
  background-size: 200px 200px;
  background-repeat: repeat;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip; /* contain viewport-width hero figures */
}


/* ── 4. Layout chrome ───────────────────────────────────── */

.layout-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 52px;
  padding: 0 var(--space-48);
  /* Frosted nav matching marketing pages — translucent + backdrop-blur,
     with the body's dot-grid texture layered in via the ::before below
     so the header reacts to scrolled content rather than sitting flat. */
  background-color: transparent;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  isolation: isolate;
  border-bottom: var(--rule-30);
}
.layout-nav::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--grain);
  background-size: 200px 200px;
  background-repeat: repeat;
  mix-blend-mode: multiply;
  opacity: 0.6;
  pointer-events: none;
}
.layout-nav > * {
  position: relative;
  z-index: 1;
}
.layout-nav-logo {
  display: inline-flex;
  align-items: center;
  min-width: 100px;
  height: 100%;
  font-family: var(--font-serif);
  font-style: italic;
  font-size: var(--text-body-lg);
  color: var(--primary);
  text-decoration: none;
}
.layout-nav-logo img {
  display: block;
  height: 28px;
  width: auto;
}
.layout-nav-links { display: flex; gap: var(--space-4); }
.layout-nav-links a {
  display: inline-block;
  padding: 6px 12px;
  border-radius: var(--r-pill);
  background: var(--canvas-bg);
  border: var(--rule);
  font-family: var(--font-mono);
  font-size: var(--text-pill);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--primary);
  text-decoration: none;
  text-transform: uppercase;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.layout-nav-links a:hover,
.layout-nav-links a.is-active { background: var(--primary); color: var(--surface); }

/* Hamburger toggle — hidden on desktop, shown below 1024 (X1). */
.layout-nav-toggle { display: none; }

/* ── Nav drawer (X1) — hamburger + full-height slide-in drawer below desktop
   (tablet + phone); logo to the gutter + ~1.1× (X11a). Mirrors pages.css. ── */
@media (max-width: 1024px) {
  .layout-nav {
    padding-left: var(--space-20);
    padding-right: var(--space-20);
    /* Bar keeps its real frosted blur (backdrop-filter from the base rule). */
  }
  .layout-nav-logo { position: relative; z-index: 201; }
  .layout-nav-logo img { height: 31px; }

  .layout-nav-toggle {
    display: inline-flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-end; /* bars hug the right → align to the gutter (= menu items) */
    gap: 4px;
    width: 44px;
    height: 44px;
    margin-left: auto;
    padding: 10px 0 10px 10px;
    background: transparent;
    border: 0;
    cursor: pointer;
    position: relative;
    z-index: 201;
  }
  .layout-nav-toggle span {
    display: block;
    height: 2px;
    width: 19px; /* ~0.8× bars, same 44px tap target */
    background: var(--primary);
    transition: transform 0.2s ease, opacity 0.2s ease;
  }
  .layout-nav.is-open .layout-nav-toggle span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
  .layout-nav.is-open .layout-nav-toggle span:nth-child(2) { opacity: 0; }
  .layout-nav.is-open .layout-nav-toggle span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

  /* Frosted panel — translucent surface + grain texture (no nested blur). */
  .layout-nav-links {
    position: fixed;
    z-index: 200;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-8);
    background: var(--surface); /* solid panel + grain texture (::before) */
    overflow-y: auto;
    isolation: isolate;
    /* TABLET default: full-height side drawer from the right. */
    top: 0;
    right: 0;
    height: 100vh;
    width: min(78vw, 340px);
    padding: calc(52px + var(--space-16)) var(--space-20) var(--space-24);
    border-left: var(--rule);
    transform: translateX(100%);
    transition: transform 0.25s ease; /* tablet drawer animates in AND out */
  }
  .layout-nav-links::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    background-image: var(--grain);
    background-size: 200px 200px;
    background-repeat: repeat;
    mix-blend-mode: multiply;
    opacity: 0.6;
    pointer-events: none;
  }
  .layout-nav-links a {
    position: relative;
    z-index: 1;
    font-size: var(--text-base); /* 14px — the nav/UI token */
    padding: var(--space-12);
    border-radius: var(--r-card);
  }
  /* Notification dot — inline just right of the label, a touch bigger (overrides
     render_nav's inline corner-dot styles; desktop bar dot untouched). */
  .layout-nav-links a > span[aria-hidden] {
    position: static !important;
    top: auto !important; right: auto !important; left: auto !important; bottom: auto !important;
    display: inline-block;
    width: 11px !important;
    height: 11px !important;
    margin-left: var(--space-8);
    vertical-align: middle;
    transform: translateY(-1px); /* optical centre — middle sat 1px low */
  }
  /* Pill highlight (with text) — same: inline just right of the label, a touch bigger. */
  .layout-nav-links a > .layout-nav-pill {
    position: static !important;
    top: auto !important; right: auto !important; left: auto !important; bottom: auto !important;
    display: inline-block !important;
    line-height: 1;
    margin-left: var(--space-8);
    vertical-align: middle;
    font-size: var(--text-label) !important;
    padding: 2px 6px 3px 7px !important; /* pill height + optical centring (Alex-tuned) */
    border-radius: 5px !important;
    transform: translateY(-1px);
  }
  .layout-nav.is-open .layout-nav-links {
    transform: translateX(0);
    transition: transform 0.25s ease;
  }
  /* Dim overlay. TABLET (side drawer): full screen incl. the bar. Mobile
     overrides to start below the bar. Sized in vw/vh because the bar's
     backdrop-filter makes it the containing block (inset:0 would clip to 52px). */
  .layout-nav::after {
    content: "";
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    z-index: 199;
    background: rgba(0, 0, 0, 0.3); /* dim behind the open menu */
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.22s ease, visibility 0s linear 0.22s;
  }
  .layout-nav.is-open::after {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 0.22s ease;
  }
}

/* MOBILE only — menu drops DOWN from under the bar. Item sizing inherits the
   tablet drawer size (no enlargement). */
@media (max-width: 767px) {
  .layout-nav-links {
    top: 52px;
    left: 0;
    right: 0;
    height: auto;
    width: auto;
    max-height: calc(100vh - 52px);
    padding: var(--space-20); /* uniform gutter: bar-to-first-item == side gutters */
    border-left: 0;
    border-bottom: var(--rule);
    /* Unfurl downward from under the bar via clip — never crosses the bar line. */
    transform: none;
    clip-path: inset(0 0 100% 0);
    opacity: 0;
    visibility: hidden;
  }
  .layout-nav.is-open .layout-nav-links {
    clip-path: inset(0 0 0 0);
    opacity: 1;
    visibility: visible;
    transition: clip-path 0.24s ease, opacity 0.18s ease;
  }
  /* Mobile dropdown: dim starts below the bar (bar stays clear). */
  .layout-nav::after { top: 52px; height: calc(100vh - 52px); }
  /* Per-item "hide on mobile" (phone only) — set via the Navigation editor.
     Applies to both the header menu and the footer link row. */
  .layout-nav-links a.is-hide-mobile,
  .layout-footer-right a.is-hide-mobile { display: none; }
}

/* Suppress nav transitions during viewport resize (no breakpoint-cross ghost). */
html.nav-no-anim .layout-nav-links,
html.nav-no-anim .layout-nav::after { transition: none !important; }

.layout-footer {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 28px var(--space-48);
  background: transparent;
  border-top: var(--rule-30);
}
.layout-footer-left,
.layout-footer-right a {
  font-family: var(--font-cond);
  font-size: var(--text-meta);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--primary);
  text-decoration: none;
}
.layout-footer-right { display: flex; gap: var(--space-16); }

/* Footer (X6) — mobile: centre-aligned, links on top, copyright at the bottom.
   DOM order is copyright-then-links, so column-reverse puts links first. */
@media (max-width: 600px) {
  .layout-footer {
    flex-direction: column-reverse;
    align-items: center;
    text-align: center;
    gap: var(--space-12);
    padding: var(--space-20);
  }
  .layout-footer-right { gap: var(--space-12); justify-content: center; flex-wrap: wrap; }
}


/* ── 5. Article layout ──────────────────────────────────────
   Long-form reading template. Single centred column, generous
   line-height, opening serif, pull quote, tag footer. Category
   tint is driven by [data-category] on the <article> root —
   adding a new category is one mapping line below.
   ──────────────────────────────────────────────────────── */

/* Category mapping — one line per hue, driven by data-category. The CMS
   stores the colour slug on the category row; templates set the matching
   attribute on the <article> root; every category-tinted element below
   reads var(--c-current, var(--primary)). Matches every brand --c-* token
   defined in _design-system/css/tokens.css. */
.article[data-category="rust"]       { --c-current: var(--c-rust); }
.article[data-category="terracotta"] { --c-current: var(--c-terracotta); }
.article[data-category="clay"]       { --c-current: var(--c-clay); }
.article[data-category="amber"]      { --c-current: var(--c-amber); }
.article[data-category="ochre"]      { --c-current: var(--c-ochre); }
.article[data-category="olive"]      { --c-current: var(--c-olive); }
.article[data-category="moss"]       { --c-current: var(--c-moss); }
.article[data-category="forest"]     { --c-current: var(--c-forest); }
.article[data-category="sage"]       { --c-current: var(--c-sage); }
.article[data-category="teal"]       { --c-current: var(--c-teal); }
.article[data-category="ocean"]      { --c-current: var(--c-ocean); }
.article[data-category="denim"]      { --c-current: var(--c-denim); }
.article[data-category="indigo"]     { --c-current: var(--c-indigo); }
.article[data-category="purple"]     { --c-current: var(--c-purple); }
.article[data-category="violet"]     { --c-current: var(--c-violet); }
.article[data-category="plum"]       { --c-current: var(--c-plum); }
.article[data-category="mauve"]      { --c-current: var(--c-mauve); }
.article[data-category="rose"]       { --c-current: var(--c-rose); }

.article-breadcrumb {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  /* BUG-2 (X11c): align the breadcrumb gutter to the nav logo gutter (space-48),
     not the wider space-64 it had — they were visibly misaligned on desktop. */
  padding: var(--space-16) var(--space-48);
  background: var(--surface);
  border-bottom: var(--rule-faint);
  font-family: var(--font-cond);
  font-size: var(--text-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.article-breadcrumb-root {
  font-weight: 700;
  color: var(--muted);
  text-decoration: none;
}
.article-breadcrumb-root:hover { color: var(--primary); }
.article-breadcrumb-sep { color: var(--muted); }
.article-breadcrumb-current { font-weight: 700; color: var(--primary); }

.article {
  max-width: 720px;
  margin: 0 auto;
  padding: var(--space-56) var(--space-40) var(--space-80);
}

.article-header { margin-bottom: var(--space-40); }

.article-meta {
  display: flex;
  align-items: center;
  gap: var(--space-12);
  margin-bottom: var(--space-20);
  font-family: var(--font-cond);
  text-transform: uppercase;
}
.article-category {
  font-size: var(--text-pill);
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--c-current, var(--primary));
}
.article-meta-sep { color: var(--ink-18); }
.article-meta-date,
.article-meta-updated {
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.10em;
  color: var(--muted);
}

.article-title {
  font-size: var(--text-h0);
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.1;
  margin-bottom: var(--space-20);
}
.article-title-serif {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  display: inline-block; /* prevents descender clipping */
}

/* Modifier: full Instrument Serif italic title (no Barlow base) */
.article-title.is-serif {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.05;
}

/* Summary: optional short deck rendered as a larger-body paragraph
   below the title. Drawn from the `summary` field. Hide when empty. */
.article-summary {
  font-size: var(--text-body-lg);
  font-weight: 500;
  line-height: 1.5;
  color: var(--secondary);
  margin-bottom: var(--space-32);
}

/* Author byline: avatar + name + short tagline, rendered as a single
   inline line ("Alex M. Chong – UX Design Director & Career Coach").
   Drawn from the Author config (single-author site today; injected into
   every page). The full Author config has four fields — image, name,
   short_description, extended_description. The byline uses the first
   three; the footer Author Bio block uses image + extended_description. */
.article-author {
  display: flex;
  align-items: center;
  gap: var(--space-16);
  margin-bottom: var(--space-32);
  padding-bottom: var(--space-24);
  border-bottom: var(--rule-faint);
}
.article-author-info {
  font-size: var(--text-md);
  line-height: 1.4;
  min-width: 0;
}
.article-author-name { font-weight: 600; color: var(--primary); }
.article-author-separator { color: var(--muted); margin: 0 4px; }
.article-author-tagline { font-weight: 500; color: var(--secondary); }

/* Author Bio: extended description block in the footer area. Image plus
   the long-form bio paragraph. Independently toggleable from the byline. */
.article-author-bio {
  display: flex;
  align-items: flex-start;
  gap: var(--space-24);
  margin: var(--space-48) 0 var(--space-24);
  padding: var(--space-32);
  background: var(--surface);
  border: var(--rule-faint);
  border-radius: var(--r-card);
}
.article-author-bio-extended {
  font-size: var(--text-md);
  line-height: 1.7;
  color: var(--secondary);
  margin: 0;
}

/* Shared avatar styling for both byline (48px) and bio (80px). */
.article-author-avatar,
.article-author-bio-avatar {
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--canvas-bg);
  border: var(--rule-faint);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--secondary);
  overflow: hidden;
}
.article-author-avatar { width: 48px; height: 48px; font-size: var(--text-body-lg); }
.article-author-bio-avatar { width: 80px; height: 80px; font-size: 32px; }
.article-author-avatar img,
.article-author-bio-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* When a real photo is present, drop the ring around the circular crop so the
   image stays crisp (Author Line bundle, companion #1). Initials-only avatars
   keep their outline. :has() raises specificity above both the light
   (--rule-faint) and dark (white-15) avatar borders. */
.article-author-avatar:has(img),
.article-author-bio-avatar:has(img) { border: none; }

.article-series {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: var(--space-32);
  border-bottom: var(--rule-faint);
}
.article-series-pill {
  font-family: var(--font-cond);
  font-size: var(--text-pill);
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  border: 1px solid color-mix(in srgb, var(--c-current, var(--primary)) 35%, transparent);
  color: var(--c-current, var(--primary));
  text-decoration: none;
  display: inline-block;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}
a.article-series-pill:hover {
  background-color: color-mix(in srgb, var(--c-current, var(--primary)) 8%, transparent);
  border-color: color-mix(in srgb, var(--c-current, var(--primary)) 60%, transparent);
}
.article-series-progress {
  font-family: var(--font-cond);
  font-size: var(--text-pill);
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--muted);
}
.article-series-dots { display: flex; gap: 3px; align-items: center; }
.article-series-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--c-current, var(--primary)) 20%, transparent);
}
.article-series-dot.is-active {
  background: var(--c-current, var(--primary));
}

/* Special tag (Principle / Framework) — articles only. Renders inline with
   the series row when set. Tinted by the active category colour. */
.article-special-tag {
  font-family: var(--font-cond);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  border: 1px solid color-mix(in srgb, var(--c-current, var(--primary)) 40%, transparent);
  color: var(--c-current, var(--primary));
  background: transparent;
}

/* ── Article hero image ─────────────────────────────────────
   Optional hero figure that sits between the article header and
   the prose body. Rendered as its own block so the CMS can toggle
   it independently of inline images in the body. The data-size
   attribute drives the same default / wide / full variants used
   by figures inside .article-prose.
   ──────────────────────────────────────────────────────── */

.article-hero {
  margin: 0 0 var(--space-40);
}
.article-hero img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-card);
  border: var(--rule-faint);
}
.article-hero figcaption {
  font-family: var(--font-cond);
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: var(--space-12);
  text-align: center;
}
.article-hero[data-size="wide"],
.article-hero[data-size="full"] {
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  max-width: none;
}
.article-hero[data-size="wide"] {
  width: min(1080px, calc(100vw - var(--space-32) * 2));
}
.article-hero[data-size="full"] {
  width: 100vw;
}
.article-hero[data-size="full"] img {
  border-radius: 0;
  border-left: 0;
  border-right: 0;
}
.article-hero[data-size="wide"] figcaption,
.article-hero[data-size="full"] figcaption {
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
}


/* ── Article prose ──────────────────────────────────────────
   Wraps the rich-text body. The selectors below style the bare
   HTML the CMS editor (Tiptap) emits — no per-paragraph classes
   needed in the body. Add new tag styles here as the editor's
   block library grows.
   ──────────────────────────────────────────────────────── */

.article-prose { margin-top: var(--space-40); }
.article-prose > * + * { margin-top: var(--space-20); }

/* Default paragraph */
.article-prose p {
  font-size: var(--text-md);
  font-weight: 500;
  line-height: 1.8;
  color: var(--secondary);
}

/* Lede: the first paragraph styled as Instrument Serif italic intro.
   `:first-of-type` matches the first <p> in the prose, so the rule still
   applies when a hero figure precedes it. */
.article-prose > p:first-of-type {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: var(--text-h4);
  line-height: 1.5;
  color: var(--secondary);
}

/* Headings — body content only (article H1 is outside .article-prose) */
.article-prose h2 {
  font-size: var(--text-h4);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--primary);
  margin-top: var(--space-48);
}
.article-prose h3 {
  font-size: var(--text-body-lg);
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.35;
  color: var(--primary);
  margin-top: var(--space-32);
}
.article-prose h4 {
  font-family: var(--font-cond);
  font-size: var(--text-base);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--primary);
  margin-top: var(--space-32);
}

/* Pull quote */
.article-prose blockquote {
  border-left: 2px solid var(--primary);
  padding-left: 28px;
  margin: 36px 0;
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: var(--text-h4); /* matches .article-prose p:first-of-type */
  line-height: 1.45;
  color: var(--primary);
}
/* Tiptap always wraps blockquote content in a <p> (ProseMirror's
   blockquote node requires block content). `.article-prose p` matches
   that inner <p> directly and otherwise wins (same specificity, explicit
   font-size/weight/line-height/color) — so it silently overrode every
   property above except font-family/font-style, which inherit untouched.
   Force the inner <p> back to inheriting from blockquote. */
.article-prose blockquote p {
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
  color: inherit;
}

/* Lists */
.article-prose ul,
.article-prose ol {
  padding-left: var(--space-24);
  font-size: var(--text-md);
  font-weight: 500;
  line-height: 1.7;
  color: var(--secondary);
}
.article-prose li + li { margin-top: var(--space-8); }
.article-prose li::marker { color: var(--muted); }

/* Inline elements */
.article-prose a {
  color: var(--primary);
  text-decoration: underline;
  text-decoration-color: var(--ink-30);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: text-decoration-color 0.15s ease, color 0.15s ease;
}
.article-prose a:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}
.article-prose strong { font-weight: 700; color: var(--primary); }
.article-prose em { font-style: italic; }

/* Inline + block code */
.article-prose code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--canvas-bg);
  padding: 2px 6px;
  border-radius: 3px;
  border: var(--rule-faint);
  color: var(--primary);
}
.article-prose pre {
  font-family: var(--font-mono);
  font-size: var(--text-base);
  line-height: 1.6;
  background: var(--canvas-bg);
  padding: var(--space-16) var(--space-20);
  border: var(--rule-faint);
  border-radius: var(--r-card);
  overflow-x: auto;
  color: var(--primary);
}
.article-prose pre code {
  background: none;
  border: none;
  padding: 0;
  font-size: inherit;
}

/* Section break */
.article-prose hr {
  border: 0;
  border-top: var(--rule-faint);
  margin: var(--space-48) 0;
}

/* Figures (img + caption) */
.article-prose figure { margin: var(--space-32) 0; }
/* Longhand border sides (not the `border` shorthand) so the Rounded/Border
   toggles below and the full-bleed override further down can each turn off
   one side without having to re-state the others. */
.article-prose figure img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--r-card);
  border-top: var(--rule-faint);
  border-bottom: var(--rule-faint);
  border-left: var(--rule-faint);
  border-right: var(--rule-faint);
}
/* Rounded/Border toggles — CMS floating panel, default both on (matches
   every figure that existed before this feature shipped: no attribute on
   the figure means "on" for both). */
.article-prose figure[data-rounded="0"] img { border-radius: 0; }
.article-prose figure[data-border="0"] img {
  border-top: none;
  border-bottom: none;
  border-left: none;
  border-right: none;
}
/* Bare <img> (no <figure> wrap) — caps width so an oversized upload doesn't
   blow past the column. TipTap can insert bare images via the toolbar
   Image button when the author doesn't add a caption. */
.article-prose > img,
.article-prose p > img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: var(--space-32) auto;
  border-radius: var(--r-card);
  border: var(--rule-faint);
}
.article-prose figcaption {
  font-family: var(--font-cond);
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: var(--space-12);
  text-align: center;
}
/* No caption authored — collapse fully instead of leaving a dead gap
   below the image (an empty figcaption still takes its margin-top even
   with no visible text). */
.article-prose figcaption:empty {
  display: none;
}

/* Figure size variants — author-toggleable per image (CMS sets data-size).
   Default: figure fills the article column (~640px content width).
   wide:    extends beyond the column, capped at 1080px or viewport.
   full:    viewport-width hero (NYT-style oversized image).
   The position:relative + left:50% + translateX(-50%) trick centres
   the figure in the viewport regardless of its constrained parent. */
.article-prose figure[data-size="wide"],
.article-prose figure[data-size="full"] {
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  max-width: none;
}
.article-prose figure[data-size="wide"] {
  width: min(1080px, calc(100vw - var(--space-32) * 2));
}
.article-prose figure[data-size="full"] {
  width: 100vw;
}
.article-prose figure[data-size="full"] img {
  border-radius: 0;
  border-left: 0;
  border-right: 0;
}
/* Caption stays at column-readable width even when the image is wide. */
.article-prose figure[data-size="wide"] figcaption,
.article-prose figure[data-size="full"] figcaption {
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
}

/* HTML Embed — raw markup pasted into the body (e.g. an exported SVG).
   Same size-variant mechanics as Figure above; see that comment for the
   centering trick. Author content fills the block at its natural size. */
.article-prose .html-embed { margin: var(--space-32) 0; }
.article-prose .html-embed[data-size="wide"],
.article-prose .html-embed[data-size="full"] {
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  max-width: none;
}
.article-prose .html-embed[data-size="wide"] {
  width: min(1080px, calc(100vw - var(--space-32) * 2));
}
.article-prose .html-embed[data-size="full"] {
  width: 100vw;
}

/* ── Figure size toggle (demo only) ──────────────────────────
   Authoring affordance to preview each size on this static page.
   In the CMS this control lives in the Tiptap floating menu —
   it does NOT render on the public article page. Styled as
   editor scaffolding (mono, dashed) so it reads as not-content. */
.figure-size-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  padding: 6px var(--space-12);
  margin-top: var(--space-32);
  border: 1px dashed var(--ink-30);
  border-radius: var(--r-card);
  background: var(--canvas-bg);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  color: var(--muted);
}
.figure-size-toggle-label {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 500;
}
.figure-size-toggle button {
  padding: 3px 8px;
  border: 1px solid var(--ink-18);
  border-radius: 3px;
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  color: var(--secondary);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.figure-size-toggle button:hover {
  border-color: var(--primary);
  color: var(--primary);
}
.figure-size-toggle button[aria-pressed="true"] {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--surface);
}
.article-prose .figure-size-toggle + figure { margin-top: var(--space-12); }

/* Series prev/next navigation — rendered only when template = series.
   Sits between the body end and the tag footer. Tinted by --c-current. */
.article-series-nav {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-16);
  margin-top: var(--space-40);
  padding-top: var(--space-32);
  border-top: var(--rule-faint);
}
.article-series-nav-link {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  padding: var(--space-16) var(--space-20);
  border: 1px solid color-mix(in srgb, var(--c-current, var(--primary)) 25%, transparent);
  border-radius: var(--r-card);
  text-decoration: none;
  color: var(--primary);
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.article-series-nav-link:hover {
  border-color: var(--c-current, var(--primary));
  background: color-mix(in srgb, var(--c-current, var(--primary)) 4%, transparent);
}
.article-series-nav-link.is-next {
  text-align: right;
  align-items: flex-end;
}
.article-series-nav-direction {
  font-family: var(--font-cond);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}
.article-series-nav-title {
  font-size: var(--text-md);
  font-weight: 600;
  line-height: 1.3;
  color: var(--primary);
}

.article-tags {
  margin-top: var(--space-40);
  padding-top: var(--space-24);
  border-top: var(--rule-faint);
}
.article-tags-label {
  font-family: var(--font-cond);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--space-12);
}
.article-tags-list { display: flex; gap: 6px; flex-wrap: wrap; }
.article-tag {
  font-family: var(--font-cond);
  font-size: var(--text-pill);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: var(--r-tag);
  border: 1px solid var(--ink-18);
  color: var(--muted);
  background: transparent;
}


/* ============================================================
   6. Editorial layout (Phase 6b)
   Promoted from site/_templates/article.html's inline <style>.
   Overrides earlier rules where necessary via cascade order.
   The article-standard PHP template renders blocks in this
   shape: split topstrip → title → summary → tag row → date row
   → byline-row-with-rules → hero → body → author bio → tags.
   ============================================================ */

/* Top split bar — category (left) · series row (right) */
.article-topstrip {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-24);
  margin-bottom: var(--space-40);
}
.article-topstrip .article-category {
  font-family: var(--font-cond);
  font-size: var(--text-md);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-current, var(--primary));
}
.article-topstrip-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* Title — bumped to 56px in the editorial layout */
.article-title.is-serif {
  font-size: 56px;
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin-bottom: var(--space-32);
}

/* Summary — restated so the new spacing token applies */
.article-summary {
  font-size: var(--text-body-lg);
  line-height: 1.5;
  color: var(--secondary);
  margin-bottom: var(--space-32);
}

/* Special Tag — own row */
.article-tag-row {
  margin-bottom: var(--space-32);
}

/* Date row — publish + updated, condensed uppercase */
.article-dates {
  display: flex;
  align-items: baseline;
  gap: var(--space-12);
  margin-bottom: var(--space-24);
  flex-wrap: wrap;
}
.article-pub-date {
  font-family: var(--font-cond);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--primary);
}
.article-updated-date {
  font-family: var(--font-cond);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Byline row with rules above and below — wraps the author block
   and the read-time slot. The .article-author overrides strip the
   legacy standalone border/padding so the row owns the framing. */
.article-byline-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-24);
  padding: var(--space-20) 0;
  border-top: var(--rule-faint);
  border-bottom: var(--rule-faint);
  margin-bottom: var(--space-40);
}
.article-byline-row .article-author {
  margin: 0;
  padding: 0;
  border: 0;
}
/* Stack name + tagline in the byline (inline "Name – Tagline" form
   only applies when .article-author is rendered standalone). */
.article-byline-row .article-author-info {
  display: block;
  line-height: 1.3;
}
.article-byline-row .article-author-name {
  display: block;
  font-weight: 700;
  font-size: var(--text-md);
}
.article-byline-row .article-author-separator { display: none; }
.article-byline-row .article-author-tagline {
  display: block;
  font-size: var(--text-base);
  color: var(--secondary);
  margin-top: 2px;
}
.article-read-time {
  font-family: var(--font);
  font-size: var(--text-base);
  color: var(--secondary);
  flex-shrink: 0;
}

/* Author Bio — dark footer block (replaces the legacy light card).
   Smaller, less prominent than the §5 default; reads as a sign-off. */
.article-author-bio {
  align-items: center;
  gap: var(--space-16);
  padding: var(--space-20) var(--space-24);
  background: var(--primary);
  border: 0;
  margin-top: var(--space-48);
}
.article-author-bio-avatar {
  width: 44px;
  height: 44px;
  font-size: var(--text-md);
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.15);
  color: rgba(255,255,255,0.85);
}
.article-author-bio-extended {
  font-size: var(--text-base);
  line-height: 1.6;
  color: rgba(255,255,255,0.72);
}


/* ── Responsive ─────────────────────────────────────────────
   Tablet (≤768px) and small mobile (≤480px) — keeps the
   editorial layout breathing on narrow screens.
   ──────────────────────────────────────────────────────── */

@media (max-width: 768px) {
  .article {
    padding: var(--space-32) var(--space-20) var(--space-48);
  }
  .article-breadcrumb {
    padding: var(--space-12) var(--space-20);
  }
  .article-topstrip {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-8);
    margin-bottom: var(--space-32);
  }
  .article-topstrip-right {
    max-width: 100%;
  }
  .article-title.is-serif {
    font-size: 40px;
    line-height: 1.1;
    margin-bottom: var(--space-24);
  }
  .article-summary {
    font-size: 17px;
    margin-bottom: var(--space-24);
  }
  .article-dates {
    margin-bottom: var(--space-20);
  }
  .article-byline-row {
    padding: var(--space-16) 0;
    gap: var(--space-12);
    flex-wrap: wrap;
  }
  .article-byline-row .article-author {
    gap: var(--space-12);
  }
  .article-hero[data-size="wide"] {
    width: 100%;
    position: static;
    left: auto;
    transform: none;
    max-width: 100%;
  }
  .article-hero[data-size="wide"] figcaption {
    max-width: 100%;
  }
  .article-prose h2 {
    font-size: 22px;
    margin-top: var(--space-32);
  }
  .article-prose h3 {
    font-size: 18px;
  }
  .article-prose blockquote {
    font-size: var(--text-h4);
    padding-left: var(--space-20);
    margin: var(--space-32) 0;
  }
  .article-series-nav {
    grid-template-columns: 1fr;
    gap: var(--space-12);
  }
  .article-series-nav-link.is-next {
    text-align: left;
    align-items: flex-start;
  }
  .article-author-bio {
    padding: var(--space-16) var(--space-20);
    gap: var(--space-12);
  }
  .article-author-bio-avatar {
    width: 40px;
    height: 40px;
    font-size: var(--text-base);
  }
  .article-author-bio-extended {
    font-size: 13px;
    line-height: 1.5;
  }
  .article-tags { margin-top: var(--space-32); }
}

@media (max-width: 480px) {
  .article {
    padding: var(--space-24) var(--space-16) var(--space-40);
  }
  .article-breadcrumb {
    padding: var(--space-12) var(--space-16);
  }
  .article-title.is-serif {
    font-size: 35px; /* X2: article title +1.1× on mobile (was 32) */
  }
  .article-summary {
    font-size: 16px;
  }
}

/* ─── Journal — Phase 8 ─────────────────────────────────────────────── */
/* Key Statement replaces the title block: Instrument Serif italic with
   a left rule in the category colour. Falls back to muted ink when no
   category is set (Phase 8 ships before Categories admin, so this is
   the common path until categories land). */
.article-key-statement {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 38px;
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--primary);
  padding-left: var(--space-24);
  border-left: 4px solid var(--c-current, var(--ink-30));
  margin: var(--space-16) 0 var(--space-32);
}

/* Entry Number — small monospace label in the topstrip right slot. */
.article-entry-number {
  font-family: var(--font-mono);
  font-size: var(--text-tiny);
  letter-spacing: 0.04em;
  color: var(--muted);
}

/* Journal byline row — only the author appears (no read-time slot), so
   the row collapses to a single-column layout. */
.article-byline-row--journal { grid-template-columns: 1fr; }
.article-byline-row--journal .article-author { justify-content: flex-start; }

@media (max-width: 768px) {
  .article-key-statement {
    font-size: 31px; /* B3: key statement +1.1× from tablet down (was 28) */
    padding-left: var(--space-16);
  }
}
@media (max-width: 480px) {
  .article-key-statement {
    font-size: 24px; /* B3: +1.1× (was 22) */
  }
}

/* ─── Live Session — Phase 9 ───────────────────────────────────────── */
/* Event Card combines Event Details + Format Tags into one white panel,
   per BLOCKS.md §7. Same surface treatment as Author Bio: a faint-bordered
   card sitting between the publish date and the byline row. */
/* Event card — combined When/Where + format tags in a single white card
   with the same surface treatment as the author bio block. Date and
   location render in Instrument Serif italic at 28px for editorial
   flair. Format tags fill the bottom row across both columns with a
   top rule separator. Design ported from site/_templates/layouts.html. */
.event-card {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-32);
  margin: var(--space-24) 0 var(--space-32);
  padding: var(--space-32);
  background: var(--surface);
  border: var(--rule-faint);
  border-radius: var(--r-card);
}
.event-meta-block-label {
  font-family: var(--font-cond);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--space-12);
}
.event-date,
.event-location {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 28px;
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--primary);
}
.event-time,
.event-cost-detail {
  font-family: var(--font);
  font-size: var(--text-md);
  color: var(--secondary);
  margin-top: var(--space-8);
}
.event-card-tags {
  grid-column: 1 / -1;
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  padding-top: var(--space-24);
  border-top: var(--rule-faint);
}
.event-format-tag {
  font-family: var(--font-cond);
  font-size: var(--text-pill);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  border: 1px solid var(--ink-18);
  color: var(--secondary);
  background: transparent;
}
.event-format-tag.is-free {
  color: var(--c-forest);
  border-color: rgba(36,102,54,0.4);
  background: rgba(36,102,54,0.08);
}
.event-format-tag.is-in-person {
  color: var(--c-clay);
  border-color: rgba(107,64,16,0.4);
  background: rgba(107,64,16,0.07);
}

/* PAST badge — small chip in the topstrip right slot when the event has
   passed. Mutes the live session without hiding it (per Decisions). */
.article-past-badge {
  display: inline-block;
  font-family: var(--font-cond);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--ink-18);
  border-radius: var(--r-pill);
  padding: 3px 10px;
}

@media (max-width: 480px) {
  /* Event card stacks (When over Where), tighter padding, smaller dates. */
  .event-card {
    grid-template-columns: 1fr;
    gap: var(--space-20);
    padding: var(--space-24);
  }
  .event-date,
  .event-location {
    font-size: 24px;
  }
}


/* ─── 12. Index pages — /writing/, /journal/, /live-sessions/,
        /experiments/, /series/[slug]/.

   Mirrors the "Full Page Index" treatment in the DS showcase
   (site/_design-system/index.html §04): left-aligned eyebrow + title
   with serif-italic emphasis, count chip on the right, header divider,
   3-col card grid below. Card chrome itself lives in views.css; this
   section owns only the page-level wrapper, header, and filter row.
   ───────────────────────────────────────────────────────────────── */

.index-page {
  display: block;
}

/* ── Page header (matches DS showcase inline rules) ── */
.index-page-header {
  padding: var(--space-56) var(--space-48) var(--space-40);
  border-bottom: 1px solid var(--ink-18);
}
.index-page-header-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-24);
  margin-bottom: var(--space-24);
}
.index-page-header-left {
  flex: 1;
  min-width: 0;
  text-align: left;
}
.index-eyebrow {
  font-family: var(--font-cond);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 10px;
}
.index-title {
  font-family: var(--font);
  font-size: var(--text-h2);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--primary);
  margin: 0;
}
/* Asterisk-wrapped words render as <em class="serif-em"> — Instrument
   Serif italic at the same size, weight 400 (lighter than the Barlow
   600 around it), so it reads as a graceful emphasis rather than a
   second voice. */
.index-title .serif-em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: 1em;
  display: inline-block;
}
.index-subtitle {
  font-family: var(--font);
  font-size: var(--text-body);
  font-weight: 500;
  line-height: 1.5;
  color: var(--secondary);
  margin: 12px 0 0;
  max-width: 56ch;
}
.index-count {
  font-family: var(--font);
  font-size: var(--text-base);
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
}

/* ── Card grid sections ── */
.index-grid,
.index-hero,
.index-featured {
  padding: var(--space-32) var(--space-48);
}
.index-grid + .index-grid,
.index-featured + .index-grid,
.index-hero + .index-featured,
.index-hero + .index-grid {
  padding-top: 0;
}

/* Hero card spans the full width and reads larger so it anchors the
   page. The inner grid passes grid-template-columns:1fr inline. */
.index-hero .card { min-height: 320px; }
.index-hero .card .title,
.index-hero .card .ev-title,
.index-hero .card .od-title { font-size: var(--text-h3); }

.index-empty {
  font-family: var(--font);
  font-size: var(--text-body);
  color: var(--muted);
  font-style: italic;
  padding: var(--space-48);
}

/* Cards are <a> elements — strip default link colour/underline so the
   internal .card-* rules keep their own colour decisions. */
.cards-grid a.card,
.cards-grid a.card:hover,
.cards-grid a.card:visited { color: inherit; text-decoration: none; }

/* Filter-pill hide. views.css forces .card { display:flex !important },
   so we need our own !important to override it when JS toggles a card
   out of the active filter set. */
.cards-grid .card.is-filtered-out { display: none !important; }

/* ── Card-grid motion (load + filter) ──────────────────────────────────
   Promoted from the card-load sandbox (2026-06). Driven by
   /_ds/js/public/card-grid.js on every public .cards-grid:
   • Load   — Stagger entrance (cards fade/translate up on first paint).
   • Filter — Re-enter (the grid fades out, the filtered set animates back in).
   --cg-dur is set per phase by the JS (320ms load / 260ms filter); the
   per-card stagger delay is applied inline. */
.cards-grid .card.cg-enter {
  opacity: 0;
  transform: translate3d(0, 12px, 0);
  will-change: transform, opacity;
}
.cards-grid .card.cg-enter-active {
  transition: opacity var(--cg-dur, 320ms) ease,
              transform var(--cg-dur, 320ms) cubic-bezier(0.22, 0.61, 0.36, 1);
  opacity: 1;
  transform: translate3d(0, 0, 0);
}
@media (prefers-reduced-motion: reduce) {
  .cards-grid .card.cg-enter,
  .cards-grid .card.cg-enter-active { transition: none !important; transform: none !important; opacity: 1; }
}

@media (max-width: 900px) {
  .index-page-header { padding: var(--space-40) var(--space-32) var(--space-32); }
  .index-grid,
  .index-hero,
  .index-featured { padding: var(--space-24) var(--space-32); }
}
@media (max-width: 600px) {
  .index-page-header { padding: var(--space-32) var(--space-24) var(--space-24); }
  .index-page-header-row { flex-direction: column; align-items: flex-start; }
  .index-grid,
  .index-hero,
  .index-featured { padding: var(--space-24); }
  .index-title { font-size: calc(var(--text-h4) * 1.1); } /* X2: index title +1.1× on mobile */
}

/* ── Editorial section stack (Phase 21.7) ──
   Each authored section renders as <section class="index-section
   index-section--{hero|curated|feed}">. The .cards-grid inside follows
   the same vocab as .index-grid (responsive 4/2/1 columns set by
   views.css), so we just need section-level padding + the optional
   header bar (title + see-more link). Hero variant matches .index-hero.
*/
.index-section { padding: var(--space-32) var(--space-48); }
.index-section + .index-section { padding-top: 0; }

/* Big section header — full serif title with italic-emphasis pair,
   matches the "Latest *thinking*" treatment in landing mocks. */
.index-section-header.is-big {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-16);
  padding-bottom: var(--space-20);
}
/* Big section title — mirrors /writing/'s .index-title treatment but
   one step smaller so the page-level title stays dominant. */
.index-section-title-big {
  font-family: var(--font);
  font-size: var(--text-h4);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin: 0;
  color: var(--primary);
}
.index-section-title-big .serif-em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: 1em;
  display: inline-block;
}

/* Bump the small (group-header) eyebrow + link up one step from the
   DS default — index sections want a bit more presence than the
   inline tag chips the eyebrow style was originally tuned for. */
.index-section .group-header-eyebrow,
.index-section .group-header-link { font-size: var(--text-meta); }
.index-section-view-all {
  font-family: var(--font-cond);
  font-size: var(--text-meta);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--primary);
  text-decoration: none;
  white-space: nowrap;
  transition: opacity 0.15s;
}
.index-section-view-all:hover { opacity: 0.5; }

/* Bare per-section visitor pills — no .controller white bar.
   Override the colored category-tinted hovers from status.css; inside
   editorial sections we want a single neutral hover treatment. */
.index-section-pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  padding-bottom: var(--space-20);
}
.index-section-pills .fp,
.index-section-pills .fp:hover {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--surface);
}
.index-section-pills .fp.on,
.index-section-pills .fp.on:hover {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--surface);
}

/* Hero section is a full-bleed editorial banner, not a card-grid. */
.index-section--hero {
  padding: 0;
  border-bottom: 1px solid var(--ink-18);
  background: transparent;
}
/* A within/plain hero draws its OWN top+bottom framing rules on the .editorial-
   hero element. Suppress the redundant SECTION-level border-bottom for those so
   the two don't stack into a thick double-line (the real source of the double).
   Solo/bleed heroes have no element border, so they keep the section rule as
   their divider. (0,5,0) to beat the base rule above. */
.index-section--hero:has(.editorial-hero:not(.is-solo):not(.editorial-hero--bleed-dark):not(.editorial-hero--bleed-light)) {
  border-bottom: 0;
}
/* Editorial heroes stack as flush blocks — no vertical section padding, so the
   hero's framing rules + internal padding ARE the block (hero↔hero is flush). */
.index-section:has(.editorial-hero) { padding-top: 0; padding-bottom: 0; }
/* Hero↔module transitions (either order) get a top gutter so the spacing around
   heroes matches the module-to-module rhythm. Hero→hero stays flush. */
.index-section:has(.editorial-hero) + .index-section:not(:has(.editorial-hero)),
.index-section:not(:has(.editorial-hero)) + .index-section:has(.editorial-hero) {
  padding-top: var(--space-48);
}
/* Faint divider between adjacent card-grid (module) sections — INSET to the
   content gutter (not full-bleed) so it lines up with the cards. */
.index-section:not(:has(.editorial-hero)) + .index-section:not(:has(.editorial-hero)) {
  position: relative;
  padding-top: var(--space-48);
}
.index-section:not(:has(.editorial-hero)) + .index-section:not(:has(.editorial-hero))::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--space-48);
  right: var(--space-48);
  border-top: var(--rule-faint);
}
@media (max-width: 900px) {
  .index-section:not(:has(.editorial-hero)) + .index-section:not(:has(.editorial-hero))::before {
    left: var(--space-32); right: var(--space-32);
  }
}
@media (max-width: 600px) {
  .index-section:not(:has(.editorial-hero)) + .index-section:not(:has(.editorial-hero))::before {
    left: var(--space-24); right: var(--space-24);
  }
}

.editorial-hero {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: var(--space-48);
  padding: var(--space-64) var(--space-48);
  align-items: stretch;
  /* Floor so every hero variant reads the same height even when padding is
     trimmed (first-child, or sitting above an Author block). Content is
     centered within the floor so short heroes don't sit top-heavy. */
  min-height: 360px;
}
.editorial-hero.is-solo,
.editorial-hero--plain {
  grid-template-columns: 1fr;
}

/* ── Background variants for plain + within ── */
.editorial-hero--bg-surface { background: var(--surface); }

/* ── Bleed variants — full-bleed bg image with gradient overlay ── */
.editorial-hero--bleed-dark,
.editorial-hero--bleed-light {
  position: relative;
  display: flex;
  align-items: flex-end;
  grid-template-columns: none;
  min-height: 480px;
  background-size: cover;
  background-position: center;
  overflow: hidden;
  gap: 0;
}
.editorial-hero--bleed-dark::before,
.editorial-hero--bleed-light::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.editorial-hero--bleed-dark::before {
  background: linear-gradient(
    180deg,
    rgba(0,0,0,0.15) 0%,
    rgba(0,0,0,0.6) 40%,
    rgba(0,0,0,0.8) 100%
  );
}
.editorial-hero--bleed-light::before {
  background: linear-gradient(
    180deg,
    rgba(255,255,255,0.10) 0%,
    rgba(255,255,255,0.65) 40%,
    rgba(255,255,255,0.95) 100%
  );
}
/* Optional bottom blur (CMS toggle → .editorial-hero--blur): a blur layer full
   at the bottom (behind the text), masked to fade to clear toward the top.
   Bleed layouts only — never on plain/within. */
.editorial-hero--bleed-dark.editorial-hero--blur::after,
.editorial-hero--bleed-light.editorial-hero--blur::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0; /* above bg image, below text (text is z-index:1) */
  pointer-events: none;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 35%, transparent 80%);
          mask-image: linear-gradient(to top, #000 0%, #000 35%, transparent 80%);
}
.editorial-hero--bleed-dark .editorial-hero-text,
.editorial-hero--bleed-light .editorial-hero-text {
  position: relative;
  z-index: 1;
}
.editorial-hero--bleed-dark .editorial-hero-text    { color: #fff; }
.editorial-hero--bleed-dark .editorial-hero-title   { color: #fff; }
.editorial-hero--bleed-dark .editorial-hero-summary { color: rgba(255,255,255,0.86); }
.editorial-hero--bleed-dark .editorial-hero-meta    { color: rgba(255,255,255,0.65); }
.editorial-hero--bleed-dark .editorial-hero-cta {
  background: #fff;
  color: #000;
}
.editorial-hero--bleed-light .editorial-hero-text   { color: var(--primary); }
.editorial-hero--bleed-light .editorial-hero-summary { color: var(--secondary); }
.editorial-hero--bleed-light .editorial-hero-meta    { color: var(--muted); }
.editorial-hero--bleed-light .editorial-hero-cta {
  background: var(--primary);
  color: var(--surface);
}
/* On the dark bleed hero the category colour needs to be lightened (mixed
   toward white) to read against the dark image. Same hue, token unchanged.
   (Light bleed is rare and reads fine, so no darken rule.) */
.editorial-hero--bleed-dark .editorial-hero-eyebrow {
  color: color-mix(in srgb, var(--c-current, var(--c-terracotta)), #fff 50%);
}
/* Eyebrow follows the post's category colour across every layout.
   The inline style="--c-current: var(--c-<name>)" set in the template overrides;
   fallback is terracotta. */
.editorial-hero-eyebrow { color: var(--c-current, var(--c-terracotta, var(--primary))); }

.editorial-hero-text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-24);
  min-width: 0;
  max-width: 760px;
}
.editorial-hero-eyebrow {
  font-family: var(--font-cond);
  font-size: var(--text-meta);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--c-current, var(--c-terracotta, var(--primary)));
}
.editorial-hero-title {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(34px, 3.8vw, 54px);
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--primary);
  margin: 0;
}
.editorial-hero-summary {
  font-family: var(--font);
  font-size: var(--text-h5, 19px);
  line-height: 1.55;
  color: var(--secondary);
  margin: 0;
}
.editorial-hero-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-24);
  margin-top: var(--space-16);
  flex-wrap: wrap;
}
.editorial-hero-meta {
  font-family: var(--font-cond);
  font-size: var(--text-meta);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}
.editorial-hero-cta {
  font-family: var(--font);
  font-size: var(--text-meta);
  font-weight: 600;
  color: var(--surface);
  background: var(--primary);
  padding: 10px var(--space-20);
  border-radius: 4px;
  text-decoration: none;
  transition: background 0.15s;
  white-space: nowrap;
}
.editorial-hero-cta:hover { background: var(--primary-hover, #000); }

.editorial-hero-side {
  display: flex;
  align-items: stretch;
  min-width: 0;
}
.editorial-hero-card {
  flex: 1;
  background: var(--surface);
  border: 1px solid var(--ink-12);
  border-radius: 4px;
  padding: var(--space-32);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 280px;
}
.editorial-hero-card--empty { background: var(--surface); }
.editorial-hero-card-label {
  font-family: var(--font-cond);
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--space-12);
}
.editorial-hero-card-title {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: var(--text-h4);
  line-height: 1.25;
  color: var(--primary);
}
.editorial-hero-card-part {
  display: inline;
  color: var(--secondary);
  font-style: italic;
}
.editorial-hero-thumb {
  flex: 1;
  background-size: cover;
  background-position: center;
  border-radius: 4px;
  min-height: 280px;
}

/* ── Author Info section — a compact author line ───────────────────────
   Reuses the post author-bio avatar (.article-author-bio-avatar). Photo on
   the left, the bio flows as one paragraph with an inline "read more" link
   at the end. The four backgrounds reuse existing tokens — Black flips the
   foreground to white. */
.index-author { display: flex; align-items: center; gap: var(--space-20); padding: 0; }
.index-author-avatar { width: 48px; height: 48px; font-size: 18px; }
.index-author-bio { font-size: var(--text-md); line-height: 1.6; color: var(--secondary); margin: 0; max-width: 120ch; }
.index-author-link { font-weight: 600; color: var(--primary); text-decoration: underline; text-underline-offset: 2px; white-space: nowrap; }
.index-author-link:hover { text-decoration-thickness: 2px; }
/* Filled backgrounds span the band full-bleed; content keeps the page gutter.
   Transparent rides the canvas with no box. */
.index-author--abg-shaded { background: rgba(0,0,0,0.05); padding: var(--space-24) var(--space-48); }
.index-author--abg-white  { background: var(--surface);   padding: var(--space-24) var(--space-48); }
.index-author--abg-black  { background: var(--primary);   padding: var(--space-24) var(--space-48); }
/* The section sits as a calm band with small equal vertical padding, framed by
   full-width rules above + below (like the hero's framing). */
.index-section--author-info {
  padding-top: var(--space-24);
  padding-bottom: var(--space-24);
  border-top: var(--rule-faint);
  border-bottom: var(--rule-faint);
}
/* Filled backgrounds: zero ALL section padding so the box fills edge-to-edge.
   The framing rules stay on the section → they sit at the band's top/bottom. */
.index-section--author-info:has(.index-author--abg-shaded),
.index-section--author-info:has(.index-author--abg-white),
.index-section--author-info:has(.index-author--abg-black) { padding: 0; }
/* Black needs no framing rules — the dark fill is its own frame. */
.index-section--author-info:has(.index-author--abg-black) { border-top: 0; border-bottom: 0; }
/* The Author's full-width bottom rule replaces the inset module divider the next
   module would otherwise draw — suppress the double line. (0,4,1) to beat it. */
.index-section.index-section--author-info + .index-section:not(:has(.editorial-hero))::before { display: none; }
/* The Author band stacks FLUSH with heroes on EITHER side (like hero↔hero).
   These must out-specify the generic hero↔module gutter rules (0,4,0) — hence
   the doubled .index-section.index-section--author-info. */
/* hero ABOVE → filled bands sit FLUSH; transparent gets a small breathing gap. */
.index-section:has(.editorial-hero) + .index-section.index-section--author-info { padding-top: var(--space-24); }
.index-section:has(.editorial-hero) + .index-section.index-section--author-info:has(.index-author--abg-shaded),
.index-section:has(.editorial-hero) + .index-section.index-section--author-info:has(.index-author--abg-white),
.index-section:has(.editorial-hero) + .index-section.index-section--author-info:has(.index-author--abg-black) { padding-top: 0; }
/* hero BELOW → kill the author's own bottom AND the next hero's top gutter */
.index-section.index-section--author-info:has(+ .index-section:has(.editorial-hero)) { padding-bottom: 0; }
.index-section.index-section--author-info + .index-section:has(.editorial-hero) { padding-top: 0; }
/* At a hero↔Author boundary the HERO keeps its framing rule; the AUTHOR drops
   its rule on the shared edge so the two don't stack into a thick double-line.
   (0,4,0) beats the base author border rule (0,2,0). */
.index-section:has(.editorial-hero) + .index-section.index-section--author-info { border-top: 0; }
.index-section.index-section--author-info:has(+ .index-section:has(.editorial-hero)) { border-bottom: 0; }
/* First section (a hero) shouldn't carry an oversized top gap. */
.index-section:first-child .editorial-hero { padding-top: var(--space-32); }
/* A hero directly above an Author block sits tight to it — trim the hero's
   bottom padding so the author band reads as attached, not floating below. The
   min-height floor (below) keeps the band the same height as untrimmed heroes. */
.index-section:has(.editorial-hero):has(+ .index-section--author-info) .editorial-hero { padding-bottom: var(--space-32); }
.index-author--abg-black .index-author-bio { color: rgba(255,255,255,0.84); }
.index-author--abg-black .index-author-link { color: #fff; }
.index-author--abg-black .index-author-avatar { background: rgba(255,255,255,0.08); border-color: rgba(255,255,255,0.18); color: rgba(255,255,255,0.72); }

@media (max-width: 900px) {
  .editorial-hero {
    grid-template-columns: 1fr;
    padding: var(--space-40) var(--space-32);
    gap: var(--space-32);
    min-height: 0; /* stacked content sets its own height */
  }
  /* Filled author band: inner inset tracks the page gutter (space-32 here). */
  .index-author--abg-shaded,
  .index-author--abg-white,
  .index-author--abg-black { padding: var(--space-24) var(--space-32); }
  /* Bleed-hero scrim: heavier, earlier-ramping haze on tablet/mobile so the
     eyebrow/title stay legible over busy images. */
  .editorial-hero--bleed-dark::before {
    background: linear-gradient(
      180deg,
      rgba(0,0,0,0.4) 0%,
      rgba(0,0,0,0.7) 30%,
      rgba(0,0,0,0.9) 100%
    );
  }
  .editorial-hero--bleed-light::before {
    background: linear-gradient(
      180deg,
      rgba(255,255,255,0.40) 0%,
      rgba(255,255,255,0.7) 30%,
      rgba(255,255,255,0.95) 100%
    );
  }
}
@media (max-width: 600px) {
  .editorial-hero { padding: var(--space-32) var(--space-24); }
  .index-author--abg-shaded,
  .index-author--abg-white,
  .index-author--abg-black { padding: var(--space-20) var(--space-24); }
}

/* Carousel: horizontal scroll, native scrollbar. v1 — no controls.
   Both sides bleed past the parent .index-section's horizontal
   padding (negative margins) so cards clip at both browser edges
   when scrolled. The matching padding-left keeps the FIRST card
   aligned with the section header in the initial scroll position;
   scroll-padding-left keeps snap alignment correct after scrolling. */
.cards-grid.is-carousel {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-24);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-20);
  margin-left: calc(-1 * var(--space-48));
  margin-right: calc(-1 * var(--space-48));
  padding-left: var(--space-48);
  padding-right: var(--space-48);
  scroll-padding-left: var(--space-48);
  scroll-padding-right: var(--space-48);
  /* Soft fade on both edges matching the negative margin. Bottoms
     out at 20% opacity (80% faded) rather than fully transparent so
     edge cards still register as content. */
  -webkit-mask-image: linear-gradient(to right, rgba(0,0,0,0.2) 0, #000 var(--space-48), #000 calc(100% - var(--space-48)), rgba(0,0,0,0.2) 100%);
          mask-image: linear-gradient(to right, rgba(0,0,0,0.2) 0, #000 var(--space-48), #000 calc(100% - var(--space-48)), rgba(0,0,0,0.2) 100%);
}
.cards-grid.is-carousel > .card {
  flex: 0 0 320px;
  scroll-snap-align: start;
}
/* Translucent-black scrollbar (~18% — the system's --ink-18), readable on the
   light track. WebKit = full control; Firefox uses scrollbar-color. */
.cards-grid.is-carousel {
  scrollbar-width: thin;
  scrollbar-color: var(--ink-18) transparent;
}
.cards-grid.is-carousel::-webkit-scrollbar { height: 10px; }
.cards-grid.is-carousel::-webkit-scrollbar-track { background: transparent; }
.cards-grid.is-carousel::-webkit-scrollbar-thumb {
  background: var(--ink-18);
  border-radius: 999px;
}
@media (max-width: 900px) {
  .cards-grid.is-carousel {
    margin-left:  calc(-1 * var(--space-32));
    margin-right: calc(-1 * var(--space-32));
    padding-left: var(--space-32);
    padding-right: var(--space-32);
    scroll-padding-left: var(--space-32);
    scroll-padding-right: var(--space-32);
    /* fade must match the gutter exactly (was stuck at the desktop space-48) */
    -webkit-mask-image: linear-gradient(to right, rgba(0,0,0,0.2) 0, #000 var(--space-32), #000 calc(100% - var(--space-32)), rgba(0,0,0,0.2) 100%);
            mask-image: linear-gradient(to right, rgba(0,0,0,0.2) 0, #000 var(--space-32), #000 calc(100% - var(--space-32)), rgba(0,0,0,0.2) 100%);
  }
}
@media (max-width: 600px) {
  .cards-grid.is-carousel {
    margin-left:  calc(-1 * var(--space-24));
    margin-right: calc(-1 * var(--space-24));
    padding-left: var(--space-24);
    padding-right: var(--space-24);
    scroll-padding-left: var(--space-24);
    scroll-padding-right: var(--space-24);
    -webkit-mask-image: linear-gradient(to right, rgba(0,0,0,0.2) 0, #000 var(--space-24), #000 calc(100% - var(--space-24)), rgba(0,0,0,0.2) 100%);
            mask-image: linear-gradient(to right, rgba(0,0,0,0.2) 0, #000 var(--space-24), #000 calc(100% - var(--space-24)), rgba(0,0,0,0.2) 100%);
  }
}

@media (max-width: 900px) {
  .index-section { padding: var(--space-24) var(--space-32); }
  /* X12: keep the bleed hero full-bleed below desktop — the generic
     .index-section padding above was re-introducing side gutters + the rule
     under it. Hero stays edge-to-edge with no underline. */
  .index-section--hero { padding: 0; border-bottom: 0; }
}
@media (max-width: 600px) {
  .index-section { padding: var(--space-24); }
  .index-section--hero { padding: 0; border-bottom: 0; }
  /* X13: keep the section title + "View all" on one row (View all anchored
     right); wrap only if it can't fit. (Was stacking into a column.) */
  .index-section-header { flex-wrap: wrap; gap: var(--space-8); }
}

/* C3: "within" hero (text + side image/card) — faint rules above + below at
   ALL widths to frame it as the hero bounds. Image stacks ABOVE the text only
   on mobile/tablet (≤900); side-by-side on desktop. */
.editorial-hero:not(.is-solo):not(.editorial-hero--bleed-dark):not(.editorial-hero--bleed-light) {
  border-top: var(--rule-faint);
  border-bottom: var(--rule-faint);
}
/* A hero leading the page (first section) drops its top line — nothing above to
   frame against. Scoped to :first-child + (0,4,0) so it beats the within-hero
   border rule above and doesn't strip the top line from mid-page heroes. */
.index-section:first-child.index-section--hero .editorial-hero { border-top: 0; }
@media (max-width: 900px) {
  .editorial-hero-side { order: -1; }
}

/* X8: Topic/filter bar (listing index) — align to the page gutter at each
   width; mobile hides the "Topic" label so the first pill sits at the gutter.
   Scoped to .index-page so the shared CMS .controller is untouched. */
@media (max-width: 900px) {
  .index-page .controller-row { padding-left: var(--space-32); padding-right: var(--space-32); }
}
@media (max-width: 600px) {
  .index-page .controller-row { padding-left: var(--space-24); padding-right: var(--space-24); }
  .index-page .ctrl-label { display: none; }
}

/* docs/MOTION.md §5 — html-body article fade-in.
   Initial opacity 0; .is-loaded toggles to 1 once DOM is ready. */
.article-prose[data-body-mode="html-body"] {
  opacity: 0;
  transition: opacity 420ms cubic-bezier(0.22, 1, 0.36, 1);
}
.article-prose[data-body-mode="html-body"].is-loaded {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .article-prose[data-body-mode="html-body"] {
    opacity: 1;
    transition: none;
  }
}
