/* Methodology — reading surface for arbor.linktr.ee/methodology/*
 *
 * F3 publication aesthetic: distinct from /changelog/ operator console
 * and /` install hero. Editorial Link Sans Display face on headlines and
 * eyebrows, condensed Link Sans Product on body prose. 800 extrabold is
 * intentionally absent on this surface, held in reserve for marketing
 * use elsewhere.
 *
 * Index page: H1-Emphatic (700 / 48 px / -0.02em) mirrors the changelog
 * page-heading scale — the two reading surfaces read as peers under
 * shared chrome. Italic lede (54ch cap) sits beneath the H1 and parallels
 * the article-page opening. The entry list adopts the changelog timeline
 * grid: 200px sticky dated rail + 1fr body with a continuous 1px vertical
 * line through the rail (Linear /now pattern). Per-entry footer reuses
 * the changelog's .change-meta posture (readTime · BookOpen · author).
 *
 * Article page: keeps the original opening rhythm — `METHODOLOGY` eyebrow
 * doubles as the back-link, then centered H1-Restrained (700 / 40 px)
 * → editorial centered byline → italic summary. No breadcrumb.
 *
 * The timeline CSS in this file mirrors apps/dashboard/src/styles.css's
 * .timeline-row / .timeline-stream / .change-meta rules. Keep both in
 * sync — the standalone methodology index renders structurally identical
 * to the changelog timeline's `filter=methodology` view.
 *
 * Italic-via-Inter pilot (decision doc: docs/design-system/research/
 * components/arbor-italic-typography.md). Link Sans has no authored
 * italic master, so the previous behavior was browser-synthesized
 * faux-italic on every italic-rendering selector. Inter ships authored
 * italic glyphs and is geometrically close to Link Sans; loading Inter
 * italic from Google Fonts and applying it to the four italic-rendering
 * selectors below — `.m-article-summary`, `.m-index-summary`,
 * `.m-prose em`, `.m-prose blockquote` — scopes the change to
 * methodology articles without affecting any npm consumer of
 * @linktr.ee/arbor. Each carrier rule pairs `font-family:
 * var(--font-emphasis)` with `font-synthesis-style: none` so the
 * cascade walks past Link Sans (which would otherwise be faked) to
 * Inter's real italic glyphs. Promotion to a global rule depends on
 * this pilot.
 */

@import url("https://fonts.googleapis.com/css2?family=Inter:ital,wght@1,300..900&display=swap");

@font-face {
  font-family: "Link Sans Product";
  src: url("/fonts/LinkSansProduct.woff2") format("woff2");
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Link Sans";
  src: url("/fonts/LinkSansDisplay.woff2") format("woff2");
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: rgb(243 243 241);
  --text: rgb(27 29 26);
  --text-muted: rgb(56 58 54);
  --text-tertiary: rgb(0 0 0 / 40%);
  --border: rgb(0 0 0 / 14.1%);
  --border-subtle: rgb(0 0 0 / 7.8%);
  --accent: #2e49ff;
  /* MIRROR: --chartreuse in apps/dashboard/src/styles.css:11. Used for
     the methodology rail marker on the index timeline so the dot color
     matches the .change-rail-marker.methodology variant in the changelog. */
  --chartreuse: #d2e823;
  --bg-hover: rgb(230 229 227);
  --bg-code: rgb(0 0 0 / 4%);
  --radius: 10px;
  --font-display:
    "Link Sans", "Link Sans Product", -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, sans-serif;
  --font-sans:
    "Link Sans Product", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    sans-serif;
  --font-mono:
    ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  /* Italic carrier: Inter has an authored italic; Link Sans does not.
     Used by the four italic-rendering selectors — .m-article-summary,
     .m-index-summary, .m-prose em, .m-prose blockquote (with a
     .m-prose blockquote p override below to defeat .m-prose p's
     font-family). The rest of the page stays on Link Sans for
     upright text. */
  --font-emphasis:
    "Inter", "Link Sans Product", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Roboto, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: rgb(24 24 26);
    --text: rgb(255 255 255 / 98%);
    --text-muted: rgb(255 255 255 / 70.2%);
    --text-tertiary: rgb(255 255 255 / 40%);
    --border: rgb(255 255 255 / 16.1%);
    --border-subtle: rgb(255 255 255 / 7.8%);
    --accent: #a5b4fc;
    --bg-hover: rgb(71 71 74);
    --bg-code: rgb(255 255 255 / 6%);
  }
}

