@import url('https://fonts.googleapis.com/css2?family=Anton&family=Fraunces:opsz,wght@9..144,300;9..144,400&family=Inter:wght@400;500;700&display=swap');

/* ==========================================================================
   ALerts — "Switch-Lit" design system
   Editorial-brutalist, inked hairlines on Manuscript Cream paper. Light only.
   ========================================================================== */

:root {
  /* Colors */
  --color-manuscript-cream: #f9f9f7;
  --color-press-black: #000000;
  --color-sage-border: #d2ddd2;
  --color-mist-green: #dee5dd;
  --color-paper-shadow: #edf0e9;
  --color-smoke: #c7c7c6;
  --color-highlighter-lime: #edfe5e;
  --color-cornflower-wash: #bed4fb;
  --color-spring-green: #31e992;

  /* Typography — semantic font families (substitutes for the proprietary faces).
     Anton    → ABCCameraHeavy (heavy condensed display; reserved, not used here)
     Fraunces → ABCArizonaFlare (light flared serif headings)
     Inter    → ABCArizonaSans / MixVariable (grotesque body & UI) */
  --font-display: 'Anton', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-serif: 'Fraunces', Georgia, 'Times New Roman', serif;
  --font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  /* Typography — Scale */
  --text-caption: 11px;
  --leading-caption: 1;
  --tracking-caption: 0.363px;
  --text-body: 15px;
  --leading-body: 1.18;
  --tracking-body: 0.6px;
  --text-body-lg: 18px;
  --leading-body-lg: 1.27;
  --tracking-body-lg: 1.206px;
  --text-subheading: 20px;
  --leading-subheading: 1.15;
  --tracking-subheading: 0.5px;
  --text-heading-sm: 28px;
  --leading-heading-sm: 1.07;
  --tracking-heading-sm: -0.056px;
  --text-heading: 57px;
  --leading-heading: 1.04;
  --tracking-heading: 0.114px;
  --text-display: 68px;
  --leading-display: 0.74;
  --tracking-display: -0.068px;

  /* Typography — Weights */
  --font-weight-light: 300;
  --font-weight-regular: 400;
  --font-weight-medium: 500;
  --font-weight-bold: 700;

  /* Spacing */
  --spacing-5: 5px;
  --spacing-6: 6px;
  --spacing-8: 8px;
  --spacing-10: 10px;
  --spacing-12: 12px;
  --spacing-13: 13px;
  --spacing-15: 15px;
  --spacing-16: 16px;
  --spacing-19: 19px;
  --spacing-20: 20px;
  --spacing-29: 29px;
  --spacing-30: 30px;
  --spacing-40: 40px;
  --spacing-60: 60px;
  --spacing-160: 160px;
  --spacing-170: 170px;

  /* Layout */
  --page-max-width: 1200px;
  --section-gap: 80px;
  --card-padding: 20px;
  --element-gap: 20px;

  /* Border Radius */
  --radius-sm: 3px;
  --radius-md: 6px;
  --radius-xl: 12px;
  --radius-3xl: 24px;
  --radius-cards: 12px;
  --radius-pills: 24px;
  --radius-small: 3px;
  --radius-buttons: 6px;
  --radius-default: 6px;

  /* Shadows — the single allowed shadow, buttons only */
  --shadow-sm: rgba(0, 0, 0, 0.2) 0px 2px 6px 0px;

  /* Surfaces (typos from DESIGN.md fixed: #edfe5 → #edfe5e) */
  --surface-page-canvas: #f9f9f7;
  --surface-card-surface: #f9f9f7;
  --surface-accent-card-surface: #edfe5e;
  --surface-cool-accent-surface: #bed4fb;
}

/* --------------------------------------------------------------------------
   Base / reset — cream canvas, Press Black ink, Inter body
   -------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-body);
  font-weight: var(--font-weight-regular);
  line-height: 1.27;
  letter-spacing: 0.04em;
  text-align: left;
  color: var(--color-press-black);
  background: var(--surface-page-canvas);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img {
  max-width: 100%;
  height: auto;
}

a {
  color: var(--color-press-black);
}

::selection {
  background: var(--color-highlighter-lime);
  color: var(--color-press-black);
}

:focus-visible {
  outline: 2px solid #000;
  outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   Shared components (used by article.html)
   -------------------------------------------------------------------------- */

/* Masthead ribbon — one component, identical on every page and on both of the
   front page's section fronts. Inked band, wordmark centred, and a single nav
   slot on the right holding Archive. The three-column grid (1fr auto 1fr) is
   what keeps the wordmark optically centred no matter how wide the right-hand
   link runs; a flex row with space-between would push it off-centre. */
.masthead {
  border-bottom: 1px solid var(--color-press-black);
  background: var(--color-press-black);
}

/* Dynamic masthead: sticks to the top and retracts on scroll-down, returning on
   scroll-up (see initDynamicHeader). Applied per-page/per-panel from script so
   the front-page gate's static masthead is left alone. */
.masthead--dynamic {
  position: sticky;
  top: 0;
  z-index: 40;
  transition: transform 0.34s cubic-bezier(0.4, 0, 0.2, 1);
  will-change: transform;
}