html {
  color-scheme: light dark;
}

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

body {
  font-family: var(--font-sans);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  animation: m-page-fade-in 0.4s ease-out;
}
@keyframes m-page-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  body {
    animation: none;
  }
}

[id] {
  scroll-margin-top: 96px;
}

:focus:not(:focus-visible) {
  outline: none;
}
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ── Chrome ───────────────────────────────────────────────────────────── */

.m-chrome {
  border-bottom: 1px solid var(--border-subtle);
  width: 100%;
}
.m-chrome-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 64px;
  max-width: 1440px;
  margin: 0 auto;
  flex-wrap: wrap;
  /* Pinned line-height so the chrome height doesn't shift when navigating
     between methodology (body line-height 1.6) and changelog (body
     line-height 1.5). MIRRORS the inline lineHeight: 1.4 on
     apps/dashboard/src/App.tsx's dashboard-chrome-inner div. */
  line-height: 1.4;
}
.m-wordmark {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  letter-spacing: -0.005em;
}
/* "Design System" hides at medium widths — full brand reads "Arbor
   Design System" on desktop, just "Arbor" on tablet + phone so the
   peer nav doesn't get squeezed. MIRRORS .nav-brand-suffix (root) +
   .wordmark-suffix (dashboard). */
@media (max-width: 768px) {
  .m-wordmark-suffix {
    display: none;
  }
}
.m-nav {
  display: flex;
  align-items: center;
  gap: 24px;
}
.m-nav-link {
  font-size: 14px;
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
}
.m-nav-link:hover {
  color: var(--text);
  text-decoration: underline;
}
/* Canonical "you are here" treatment for the current peer-nav item.
   Mirrors NAV_LINK_ACTIVE_STYLE in apps/dashboard/src/App.tsx — keep
   the two in sync so the chrome reads identically across surfaces. */
.m-nav-link--active {
  color: var(--text);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1.5px;
}

/* ── Main layout ──────────────────────────────────────────────────────── */

.m-main {
  max-width: 1440px;
  margin: 0 auto;
  padding: 48px 64px 96px;
}

/* ── Opening rhythm (shared by index + article) ───────────────────────── */

.m-eyebrow {
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text);
  display: inline-block;
  margin-bottom: 16px;
  text-decoration: none;
}
a.m-eyebrow:hover {
  text-decoration: underline;
  text-underline-offset: 4px;
}
.m-eyebrow-static {
  /* No hover behavior; static label on the section root. */
  cursor: default;
}
/* Multi-part essay series eyebrow. Renders only when frontmatter
   declares `series` + `part`. On the article page the back-link
   eyebrow's TEXT extends to include the series breadcrumb ("Methodology
   · Design literacy · Part 1 of 3") — single line, no extra DOM node.
   On the index card a separate <span class="m-eyebrow m-eyebrow--series">
   sits above the entry title; tighter margin-bottom than the default
   .m-eyebrow rule because the entry title carries its own internal
   spacing. */
.m-eyebrow--series {
  display: block;
  margin-bottom: 8px;
}

/* ── Article header (centered editorial block) ───────────────────────── */

/* Centered shell containing eyebrow → title → optional hero → byline →
   italic lede. 1024px outer shell sits wider than the 720px prose body
   below; the H1's own 22ch cap keeps the title from spanning the full
   width, and the lede flips from centered to flush-left below 1152px
   viewport (see media query at end of file) so a constrained centered
   italic doesn't read scrunched. */
.m-article-header {
  margin: 0 auto 64px;
  max-width: 1024px;
  text-align: center;
}
.m-article-header .m-eyebrow {
  margin-bottom: 24px;
}
/* Shared byline row used in three places: article header (date · readTime ·
   author near the title), article footer (same row below the prose, before
   prev/next nav), and each index list entry. The base class keeps the
   uppercase small-caps treatment for the index. Modifiers below override
   for the new in-article placements (centered under the title, inline at
   the foot of the article). */