.nav-hidden .masthead--dynamic {
  transform: translateY(-100%);
}

.masthead__inner {
  max-width: var(--page-max-width);
  margin: 0 auto;
  padding: var(--spacing-12) var(--spacing-30);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-16);
}

/* The wordmark leads at the top-left; the balancing spacer is no longer needed. */
.masthead__spacer {
  display: none;
}

/* The wordmark is a logo: it must never break across lines. Below ~650px the
   full 52px would crowd the nav and wrap, so it scales down to a 34px floor. */
.wordmark {
  font-family: var(--font-display);
  font-size: clamp(34px, 8vw, 52px);
  line-height: 1;
  letter-spacing: -0.001em;
  color: var(--color-press-black);
  text-decoration: none;
  white-space: nowrap;
}

/* On the inked ribbon the lettering reverses out to cream; the lime foot block
   keeps its colour. It retains its press-black hairline — against black the
   outline reads only where it meets the cream stem, which is exactly the edge
   that needs separating so the lime square no longer merges into the letter. */
.masthead .wordmark {
  color: var(--color-manuscript-cream);
}

.masthead .wordmark__foot {
  border-color: var(--color-press-black);
}

.wordmark__foot {
  display: inline-block;
  width: 0.19em;
  height: 0.145em;
  background: var(--color-highlighter-lime);
  border: 1px solid var(--color-press-black);
  box-sizing: border-box;
  margin-left: -0.03em;
}

/* Compact page-title bar — built by initDynamicHeader and pinned to the top-left
   once the reader scrolls past the full header. It slides down + fades in as the
   masthead retracts, and tucks behind the masthead again when it returns (lower
   z-index), so the two never show at once. Cream band by default; the AI Guide
   section front overrides to its own paper tint. */
.pagehead {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 35;
  background: var(--surface-page-canvas);
  border-bottom: 1px solid var(--color-press-black);
  transform: translateY(-100%);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.34s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.22s ease;
}

.head-collapsed .pagehead {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}

.pagehead__inner {
  max-width: var(--page-max-width);
  margin: 0 auto;
  padding: var(--spacing-10) var(--spacing-30);
  display: flex;
  align-items: baseline;
  gap: var(--spacing-16);
}

.pagehead__title {
  flex: 0 1 auto;
  min-width: 0;
  font-family: var(--font-display);
  font-size: clamp(18px, 3.4vw, 24px);
  line-height: 1;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--color-press-black);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pagehead__sub {
  min-width: 0;
  font-family: var(--font-serif);
  font-weight: var(--font-weight-light);
  font-size: 13px;
  line-height: 1.2;
  letter-spacing: 0.01em;
  color: #333333;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (max-width: 640px) {
  .pagehead__inner {
    padding: var(--spacing-8) var(--spacing-16);
  }

  /* The subtitle is the first thing to go when the bar gets tight. */
  .pagehead__sub {
    display: none;
  }
}

/* Ghost text link */
.ghost-link {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.04em;
  color: var(--color-press-black);
  text-decoration: none;
}

.ghost-link:hover {
  text-decoration: underline;
}

/* An item's tag row: the "Latest" freshness badge, the subject chip, or both.
   Rendered by renderTags() in app.js and never printed empty, so the surface's
   own spacing class rides on the same element. */
.tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-8);
}

/* Topic / category chip — the subject tag */
.chip {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.067em;
  text-transform: uppercase;
  color: var(--color-press-black);
  background: var(--color-highlighter-lime);
  border: 1px solid var(--color-press-black);
  border-radius: var(--radius-buttons);
  padding: 6px 10px;
}

/* Freshness badge. Inked fill so it reads as a different axis from the lime
   subject chip it sits beside — the same separation .tool-chip makes with
   cornflower. Set from addedDate at render time, never from the data. */
.chip--latest {
  background: var(--color-press-black);
  color: var(--color-manuscript-cream);
}

/* Accent filled button — the one place --shadow-sm is allowed */
.btn-accent {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.04em;
  color: var(--color-press-black);
  background: var(--color-highlighter-lime);
  border: 1px solid var(--color-press-black);
  border-radius: var(--radius-buttons);
  padding: 12px 20px;
  min-height: 44px;
  text-decoration: none;
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.12s ease, transform 0.12s ease;
}

.btn-accent:hover {
  box-shadow: var(--shadow-sm);
  transform: translateY(-1px);
}

.btn-accent:active {
  transform: translateY(0);
  box-shadow: var(--shadow-sm);
}

/* Bordered card */
.card {
  background: var(--surface-card-surface);
  border: 1px solid var(--color-press-black);
  border-radius: var(--radius-cards);
  padding: var(--card-padding);
}

/* Caption / dateline text */
.caption {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.067em;
  text-transform: uppercase;
  color: var(--color-press-black);
}

/* --------------------------------------------------------------------------
   article.html — the "press clipping" landing
   -------------------------------------------------------------------------- */

.reading {
  max-width: 720px;
  margin: 0 auto;
  padding: var(--spacing-60) var(--spacing-30) var(--spacing-40);
}

.clipping > * {
  margin: 0;
}

.clipping__chip {
  margin-bottom: var(--spacing-20);
}

.headline {
  font-family: var(--font-serif);
  font-weight: var(--font-weight-light);
  font-size: clamp(32px, 6vw, 57px);
  line-height: 1.04;
  letter-spacing: 0.002em;
  color: var(--color-press-black);
  margin-bottom: var(--spacing-16);
}

.dateline {
  margin-bottom: var(--spacing-30);
}

/* Summary as pulled excerpt — hairline rules on the reading column only */
.excerpt {
  border-top: 1px solid var(--color-press-black);
  border-bottom: 1px solid var(--color-press-black);
  padding: var(--spacing-20) 0;
  margin-bottom: var(--spacing-30);
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: var(--font-weight-regular);
  line-height: 1.27;
  letter-spacing: 0.067em;
  max-width: 65ch;
  text-align: left;
}

/* Upvote control. Sits above the link-out so the reader meets it while still on
   the page. Script-mounted and empty until the count loads, so it must not
   reserve space of its own — the margin rides on the button instead. */
.clipping__upvote:empty {
  display: none;
}

/* Bordered pill, not a filled one: the lime .btn-accent below it is the page's
   primary action and this must not compete with it. Once pressed it inverts to
   solid ink, the same "committed" state .role-chip.is-selected uses. */
.upvote {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-8);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.04em;
  color: var(--color-press-black);
  background: transparent;
  border: 1px solid var(--color-press-black);
  border-radius: var(--radius-buttons);
  padding: 10px 16px;
  min-height: 44px;
  margin-bottom: var(--spacing-8);
  cursor: pointer;
}

.upvote:hover:not(:disabled) {
  background: var(--color-highlighter-lime);
}

.upvote__mark {
  font-size: 9px;
  line-height: 1;
}

/* The count is the payload — set it apart from the label with a hairline. */
.upvote__count {
  padding-left: var(--spacing-8);
  border-left: 1px solid var(--color-smoke);
  font-variant-numeric: tabular-nums;
  font-weight: var(--font-weight-bold);
}

.upvote[aria-pressed='true'] {
  background: var(--color-press-black);
  color: var(--color-manuscript-cream);
}

.upvote[aria-pressed='true'] .upvote__count {
  border-left-color: var(--color-manuscript-cream);
}

/* Already voted — the control stays legible, it just isn't a target any more. */
.upvote:disabled {
  cursor: default;
}

.upvote__note {
  margin-bottom: var(--spacing-16);
  color: var(--color-press-black);
  opacity: 0.6;
}

.clipping__action {
  margin-bottom: var(--spacing-16);
}

.clipping__secondary {
  margin-bottom: var(--spacing-40);
}

/* Quiet footer note */
.clipping__footnote {
  border-top: 1px solid var(--color-press-black);
  padding-top: var(--spacing-16);
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: var(--font-weight-regular);
  letter-spacing: 0.04em;
  color: var(--color-press-black);
}

/* Designed states */
.state-loading {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.067em;
  text-transform: uppercase;
  color: var(--color-press-black);
}

.state-card__title {
  font-family: var(--font-serif);
  font-weight: var(--font-weight-regular);
  font-size: 28px;
  line-height: 1.07;
  letter-spacing: -0.002em;
  color: var(--color-press-black);
  margin: 0 0 var(--spacing-12);
}

.state-card__body {
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.27;
  letter-spacing: 0.04em;
  margin: 0 0 var(--spacing-16);
}

/* --------------------------------------------------------------------------
   Mobile
   -------------------------------------------------------------------------- */

@media (max-width: 640px) {
  .masthead__inner {
    padding: var(--spacing-12) var(--spacing-16);
  }

  .reading {
    padding: var(--spacing-30) var(--spacing-16) var(--spacing-30);
  }

  .btn-accent {
    display: block;
    width: 100%;
    text-align: center;
  }
}

/* --------------------------------------------------------------------------
   index.html — the front page + the shared editorial list row (.row)
   The .row component is emitted by renderListItem() and is therefore also
   inherited by archive.html; it is the settled list-item pattern.
   -------------------------------------------------------------------------- */

/* Content column: wide per DESIGN.md; reading comfort comes from the row's
   own ~70ch measure, not from a narrow outer column. */
.frontpage {
  max-width: var(--page-max-width);
  margin: 0 auto;
  padding: var(--spacing-60) var(--spacing-30) var(--spacing-40);
}

/* Hero — the one place the Anton display face fires in the whole product.
   Anton has taller ascenders than the proprietary ABCCameraHeavy, so the
   0.74 display leading would collide; 0.9 is the tightest that stays clean. */
.hero {
  margin-bottom: var(--spacing-40);
}

.hero__display {
  margin: 0 0 var(--spacing-16);
  font-family: var(--font-display);
  font-weight: var(--font-weight-regular);
  font-size: clamp(64px, 15vw, 150px);
  line-height: 0.9;
  letter-spacing: -0.001em;
  text-transform: uppercase;
  color: var(--color-press-black);
}

.hero__display .wordmark__foot {
  border-width: 2px;
}

.hero__dateline {
  margin: 0;
}

/* Contents list — a single hairline separates the dateline from the first row;
   rows carry only bottom hairlines, so no rule is doubled. */
.contents {
  border-top: 1px solid var(--color-press-black);
}