.m-article-meta {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin: 0 0 16px;
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.m-article-meta time {
  font-variant-numeric: tabular-nums;
}
.m-article-meta-divider {
  color: var(--text-tertiary);
}
/* Centered byline under the article title — drops uppercase tracking so
   the reading-view header feels editorial rather than tabular. */
.m-article-meta--centered {
  justify-content: center;
  text-transform: none;
  letter-spacing: 0;
  font-size: 14px;
  color: var(--text-muted);
  margin: 0 0 8px;
}
/* Inline byline at the foot of the article — same editorial treatment as
   the centered byline, but sits left-aligned in a row that also carries
   the Copy link button on the right. */
.m-article-meta--inline {
  text-transform: none;
  letter-spacing: 0;
  font-size: 14px;
  margin: 0;
}
.m-article-title {
  font-family: var(--font-display);
  font-size: 40px;
  line-height: 48px;
  font-weight: 700;
  letter-spacing: -0.028em;
  color: var(--text);
  text-wrap: balance;
  margin: 0 auto 24px;
  max-width: 22ch;
}
/* Optional hero (16:9). Renders only when frontmatter sets `hero`; the
   parser throws if `hero_alt` is missing alongside it. Author-provided
   asset, so dark/light readability is the author's responsibility — the
   spec lives in public/methodology/heroes/README.md. */
.m-article-hero {
  margin: 8px 0 32px;
  border-radius: 12px;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  background: var(--bg-code);
}
.m-article-hero img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Italic lede — the frontmatter `summary` rendered as the first beat of
   the essay. Centered under the title, no width cap; the H1's own 22ch
   constraint keeps the visual composition balanced above. */
.m-article-summary {
  font-family: var(--font-emphasis);
  font-size: 24px;
  line-height: 38px;
  font-weight: 300;
  font-style: italic;
  font-synthesis-style: none;
  color: var(--text);
  text-wrap: pretty;
  margin: 20px auto;
  text-align: center;
}

/* ── Article body (single centered column) ───────────────────────────── */

/* 720px outer shell — intentionally narrower than the 1024px article
   header above so the prose column reads at the researched editorial
   measure (5/5 sources agree on 50–75ch). The 64ch prose inside hits
   that range precisely; wider opt-in blocks (tables, code) stretch to
   the 720px shell while paragraphs stay narrow. */
.m-article-body {
  max-width: 720px;
  margin: 0 auto;
  min-width: 0;
}

/* ── Prose ────────────────────────────────────────────────────────────── */

.m-prose {
  max-width: 64ch;
  margin: 0 auto;
  color: var(--text);
}
.m-prose p {
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.6;
  margin-bottom: 20px;
}
.m-prose h2 {
  font-family: var(--font-display);
  font-size: 26px;
  line-height: 34px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  text-wrap: balance;
  margin-top: 64px;
  margin-bottom: 16px;
  position: relative;
}
.m-prose h3 {
  font-family: var(--font-display);
  font-size: 18px;
  line-height: 26px;
  font-weight: 600;
  letter-spacing: -0.011em;
  color: var(--text);
  text-wrap: balance;
  margin-top: 40px;
  margin-bottom: 12px;
  position: relative;
}
.m-prose ul,
.m-prose ol {
  margin: 0 0 20px 0;
  padding-left: 24px;
}
.m-prose li {
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.6;
  margin-bottom: 8px;
}
.m-prose li > p {
  margin-bottom: 8px;
}
.m-prose a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.m-prose code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: var(--bg-code);
  padding: 0.1em 0.35em;
  border-radius: 4px;
  color: var(--text);
}
.m-prose pre {
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.5;
  background: var(--bg-code);
  padding: 16px 20px;
  border-radius: var(--radius);
  overflow-x: auto;
  margin: 0 0 20px 0;
}
.m-prose pre code {
  background: transparent;
  padding: 0;
  border-radius: 0;
}
.m-prose blockquote {
  border-left: 3px solid var(--border);
  padding-left: 20px;
  margin: 0 0 20px 0;
  color: var(--text-muted);
  font-family: var(--font-emphasis);
  font-style: italic;
  font-synthesis-style: none;
}
/* markdown-it wraps blockquote text in `<p>`, and `.m-prose p` sets
   `font-family: var(--font-sans)` which would override the italic
   carrier set on the blockquote above. Re-inherit explicitly so the
   actual prose text inside the blockquote resolves to Inter italic
   too, not Link Sans (which has no italic master). */