/* Ruled editorial row (NOT a bordered card). */
.row {
  position: relative;
  padding: var(--spacing-20) 0;
  border-bottom: 1px solid var(--color-press-black);
}

/* Last row hands the closing rule to whatever follows (footer / page end),
   so the list never shows a doubled hairline. */
.row:last-child {
  border-bottom: none;
}

.row__chip {
  margin: 0;
}

.row__headline {
  margin: var(--spacing-8) 0;
  max-width: 70ch;
  font-family: var(--font-serif);
  font-weight: var(--font-weight-regular);
  font-size: 28px;
  line-height: 1.07;
  letter-spacing: 0;
  color: var(--color-press-black);
}

.row__headline a {
  color: var(--color-press-black);
  text-decoration: none;
}

/* Stretched link: the single real anchor covers the whole row. */
.row__headline a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.row__meta {
  margin: 0 0 var(--spacing-10);
}

.row__summary {
  margin: 0;
  max-width: 70ch;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.27;
  letter-spacing: 0.04em;
  color: var(--color-press-black);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Hover: flat fill (allowed) + headline underline. */
.row:hover {
  background: var(--color-paper-shadow);
}

.row:hover .row__headline a {
  text-decoration: underline;
}

/* Keyboard focus draws the outline around the whole row; the inner anchor's
   own outline is suppressed so the two don't stack. */
.row:has(.row__headline a:focus-visible) {
  outline: 2px solid var(--color-press-black);
  outline-offset: -2px;
}

.row__headline a:focus-visible {
  outline: none;
}

/* Footer band — hairline top, pill button + a quiet note. */
.frontpage__footer {
  margin-top: var(--spacing-40);
  padding-top: var(--spacing-30);
  border-top: 1px solid var(--color-press-black);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-20);
}

.frontpage__note {
  margin: 0;
  max-width: 52ch;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: var(--font-weight-regular);
  letter-spacing: 0.04em;
  color: var(--color-press-black);
}

/* Pill button — fully rounded, transparent fill, inked outline. */
.pill {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.04em;
  color: var(--color-press-black);
  background: transparent;
  border: 1px solid var(--color-press-black);
  border-radius: var(--radius-pills);
  padding: 12px var(--spacing-29);
  min-height: 44px;
  text-decoration: none;
  transition: background 0.12s ease;
}

.pill:hover {
  background: var(--color-paper-shadow);
}

/* Front-page mobile tightening (hero clamp handles the type itself). */
@media (max-width: 640px) {
  .frontpage {
    padding: var(--spacing-40) var(--spacing-16) var(--spacing-30);
  }

  .row__headline {
    font-size: 22px;
  }
}

/* --------------------------------------------------------------------------
   archive.html — month-grouped list. Reuses .hero / .hero__display, .caption,
   .contents, and .row from the front-page section above; these two rules are
   the only archive-specific additions.
   -------------------------------------------------------------------------- */

.month-header {
  margin: var(--spacing-40) 0 var(--spacing-12);
}

.month-header:first-of-type {
  margin-top: 0;
}

/* --------------------------------------------------------------------------
   AI Guide — playbook.html (list) + usecase.html (detail).
   Reuses the masthead, .hero, .contents/.row list, .clipping detail column,
   .chip, .caption, and .btn-accent from above. These rules are the only
   AI Guide-specific additions: a right-aligned nav group, the standing
   disclaimer banner, tool chips, and the detail field blocks.
   -------------------------------------------------------------------------- */

/* Right-hand nav slot. Holds Archive alone — the wordmark is the way home and
   the section fronts are reached from the front page, so nothing else belongs
   in the top bar. Pushed to the trailing edge of its grid column. */
.masthead__nav {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--spacing-20);
  min-width: 0;
}

/* Nav links reverse out of the inked ribbon like the wordmark does. */
.masthead__nav .ghost-link {
  color: var(--color-manuscript-cream);
}

.masthead__nav .ghost-link:focus-visible {
  outline-color: var(--color-manuscript-cream);
}

/* Active nav link carries the 1px underline (DESIGN.md top-nav rule). */
.masthead__nav .ghost-link[aria-current="page"] {
  text-decoration: underline;
}

/* Standing disclaimer — a newspaper public-notice box. Stays inside the
   Switch-Lit system (inked border, press-black text) but the signal-yellow
   accent (the wordmark foot lime) fires as a heavy left stripe + a solid
   kicker block, so the notice is impossible to scroll past unnoticed. */
.disclaimer {
  border: 2px solid var(--color-press-black);
  border-left: 8px solid var(--color-highlighter-lime);
  border-radius: var(--radius-cards);
  padding: var(--spacing-16) var(--spacing-20);
  margin-bottom: var(--spacing-30);
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: var(--font-weight-regular);
  line-height: 1.4;
  letter-spacing: 0.02em;
  color: var(--color-press-black);
  max-width: 70ch;
}

.disclaimer strong {
  font-weight: var(--font-weight-bold);
}

/* Solid lime kicker block — the "READ THIS FIRST" public-notice label. */
.disclaimer__kicker {
  display: inline-block;
  margin: 0 0 var(--spacing-12);
  padding: 3px var(--spacing-8);
  background: var(--color-highlighter-lime);
  border: 1px solid var(--color-press-black);
  font-family: var(--font-sans);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-bold);
  line-height: 1;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-press-black);
}

/* --- Collapsible disclaimer (initDisclaimer) ------------------------------ */
/* Once wired, the lime kicker is the toggle: it gains a pointer + a +/- marker,
   and the body collapses to nothing so only the "Read this first" card remains. */
.disclaimer[data-collapsible="on"] .disclaimer__kicker {
  cursor: pointer;
  user-select: none;
}

.disclaimer[data-collapsible="on"] .disclaimer__kicker::after {
  content: "\2013"; /* en dash — expanded */
  margin-left: var(--spacing-8);
  font-weight: var(--font-weight-bold);
}

.disclaimer.is-collapsed .disclaimer__kicker {
  margin-bottom: 0;
}

.disclaimer.is-collapsed .disclaimer__kicker::after {
  content: "+"; /* collapsed — click to expand */
}

.disclaimer[data-collapsible="on"] .disclaimer__kicker:focus-visible {
  outline: 2px solid var(--color-press-black);
  outline-offset: 2px;
}

/* The wrapped body animates open/closed. max-height is a generous ceiling; the
   real notice is far shorter, so the ease reads cleanly on collapse. */
.disclaimer__body {
  overflow: hidden;
  max-height: 40rem;
  opacity: 1;
  transition: max-height 0.4s ease, opacity 0.28s ease;
}

.disclaimer.is-collapsed .disclaimer__body {
  max-height: 0;
  opacity: 0;
}

.disclaimer__lead {
  margin: 0;
}

/* The four standing points, ruled as a tight list. */
.disclaimer__points {
  margin: var(--spacing-12) 0 0;
  padding-left: 1.15em;
}

.disclaimer__points li {
  margin-bottom: var(--spacing-6);
}

.disclaimer__points li:last-child {
  margin-bottom: 0;
}

/* Tool chips — cornflower fill to read as a different axis from the lime
   category chip. Inked outline, same geometry as .chip. */
.tool-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-8);
  margin: 0;
  padding: 0;
  list-style: none;
}

.tool-chip {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.067em;
  text-transform: uppercase;
  color: var(--color-press-black);
  background: var(--color-cornflower-wash);
  border: 1px solid var(--color-press-black);
  border-radius: var(--radius-buttons);
  padding: 6px 10px;
}

/* --------------------------------------------------------------------------
   Role filter bar — archive.html + playbook.html (rendered by renderRoleFilter).
   Bordered pill chips; the selected chip inverts to solid press-black ink.
   -------------------------------------------------------------------------- */
.role-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-12) var(--spacing-16);
  margin-bottom: var(--spacing-40);
}

.role-filter__kicker {
  letter-spacing: 0.12em;
}

.role-filter__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-8);
}

.role-chip {
  font-family: var(--font-sans);
  font-size: var(--text-caption);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.067em;
  text-transform: uppercase;
  color: var(--color-press-black);
  background: transparent;
  border: 1px solid var(--color-press-black);
  border-radius: var(--radius-pills);
  padding: 8px 15px;
  min-height: 34px;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}

.role-chip:hover {
  background: var(--color-paper-shadow);
}

.role-chip.is-selected {
  background: var(--color-press-black);
  color: var(--color-manuscript-cream);
}

.role-chip.is-selected:hover {
  background: var(--color-press-black);
}

/* Inverted strip variant — the front-page full-width black band. The .role-strip
   wrapper paints the edge-to-edge band; .role-filter--strip constrains the row
   to the page measure. Chips outline in cream; the selected chip goes solid lime. */
.role-strip {
  background: var(--color-press-black);
  border-bottom: 1px solid var(--color-press-black);
}

.role-filter--strip {
  max-width: var(--page-max-width);
  margin: 0 auto;
  padding: var(--spacing-12) var(--spacing-30);
  gap: var(--spacing-10) var(--spacing-12);
}

.role-filter--strip .role-filter__kicker {
  color: var(--color-manuscript-cream);
}

.role-filter--strip .role-chip {
  color: var(--color-manuscript-cream);
  background: transparent;
  border-color: var(--color-manuscript-cream);
}

.role-filter--strip .role-chip:hover {
  background: rgba(249, 249, 247, 0.14);
}

.role-filter--strip .role-chip.is-selected,
.role-filter--strip .role-chip.is-selected:hover {
  color: var(--color-press-black);
  background: var(--color-highlighter-lime);
  border-color: var(--color-highlighter-lime);
}

/* Cream focus ring so the outline stays visible on the black band. */
.role-filter--strip .role-chip:focus-visible {
  outline-color: var(--color-manuscript-cream);
}

/* --------------------------------------------------------------------------
   index.html — the "Twin Gate" front page.
   A chooser gate (dot grid + two display pills) fronts two full-screen section
   fronts, News and AI Guide. Picking a side slides its panel in from the edge
   it lives on; a spine portal at the opposite edge crosses to the other side.
   Both panels stay mounted so the slide is a pure transform — inactive ones are
   marked `inert` from script so they leave the tab order.
   Reuses .chip, .pill, .state-* and the shared role-filter logic; the .panel__*
   content pieces below are shared by both section fronts.
   -------------------------------------------------------------------------- */