.m-prose blockquote p {
  font-family: inherit;
}
.m-prose hr {
  border: none;
  border-top: 1px solid var(--border-subtle);
  margin: 48px 0;
}

/* Tables — editorial style. Markdown-it emits a <table> with <thead>
   and <tbody>, no extra wrapper. The prose column is capped at 64ch but
   wider tables break that cap intentionally (width: 100% on the table
   itself lets it fill the available column). For very wide tables that
   would still overflow the rail-constrained body, the overflow-x on
   .m-prose-table-wrap keeps horizontal scroll local to the table.
   markdown-it doesn't emit a wrap by default, so we rely on overflow on
   the prose container itself being acceptable here. */
.m-prose table {
  width: 100%;
  margin: 24px 0 28px;
  border-collapse: collapse;
  font-size: 15px;
  line-height: 1.55;
  font-variant-numeric: tabular-nums;
}
.m-prose thead {
  border-bottom: 1px solid var(--border);
}
.m-prose th {
  text-align: left;
  font-weight: 600;
  color: var(--text);
  padding: 10px 16px 10px 0;
  vertical-align: bottom;
}
.m-prose th:last-child {
  padding-right: 0;
}
.m-prose td {
  padding: 12px 16px 12px 0;
  vertical-align: top;
  color: var(--text);
  border-bottom: 1px solid var(--border-subtle);
}
.m-prose td:last-child {
  padding-right: 0;
}
.m-prose tbody tr:last-child td {
  border-bottom: none;
}
/* Inline code inside tables — tighter padding so it doesn't blow up
   row height, and break-anywhere so long identifiers don't push other
   columns into wrap. */
.m-prose td code,
.m-prose th code {
  font-size: 0.88em;
  padding: 0.08em 0.3em;
  word-break: break-word;
}
.m-prose strong {
  font-weight: 600;
  color: var(--text);
}
/* Pilot (italic-via-Inter): Link Sans has no authored italic master, so
   `<em>` previously rendered as a browser-synthesized skew of the upright
   glyphs. Inter's italic is geometrically close to Link Sans and ships an
   authored italic from Google Fonts. `font-synthesis-style: none` blocks
   any residual synthesis if Inter italic is still loading. */
.m-prose em {
  font-family: var(--font-emphasis);
  font-style: italic;
  font-synthesis-style: none;
}

/* Heading anchors (visible on hover, click to copy URL) */
.m-heading-anchor {
  display: inline-block;
  margin-left: 10px;
  color: var(--text-tertiary);
  text-decoration: none;
  font-weight: 400;
  opacity: 0;
  transition:
    opacity 0.15s ease,
    color 0.15s ease;
}
.m-prose h2:hover .m-heading-anchor,
.m-prose h3:hover .m-heading-anchor,
.m-prose h2:focus-within .m-heading-anchor,
.m-prose h3:focus-within .m-heading-anchor,
.m-heading-anchor:focus-visible {
  opacity: 1;
}
.m-heading-anchor:hover {
  color: var(--accent);
}
.m-heading-anchor[data-copied="true"]::after {
  content: "Copied";
  margin-left: 8px;
  font-size: 0.7em;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 500;
}

/* ── Floating TOC (button + popover) ─────────────────────────────────── */

/* Fixed bottom-right affordance so the centered editorial column stays
   uncluttered. Hidden until the reader scrolls past .m-article-header
   (IntersectionObserver in the inline JS sets data-visible="true"). The
   popover reuses the existing .m-toc-list markup + scroll-spy. */
.m-toc-floating {
  position: fixed;
  bottom: 24px;
  right: 24px;
  z-index: 10;
  opacity: 0;
  pointer-events: none;
  transition:
    opacity 0.18s ease,
    transform 0.18s ease;
  transform: translateY(8px);
}
.m-toc-floating[data-visible="true"] {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .m-toc-floating {
    transition: none;
    transform: none;
  }
}
.m-toc-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 22px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  cursor: pointer;
  box-shadow:
    0 2px 8px rgba(0, 0, 0, 0.06),
    0 1px 2px rgba(0, 0, 0, 0.04);
  transition:
    background 0.15s ease,
    border-color 0.15s ease;
}
.m-toc-toggle:hover {
  background: var(--bg-hover);
}
.m-toc-toggle svg {
  width: 18px;
  height: 18px;
  display: block;
}
.m-toc-popover {
  position: absolute;
  bottom: 56px;
  right: 0;
  width: 280px;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow:
    0 8px 24px rgba(0, 0, 0, 0.1),
    0 2px 4px rgba(0, 0, 0, 0.04);
  padding: 20px;
  font-family: var(--font-sans);
}
.m-toc-popover[hidden] {
  display: none;
}
.m-toc-eyebrow {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text);
  margin-bottom: 12px;
}
.m-toc-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.m-toc-list li {
  position: relative;
  padding-left: 0;
}
.m-toc-list li a {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
  display: block;
  padding-left: 12px;
  border-left: 2px solid transparent;
  transition:
    color 0.12s ease,
    border-color 0.12s ease;
}
.m-toc-list li a:hover {
  color: var(--accent);
}
/* h3 entries — lighter weight, muted color, indented + tree-line scaffold */
.m-toc-list li.m-toc-indent {
  margin-left: 12px;
  padding-left: 12px;
  border-left: 1px solid var(--border-subtle);
}
.m-toc-list li.m-toc-indent a {
  font-size: 13px;
  font-weight: 400;
  color: var(--text-muted);
  padding-left: 0;
  border-left: none;
}
.m-toc-list li.m-toc-indent a:hover {
  color: var(--text);
}
/* Active section indicator */
.m-toc-list li[data-active="true"] > a {
  color: var(--accent);
  border-left-color: var(--accent);
}
.m-toc-list li.m-toc-indent[data-active="true"] {
  border-left-color: var(--accent);
}
.m-toc-list li.m-toc-indent[data-active="true"] > a {
  color: var(--accent);
}
@media (max-width: 1023px) {
  .m-toc-floating {
    display: none;
  }
}

/* ── Article footer (byline + Copy link row) ────────────────────────── */

/* Linear-style closing row: re-credit the author on the left, give the
   reader a small "Copy link" affordance on the right. Replaces the
   previous repeated UPPERCASE meta row. Nests to 64ch like the prose
   column so it visually closes the reading column rather than spanning
   the wider 720px shell. */