/* Each section front is fixed and scrolls its own content column, so the
   document behind them only ever has the masthead and the gate. Horizontal
   overflow is clipped because the panels park off-canvas left and right.
   The column layout lets the gate take whatever height the ribbon leaves. */
body.frontpage--gate {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  overflow-x: hidden;
}

.gate {
  position: relative;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--spacing-40) var(--spacing-20);
  background: var(--surface-page-canvas);
  opacity: 1;
  transition: opacity 0.35s ease;
}

/* Hidden (but still laid out) while a section front is open. */
.gate.is-behind {
  opacity: 0;
  pointer-events: none;
}

/* Printer's dot screen. The second layer is a larger, cornflower dot revealed
   only in a soft circle around the cursor — the grid "focuses" under the
   pointer. Pure decoration: masked, non-interactive, and skipped entirely for
   reduced-motion and touch (no hover) users. */
.gate__dots {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(circle at center, var(--color-sage-border) 1.2px, transparent 1.4px);
  background-size: 20px 20px;
}

.gate__dots--focus {
  background-image: radial-gradient(circle at center, var(--color-cornflower-wash) 2.2px, transparent 2.4px);
  opacity: 0;
  transition: opacity 0.18s ease;
  -webkit-mask-image: radial-gradient(circle at var(--gate-x, 50%) var(--gate-y, 50%), #000 72px, transparent 120px);
  mask-image: radial-gradient(circle at var(--gate-x, 50%) var(--gate-y, 50%), #000 72px, transparent 120px);
}

.gate.is-tracking .gate__dots--focus {
  opacity: 1;
}

.gate__inner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-30);
  text-align: center;
}

.gate__kicker {
  margin: 0;
  letter-spacing: 0.18em;
  color: #555555;
}

.gate__choices {
  display: flex;
  align-items: center;
  gap: var(--spacing-16);
}

.gate__dot {
  font-size: 22px;
  line-height: 1;
  color: var(--color-smoke);
  user-select: none;
}

/* Display-scale entry buttons — Anton, the same face the section-front titles
   use, so the gate reads as a table of contents for them. */
.gate__pill {
  font-family: var(--font-display);
  font-size: clamp(22px, 4vw, 30px);
  font-weight: var(--font-weight-regular);
  line-height: 1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 18px var(--spacing-40);
  border: 1px solid var(--color-press-black);
  border-radius: var(--radius-pills);
  cursor: pointer;
  transition: transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.18s ease;
}

.gate__pill--ink {
  color: var(--color-manuscript-cream);
  background: var(--color-press-black);
}

.gate__pill--lime {
  color: var(--color-press-black);
  background: var(--color-highlighter-lime);
}

.gate__pill:hover,
.gate__pill:focus-visible {
  transform: translateY(-4px);
  box-shadow: var(--shadow-sm);
}

.gate__pill:active {
  transform: translateY(0);
}

/* --- Section fronts ------------------------------------------------------ */

.section-front {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  transition: transform 0.62s cubic-bezier(0.77, 0, 0.175, 1);
}

/* Each panel parks off the edge it belongs to, so News always arrives from
   the left and the Guide from the right — the portals point the same way. */
.section-front--news {
  transform: translateX(-100%);
  background: var(--surface-page-canvas);
}

.section-front--guide {
  transform: translateX(100%);
  background: var(--color-paper-shadow);
}

.section-front.is-active {
  transform: translateX(0);
}

/* The panel's own scroll region — the document behind it stays fixed. The
   masthead now lives inside this column, so it scrolls and retracts with the
   content exactly like every other page (see initDynamicHeader). Its sticky
   z-index keeps it above the spine portal that overlays the panel edge. */
.section-front__scroll {
  flex: 1 1 auto;
  overflow-y: auto;
}

.section-front .masthead--dynamic {
  z-index: 41;
}

/* The AI Guide front sits on the paper tint, so its compact bar matches. */
.section-front--guide .pagehead {
  background: var(--color-paper-shadow);
}

.section-front__inner {
  max-width: 860px;
  margin: 0 auto;
  padding: var(--spacing-60) var(--spacing-40) var(--spacing-60);
}

.section-front__title {
  margin: 0 0 var(--spacing-8);
  font-family: var(--font-display);
  font-weight: var(--font-weight-regular);
  font-size: clamp(42px, 6vw, 68px);
  line-height: 0.95;
  letter-spacing: -0.001em;
  text-transform: uppercase;
  color: var(--color-press-black);
}

.section-front__sub {
  margin: 0 0 var(--spacing-30);
  font-family: var(--font-serif);
  font-weight: var(--font-weight-light);
  font-size: var(--text-body-lg);
  line-height: 1.3;
  letter-spacing: 0.01em;
  color: #333333;
}

.section-front__rule {
  border: none;
  border-top: 1px solid var(--color-press-black);
  margin: 0 0 var(--spacing-30);
}

/* Footer actions: the section's own pill, plus the always-visible cross-link
   that stands in for the spine portal wherever hover isn't available. */
.section-front__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-16);
  margin-top: var(--spacing-40);
}