.m-article-footer {
  margin: 64px auto 0;
  max-width: 64ch;
  padding: 24px 0 0;
  border-top: 1px solid var(--border-subtle);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.m-copy-link {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 500;
  color: var(--text-muted);
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-color: var(--border);
  transition:
    color 0.15s ease,
    text-decoration-color 0.15s ease;
}
.m-copy-link:hover {
  color: var(--text);
  text-decoration-color: var(--text);
}
.m-copy-link[data-copied="true"] {
  color: var(--accent);
  text-decoration-color: var(--accent);
}

/* ── Prev / Next nav (slim two-link row) ─────────────────────────────── */

/* Replaces the previous two-column card grid. Each link is plain text
   with an arrow glyph; no card chrome. Hairline top border continues
   the editorial rhythm from the footer above. */
.m-prev-next {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  margin: 32px auto 0;
  max-width: 64ch;
  padding-top: 24px;
  border-top: 1px solid var(--border-subtle);
}
.m-prev-next a {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-decoration: none;
  color: var(--text);
}
.m-prev-next a:hover .m-prev-next-title {
  text-decoration: underline;
  text-underline-offset: 4px;
}
.m-next {
  text-align: right;
}
.m-prev-next-label {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}
.m-prev-next-title {
  font-family: var(--font-display);
  font-size: 15px;
  line-height: 22px;
  font-weight: 600;
  color: var(--text);
}

@media (max-width: 640px) {
  .m-prev-next {
    grid-template-columns: 1fr;
  }
  .m-next {
    text-align: left;
  }
}

/* ── Index page ───────────────────────────────────────────────────────── */

.m-index-header {
  margin-bottom: 64px;
  max-width: 64ch;
}
/* MIRROR: page-heading inline style in apps/dashboard/src/views/Dashboard.tsx
   (3rem / 700 / 1.1 / -0.02em). The methodology index H1 reads as a peer to
   the changelog H1 across surfaces — keep the type scale in lockstep. */
.m-index-title {
  font-family: var(--font-display);
  font-size: 3rem;
  line-height: 1.1;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
  text-wrap: balance;
  margin: 0 0 20px;
}
/* Italic lede beneath the H1 — same editorial treatment as the per-article
   summary (.m-article-summary). Caps at 54ch so the lede reads as a tight
   opening beat before the timeline below, parallel to how each article
   page opens. */
.m-index-summary {
  font-family: var(--font-emphasis);
  font-size: 19px;
  line-height: 30px;
  font-weight: 400;
  font-style: italic;
  font-synthesis-style: none;
  color: var(--text);
  text-wrap: pretty;
  max-width: 54ch;
  margin: 0;
}
/* Empty-state fallback for the methodology index. Renders when
   loadMethodologyEssays() returns zero published articles (e.g. all
   essays are draft-skeleton). Defense-in-depth — the directory has
   essays today, but a silent empty <ul> would read as a broken page
   to a first-time visitor. Italic + muted + centered signals
   "no-content meta-text" rather than article body. */
.m-index-empty {
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 28px;
  font-weight: 400;
  font-style: italic;
  color: var(--text-muted);
  text-align: center;
  padding: 48px 0;
  margin: 0;
}

/* ── Index timeline ──────────────────────────────────────────────────────
   MIRROR: .timeline-stream / .timeline-row / .change-rail / .change-meta
   in apps/dashboard/src/styles.css. The static-HTML methodology index
   renders structurally identical to the changelog timeline's
   `filter=methodology` view — one row pattern, two surfaces. Keep these
   blocks in sync.

   The vertical rail line uses fixed top/bottom insets (44px) rather than
   the dashboard's JS-measured --timeline-line-top/-bottom CSS vars
   because this surface is server-rendered with stable row counts — no
   dynamic row injection to anchor around. */

.m-timeline-stream {
  list-style: none;
  padding: 0;
  margin: 0;
  position: relative;
}
.m-timeline-stream::before {
  content: "";
  position: absolute;
  /* Marker is 6px wide and sits at x=0..6 of the rail; center at x=3.
     translateX(-50%) on the 1px line keeps it exactly centered on the
     marker regardless of sub-pixel rounding. */
  left: 3px;
  transform: translateX(-50%);
  /* Inset 44px from the first/last row's top to land at the first and
     last marker centers (row padding-top 36px + marker center ≈ 8px). */
  top: 44px;
  bottom: 44px;
  width: 1px;
  background: var(--border-subtle);
  pointer-events: none;
}

.m-timeline-row {
  display: grid;
  /* 200px fits "September 28th, 2026" (the longest possible date string
     with ordinal day + year). */
  grid-template-columns: 200px 1fr;
  gap: 24px;
  padding: 36px 0;
}

.m-rail {
  font-size: 0.8125rem;
  line-height: 1.3;
  font-weight: 500;
  color: var(--text-muted);
  /* Same numeric stack as .change-rail: tabular-nums for column-aligned
     digits, lining-nums for uniform-height modern digit forms,
     slashed-zero to differentiate 0 from O at the small rail font size. */
  font-variant-numeric: tabular-nums lining-nums slashed-zero;
  font-feature-settings:
    "tnum" 1,
    "lnum" 1,
    "zero" 1;
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 4px;
  /* "May 13th, 2026" should never wrap — wrapping would push the marker
     off the vertical line. */
  white-space: nowrap;
  /* Sticky so the date stays anchored alongside the body as the user
     scrolls through a long entry. Chrome is not sticky on this surface,
     so 24px from the viewport top reads as a comfortable hover offset
     once the chrome scrolls off. */
  position: sticky;
  top: 24px;
  align-self: start;
}
.m-rail-marker {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 100%;
  /* MIRROR: .change-rail-marker.methodology — all entries on the
     standalone index are methodology entries, so the dot is uniformly
     chartreuse rather than per-row typed. */
  background: var(--chartreuse);
  flex-shrink: 0;
}
.m-rail-date {
  /* No separate styling — inherits .m-rail's typography. Wrapping the
     <time> in its own span keeps the marker/date pair sized
     independently if either ever needs to grow (e.g. a copy-link slot
     parallel to .change-rail-copy-slot in a future iteration). */
}

.m-row-body {
  width: 100%;
  max-width: 64ch;
  min-width: 0;
  margin-inline: auto;
}

/* MIRROR: .change-hero-title in apps/dashboard/src/styles.css:413-426.
   Link-styled h2 used as the entry title. Same display-face / 22px /
   700 / -0.01em / hover-underline posture. */
.m-entry-title {
  font-family: var(--font-display);
  font-size: 22px;
  line-height: 30px;
  font-weight: 700;
  color: var(--text);
  text-decoration: none;
  letter-spacing: -0.01em;
  display: inline-block;
  margin-bottom: 12px;
}
.m-entry-title:hover {
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* MIRROR: .change-body-preview in apps/dashboard/src/styles.css:561-571.
   Muted body preview with 64ch readable measure. */
.m-entry-preview {
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 24px;
  color: var(--text-muted);
  max-width: 64ch;
  margin: 0 0 8px;
  text-wrap: pretty;
}

/* MIRROR: .change-meta in apps/dashboard/src/styles.css:433-462.
   Footer metadata row with the .change-meta posture: border-top
   separator, 12px padding-top, 16px margin-top, eyebrow-scale type,
   muted color, tabular numerals. */
.m-entry-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  border-top: 1px solid var(--border-subtle);
  padding-top: 12px;
  margin-top: 16px;
  font-size: 13px;
  line-height: 18px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  flex-wrap: wrap;
}
.m-entry-meta-source {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 500;
}
.m-entry-meta-source svg {
  display: block;
}
.m-entry-meta-divider {
  color: var(--text-tertiary);
}

/* ── Responsive (small screens) ──────────────────────────────────────── */

/* Switch the article lede to flush-left at the viewport where the
   article header (max-width 1024px) has narrowed to match the article
   body (max-width 720px) — that's 720px + 128px (.m-main's 64px × 2
   side padding) = 848px. Above 848px the header still has room to
   read wider than the body and the centered italic reads as an
   editorial opening beat; at or below 848px header and body widths
   collapse together and the centering loses its purpose. */
@media (max-width: 848px) {
  .m-article-summary {
    text-align: left;
  }
}

@media (max-width: 640px) {
  .m-chrome-inner {
    padding: 16px 20px;
  }
  .m-main {
    padding: 32px 20px 64px;
  }
  .m-article-title,
  .m-index-title {
    font-size: 30px;
    line-height: 38px;
  }
  .m-prose h2 {
    font-size: 22px;
    line-height: 30px;
  }
  .m-prose h3 {
    font-size: 16px;
    line-height: 24px;
  }
  .m-article-header {
    margin-bottom: 40px;
  }
  .m-index-header {
    margin-bottom: 32px;
  }
  .m-article-summary {
    font-size: 20px;
    line-height: 30px;
  }
  .m-article-meta--centered,
  .m-article-meta--inline {
    font-size: 13px;
  }
  .m-article-hero {
    border-radius: 8px;
  }
  /* Timeline reflow — single column on narrow viewports. Hide the
     vertical line (no rail column to anchor it to) and let the rail
     date sit inline above the row body. MIRROR: the dashboard's
     equivalent reflow at apps/dashboard/src/styles.css:854-858. */
  .m-timeline-stream::before {
    display: none;
  }
  .m-timeline-row {
    grid-template-columns: 1fr;
    gap: 8px;
    padding: 14px 0;
  }
  .m-rail {
    position: static;
    padding-top: 0;
  }
  .m-entry-title {
    font-size: 18px;
    line-height: 24px;
    margin-bottom: 8px;
  }
}

/* Shared site footer — copyright + Privacy + Terms. Renders on the
 * methodology index and every article page. MIRROR: the .site-footer
 * block in apps/registry/scripts/generate-manifest.js's inline <style>
 * (registry index) and the .site-footer block in
 * apps/dashboard/src/styles.css (changelog dashboard). All three CSS
 * homes target the same visual outcome; keep them in sync. */
.site-footer {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 1.5rem 1rem;
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.4;
  color: var(--text-tertiary);
  background: transparent;
  border-top: 1px solid var(--border-subtle);
}
.site-footer a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.site-footer a:hover {
  color: var(--text);
}