.section-front__cross {
  display: none;
}

/* --- Spine portals ------------------------------------------------------- */

/* A press-black band down the outer edge — the bound spine of the other
   section. It carries its label at rest and widens on hover/focus to add the
   arrow, so the affordance is visible before anyone finds it with the mouse. */
.portal {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 30;
  width: 30px;
  padding: 0;
  border: none;
  background: var(--color-press-black);
  color: var(--color-manuscript-cream);
  cursor: pointer;
  overflow: hidden;
  transition: width 0.55s cubic-bezier(0.77, 0, 0.175, 1);
}

.portal--right {
  right: 0;
}

.portal--left {
  left: 0;
}

.portal:hover,
.portal:focus-visible {
  width: 170px;
}

.portal:focus-visible {
  outline-color: var(--color-highlighter-lime);
  outline-offset: -4px;
}

.portal__inner {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-16);
}

.portal__arrow {
  font-size: 24px;
  line-height: 1;
  color: var(--color-highlighter-lime);
  opacity: 0;
  transition: opacity 0.22s ease;
}

.portal:hover .portal__arrow,
.portal:focus-visible .portal__arrow {
  opacity: 1;
}

.portal__label {
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  white-space: nowrap;
  writing-mode: vertical-lr;
}

.portal--left .portal__label {
  transform: rotate(180deg);
}

.panel__kicker {
  display: flex;
  align-items: center;
  gap: var(--spacing-10);
  margin: 0 0 var(--spacing-6);
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: var(--font-weight-bold);
  line-height: 1;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-press-black);
}

.panel__kicker-sq {
  flex: none;
  width: 9px;
  height: 9px;
  background: var(--color-highlighter-lime);
  border: 1px solid var(--color-press-black);
}

/* Lead story — a top hairline sets it off from the section rule above. */
.panel__lead {
  border-top: 1px solid var(--color-press-black);
  padding: var(--spacing-15) 0 var(--spacing-16);
}

.panel__lead-chip {
  margin: 0 0 var(--spacing-8);
}

.panel__lead-headline {
  margin: 0 0 var(--spacing-6);
  max-width: 46ch;
  font-family: var(--font-serif);
  font-weight: var(--font-weight-regular);
  font-size: 24px;
  line-height: 1.12;
  letter-spacing: 0;
}

.panel__lead-headline a {
  color: var(--color-press-black);
  text-decoration: none;
}

.panel__lead-headline a:hover {
  text-decoration: underline;
}

.panel__lead-summary {
  margin: 0;
  max-width: 46ch;
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.4;
  letter-spacing: 0.02em;
  color: #333333;
}

.panel__meta {
  margin: var(--spacing-8) 0 0;
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: var(--font-weight-medium);
  line-height: 1.3;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #555555;
}

/* Ruled row list — the next three stories. Sage hairlines, lighter than the
   lead's press-black rule. */
.panel__rows {
  border-top: 1px solid var(--color-sage-border);
}

.panel__row {
  position: relative;
  display: flex;
  justify-content: space-between;
  gap: var(--spacing-16);
  padding: 11px 0;
  border-bottom: 1px solid var(--color-sage-border);
}

.panel__row:last-child {
  border-bottom: none;
}

.panel__row-title {
  font-family: var(--font-serif);
  font-weight: var(--font-weight-regular);
  font-size: 15px;
  line-height: 1.25;
}

.panel__row-title a {
  color: var(--color-press-black);
  text-decoration: none;
}

/* Stretched link: the whole row is one click target, one tab stop. */
.panel__row-title a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.panel__row:hover .panel__row-title a {
  text-decoration: underline;
}

.panel__row:has(.panel__row-title a:focus-visible) {
  outline: 2px solid var(--color-press-black);
  outline-offset: 2px;
}

.panel__row-title a:focus-visible {
  outline: none;
}

.panel__row-meta {
  flex: none;
  padding-top: 3px;
  font-family: var(--font-sans);
  font-size: 9px;
  font-weight: var(--font-weight-medium);
  line-height: 1.3;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #555555;
  white-space: nowrap;
}

/* Compact in-panel states keep the panel title + footer visible around them. */
.panel__empty {
  border-top: 1px solid var(--color-sage-border);
  padding-top: var(--spacing-16);
}

.panel__empty .state-card__title {
  font-size: 22px;
}

/* Slim note pinned under each section front's content column. */
.section-front__note {
  margin: var(--spacing-40) 0 0;
  padding-top: var(--spacing-20);
  border-top: 1px solid var(--color-sage-border);
  max-width: 52ch;
  font-family: var(--font-sans);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: #555555;
}

/* The portals are withdrawn below 1200px and the footer cross-link takes over.
   The threshold is set by geometry, not by device class: the reading column is
   860px centred, so an expanded 170px spine only clears it from ~1200px up.
   Any narrower and the spine would sit on top of the text it points away from.
   (Hover-less devices lose the portal for the same reason further down — the
   cross-link is the affordance that always works.) */
@media (max-width: 1200px) {
  .portal {
    display: none;
  }

  .section-front__cross {
    display: inline-block;
  }
}

@media (max-width: 900px) {
  .section-front__inner {
    padding: var(--spacing-40) var(--spacing-20) var(--spacing-40);
  }
}

/* Touch and stylus pointers never hover, so the spine could never be opened. */
@media (hover: none) {
  .portal {
    display: none;
  }

  .section-front__cross {
    display: inline-block;
  }
}

@media (max-width: 640px) {
  .section-front__nav {
    padding: var(--spacing-12) var(--spacing-16);
  }

  .section-front__links {
    gap: var(--spacing-13);
  }

  .role-filter--strip {
    padding: var(--spacing-10) var(--spacing-16);
  }

  .section-front__inner {
    padding: var(--spacing-30) var(--spacing-16) var(--spacing-40);
  }

  .gate__choices {
    flex-direction: column;
    gap: var(--spacing-12);
  }

  /* The separator dot only reads as one between two side-by-side pills. */
  .gate__dot {
    display: none;
  }
}

/* Honour reduced-motion: the panels still change, they just don't travel, and
   the cursor-tracking dot layer never fires. */
@media (prefers-reduced-motion: reduce) {
  .section-front,
  .portal,
  .gate,
  .gate__pill {
    transition: none;
  }

  .gate__dots--focus {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   "Why this matters to you" block — article.html + usecase.html (renderRelevance).
   A hairline-bordered section echoing the field blocks, with a single small
   lime marker beside the kicker (the wordmark-foot motif) as the only accent.
   -------------------------------------------------------------------------- */
.relevance {
  border: 1px solid var(--color-press-black);
  border-radius: var(--radius-cards);
  padding: var(--spacing-20);
  margin-bottom: var(--spacing-30);
  max-width: 70ch;
}

.relevance__kicker {
  display: flex;
  align-items: center;
  gap: var(--spacing-8);
  margin: 0 0 var(--spacing-16);
  letter-spacing: 0.12em;
}

.relevance__marker {
  display: inline-block;
  width: 0.7em;
  height: 0.7em;
  background: var(--color-highlighter-lime);
  border: 1px solid var(--color-press-black);
}

.relevance__entry {
  margin-bottom: var(--spacing-16);
}

.relevance__entry:last-child {
  margin-bottom: 0;
}

.relevance__label {
  margin: 0 0 var(--spacing-6);
}

.relevance__body {
  margin: 0;
  max-width: 65ch;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.27;
  letter-spacing: 0.04em;
  color: var(--color-press-black);
}

/* Detail-page field block: a caption label over a body paragraph. */
.field {
  margin-bottom: var(--spacing-20);
}

.field__label {
  margin: 0 0 var(--spacing-8);
}

.field__body {
  margin: 0;
  max-width: 65ch;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.27;
  letter-spacing: 0.04em;
  color: var(--color-press-black);
  white-space: pre-line;
}

/* --- Page transitions ----------------------------------------------------- */

/* Cross-document View Transitions. The incoming page slides in from the right
   while the outgoing one drifts left and dims underneath — the same transform,
   easing and duration as the front-page section fronts above, so clicking into
   a story moves with the same grammar as the gate.

   CSS only, deliberately. The JS alternative (intercept the link, animate out,
   then navigate) holds the reader on the old page while it plays, so a slow
   load feels slower still. Browsers without cross-document view transitions
   ignore every rule below and navigate exactly as they did before. */
@view-transition {
  navigation: auto;
}

/* The UA default for the root pair is a crossfade over an isolated,
   plus-lighter blended pair. Both have to go, or the slide arrives with a
   dissolve laid over it and the overlap greys out where the pages cross. */
::view-transition-image-pair(root) {
  isolation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  mix-blend-mode: normal;
}

@keyframes page-slide-out {
  to {
    transform: translateX(-22%);
    opacity: 0.55;
  }
}

@keyframes page-slide-in {
  from {
    transform: translateX(100%);
  }
}

/* The new snapshot paints above the old one, so the arriving page slides over a
   page that is still visibly there — that overlap is what reads as depth. */
::view-transition-old(root) {
  animation: page-slide-out 0.62s cubic-bezier(0.77, 0, 0.175, 1) both;
}

::view-transition-new(root) {
  animation: page-slide-in 0.62s cubic-bezier(0.77, 0, 0.175, 1) both;
}

/* Opt out wholesale rather than zeroing the animations: a zeroed transition
   still snapshots both pages and can flash on the swap. */
@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}

/* Back navigation plays the forward slide in reverse: the page being left
   retreats to the right and the page being returned to comes back in from
   under it, un-dimming. The z-index swap is what makes it a true mirror — on
   the way forward the arriving page travels over the top, so on the way back
   the departing page has to.

   `vt-back` is set on <html> by app.js during `pagereveal`, because only script
   can tell a Back from a Forward. Without the class every navigation keeps the
   forward direction, which is exactly the behaviour before this rule existed. */
@keyframes page-slide-back-out {
  to {
    transform: translateX(100%);
  }
}

@keyframes page-slide-back-in {
  from {
    transform: translateX(-22%);
    opacity: 0.55;
  }
}

html.vt-back::view-transition-old(root) {
  z-index: 1;
  animation-name: page-slide-back-out;
}

html.vt-back::view-transition-new(root) {
  z-index: 0;
  animation-name: page-slide-back-in;
}
