/* ===========================================================================
   therefs.org placeholder landing — Nocturne Studio (D-254)

   One stylesheet for every page of the placeholder site: the home page and the
   eight Phase B routes (D-252). Tokens, type scale, radii and spacing are the
   founder's sheet in docs/ui-ux/nocturne-studio-design-system.md, applied as
   written; the layout is that sheet's mockup with the founder's 768px reading
   column inside a wider shell.

   Two rules this file exists to keep: no @import and no external URL of any
   kind (D-241 — the page makes zero external requests), and the two typefaces
   are served from this origin under /fonts, which is why the _headers CSP now
   allows style-src 'self' and font-src 'self' and nothing more.

   Cached immutable for a year (see _headers), so every page references it with
   a version query: /styles.css?v=YYYY-MM-DD. Bump that query in every page in
   the same edit as a change here, or readers keep the old file.
   =========================================================================== */

/* --- Typefaces. Both SIL Open Font License; the licence texts ship beside the
       files in /fonts. Geist for prose and headings, JetBrains Mono for
       eyebrows, chips, record labels and values, the status pill and the footer
       line. font-display: swap, so text renders in the fallback stack first and
       never blocks on a font. ----------------------------------------------- */
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/Geist-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/Geist-Medium.woff2") format("woff2");
}
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/Geist-SemiBold.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/JetBrainsMono-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/JetBrainsMono-Medium.woff2") format("woff2");
}

:root {
  /* --- Surfaces. The page ground is the sheet's base canvas #2b3038 — the
         charcoal-graphite slate the mockup shows, "sitting comfortably above
         pitch black without washing out into mid-grey". The sheet's darker
         `background` token (#0f141b) is recorded below and deliberately unused:
         taking it as the ground would drop the whole page a tier and leave the
         recessed wells (#20242b) brighter than the canvas they sit in, which
         inverts the sheet's own tonal stepping. ------------------------------ */
  --surface: #2b3038;
  --surface-dim: #242830;
  --surface-bright: #3b414d;
  --surface-container-lowest: #20242b;
  --surface-container-low: #262a32;
  --surface-container: #2e333c;
  --surface-container-high: #353a45;
  --surface-container-highest: #3b414d;
  --background: #0f141b; /* sheet token, not the ground here — see above */

  /* --- On-surface tiers: primary content, supporting copy, operational
         labels. ------------------------------------------------------------- */
  --on-surface: #f1f3f7;
  --on-surface-variant: #cbd2db;
  --on-surface-dim: #8a92a0;

  /* --- Hairlines and focus. No cast shadows anywhere on this page: elevation
         is tonal stepping plus a 1px ghost outline. ------------------------- */
  --outline: #484f5d;
  --outline-variant: #383e4a;
  --border-focus: #8a92a0;

  /* --- Primary. No button ships on the placeholder — there is nowhere to send
         a reader before October 2026 (D-241 refinement), and a dead CTA is
         worse than none — so these carry the sheet's values for the Phase B
         pages and the app's marketing routes to inherit. ------------------- */
  --primary: #f1f3f7;
  --primary-hover: #ffffff;
  --on-primary: #20242b;

  /* --- Radii (sheet `rounded`). ---------------------------------------- */
  --radius-sm: 0.125rem;
  --radius: 0.25rem;
  --radius-md: 0.375rem;
  --radius-lg: 0.5rem;
  --radius-xl: 0.75rem;
  --radius-full: 9999px;

  /* --- Spacing (sheet `spacing`). -------------------------------------- */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.75rem;
  --space-xl: 3rem;
  --gutter: 1.5rem;

  /* --- Layout. --shell is the page container: the sheet caps content at
         1440px, the founder's mockup is ~1060 wide, so 1120 sits where the
         mockup sits with the sheet's desktop margins outside it. --measure is
         the founder's reading column ("place the content inside 768 px
         container"): every prose block below the hero is that wide and starts
         at the shell's left edge with the card grids (the D-254 addendum —
         see .measure), and the two-column blocks — the definition section, the
         search columns, the contribution lanes — use the full shell width from
         the same edge. Since that addendum's second pass the hero and the
         closing band start on that edge too: nothing is centred. --pad is the
         sheet's canvas
         margin, mobile 1.25rem / tablet 2rem / desktop 3rem. --------------- */
  --shell: 1120px;
  --measure: 768px;
  --pad: 1.25rem;
  --section-gap: clamp(4rem, 8vw, 7rem);
  /* The sticky top bar's own height, named because two rules need the same number:
     the bar sets it, and every anchored section clears it (D-258 — see .section[id]). */
  --topbar-h: 56px;

  /* --- Families. The fallback stacks are what a reader sees before the woff2
         lands, and the whole page if it never does. ------------------------ */
  --font-sans: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

@media (min-width: 768px) {
  :root { --pad: 2rem; }
}
@media (min-width: 1280px) {
  :root { --pad: 3rem; }
}

/* ===========================================================================
   Reset and ground
   =========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--surface);
  color: var(--on-surface);
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 400;
  line-height: 24px;
  letter-spacing: 0.005em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

p, h1, h2, h3, dd { margin: 0; }
ul, ol, dl { margin: 0; padding: 0; }
li { list-style: none; }

a {
  color: var(--on-surface-variant);
  text-decoration: none;
  transition: color 120ms linear;
}
a:hover { color: var(--on-surface); text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* There is no motion on this page — no transform, no animation, and the only
   transitions are colour changes on a link. This block is here so that stays
   true of anything added later. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ===========================================================================
   Type scale — the sheet's `typography`, one class per entry
   =========================================================================== */

.display-hero {
  font-family: var(--font-sans);
  font-size: 36px;
  font-weight: 600;
  line-height: 42px;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
@media (min-width: 768px) {
  .display-hero { font-size: 56px; line-height: 64px; letter-spacing: -0.03em; }
}

.headline-lg {
  font-family: var(--font-sans);
  font-size: 32px;
  font-weight: 500;
  line-height: 40px;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
.headline-md {
  font-family: var(--font-sans);
  font-size: 24px;
  font-weight: 500;
  line-height: 32px;
  letter-spacing: -0.015em;
  text-wrap: pretty;
}
.headline-sm {
  font-family: var(--font-sans);
  font-size: 20px;
  font-weight: 500;
  line-height: 28px;
  letter-spacing: -0.01em;
}
.body-lg {
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: 400;
  line-height: 30px;
  letter-spacing: 0;
  text-wrap: pretty;
}
.body-md {
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 400;
  line-height: 24px;
  letter-spacing: 0.005em;
  text-wrap: pretty;
}
.body-sm {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 400;
  line-height: 20px;
  letter-spacing: 0.01em;
  text-wrap: pretty;
}
.label-caps {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  line-height: 16px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.label-mono {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 400;
  line-height: 18px;
  letter-spacing: 0.02em;
}

/* Tone helpers, so a block's colour reads in the markup next to its scale.
   .tone-dim is unused on the home page and kept for the Phase B pages, as is
   .cards-2 below — both belong to the template's commented slots. */
.tone-variant { color: var(--on-surface-variant); }
.tone-dim { color: var(--on-surface-dim); }

/* ===========================================================================
   Layout: the shell, the reading column, the vertical rhythm
   =========================================================================== */

.shell {
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--pad);
}

/* Every prose block on the page is this: the founder's 768px column of measure.
   Where it *starts* is the D-254 addendum (founder, 2026-09-24: "Some blocks are
   centered, some blocks are aligned left, we need to unify it") as its second
   addendum finished it the same night ("Pages still not layed out properly, some
   blocks aligned left, some centered. Make another pass.").

   There is **one left edge, no exceptions**: the hero, every section, the FAQ, the
   audience line, the closing call and the footer rows all begin at the shell's left
   edge, so a section's heading and its cards line up instead of the heading starting
   ~176px to their right. Prose keeps its 768px measure and simply stops there; it is
   never centred. Nothing on this site is centred — the first addendum's two centred
   bookends are retired, and the mockup's centred hero was a reference for the shapes,
   not a second alignment rule. `.audience-line` survives as the marker for the one
   band with no heading, which the stylesheet has nothing else to name it by. */
.measure {
  max-width: var(--measure);
  margin-inline: 0;
}

.section { padding-top: var(--section-gap); }

/* Anchored sections (D-258). Two pages absorbed six retired routes, and each absorbed
   route survives as a section with the `id` its 301 lands on — /for#cinematographers,
   /film-stills#movie-scene-finder and their siblings. The top bar is `position: sticky`,
   so a section scrolled to by fragment would begin underneath it; `scroll-margin-top` is
   the browser's own offset for exactly that. One rule for every section carrying an id,
   the new anchors and the home page's older #search / #sheet / #contribute alike. */
.section[id] { scroll-margin-top: calc(var(--topbar-h) + var(--space-lg)); }

/* Consistent internal rhythm: a section's first child sets the heading, and
   everything after it steps down by one space token. */
.stack > * + * { margin-top: var(--space-md); }
.stack > * + .eyebrow { margin-top: var(--space-xl); }
/* An h3 inside a stacked section opens a sub-block rather than continuing the prose, so
   it takes the eyebrow's larger gap: on /for and /film-stills a retired page's material
   sits under the absorbing section's h2 as exactly that (D-258). */
.stack > * + h3 { margin-top: var(--space-xl); }
.stack > * + .rows { margin-top: var(--space-lg); }

/* A line that answers to the whole section rather than to the block above it. */
.section-note {
  margin-top: var(--space-xl);
  color: var(--on-surface-variant);
}

.eyebrow { color: var(--on-surface-dim); }

/* ===========================================================================
   Top bar — wordmark left, the one status the site exists to carry right
   =========================================================================== */

.topbar {
  position: sticky;
  top: 0;
  z-index: 10;
  border-bottom: 1px solid var(--outline-variant);
  background: rgba(43, 48, 56, 0.85);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.topbar .shell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  min-height: var(--topbar-h);
}
.wordmark {
  font-family: var(--font-mono);
  font-size: 15px;
  font-weight: 500;
  line-height: 20px;
  letter-spacing: 0;
  color: var(--on-surface);
}
a.wordmark:hover { color: var(--on-surface); text-decoration: none; }

/* ===========================================================================
   Pills — the sheet's metadata capsules: the eyebrow, the status, the chips
   over the reference card, the API stamp, the filter names
   =========================================================================== */

.pill {
  display: inline-block;
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-full);
  background: var(--surface-container-low);
  padding: 4px 10px;
  color: var(--on-surface-variant);
  white-space: nowrap;
}

.pill-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

/* A heading with its stamp beside it. The pill rides the heading's baseline
   rather than its line box, so the two sit level at every size — stretched to
   the line box a 16px label becomes a 40px capsule. */
.heading-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-sm) var(--space-md);
}

/* ===========================================================================
   Hero
   =========================================================================== */

/* Left-aligned like everything else since D-254's second addendum: the eyebrow pill,
   the H1, the sub-line, the status box and the fine print all start on the shell's
   left edge. The display size of the H1 is unchanged — alignment moved, scale did
   not. The grid below is still centred on the hero's own box, because it is a
   background wash rather than a block of content. */
.hero {
  position: relative;
  isolation: isolate;
  padding-top: clamp(3rem, 7vw, 5.5rem);
}

/* The mockup's faint grid behind the hero. Drawn with two gradients — no image
   file, nothing to load — and masked so it fades out well before the next
   section. */
.hero::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  background-image:
    linear-gradient(to right, rgba(241, 243, 247, 0.035) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(241, 243, 247, 0.035) 1px, transparent 1px);
  background-size: 64px 64px;
  background-position: center top;
  -webkit-mask-image: radial-gradient(ellipse 65% 70% at 50% 30%, #000, transparent 72%);
  mask-image: radial-gradient(ellipse 65% 70% at 50% 30%, #000, transparent 72%);
  pointer-events: none;
}

.hero h1 { margin-top: var(--space-lg); }
.hero .hero-sub {
  margin-top: var(--space-lg);
  color: var(--on-surface-variant);
}

/* The boxed status line: one tonal step up from the canvas, hairline, and sized as a
   statement rather than a note. It is an inline-block, so it shrink-wraps its sentence
   and sits at the hero's left edge with everything else (D-254 second addendum). */
.status-box {
  display: inline-block;
  margin-top: var(--space-xl);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-md);
  background: var(--surface-container);
  padding: 12px 20px;
  font-weight: 500;
  color: var(--on-surface);
}

/* The hero's fine print (S1.7) is a sentence, not a label, so it takes the
   supporting-copy tone rather than the operational one — changed 2026-09-24, the
   only tone change in this pass. The mono labels, eyebrows and record labels stay
   on --on-surface-dim, which is what that tier is for. */
.hero .fineprint {
  margin-top: var(--space-md);
  color: var(--on-surface-variant);
}

/* The hero is the one centred block on the site (D-254 fourth addendum, founder
   2026-09-28: "make this part centered"): its measure sits in the middle of the shell
   and its text, the eyebrow pill and the inline-block status box centre with it.
   Everything below the hero keeps the shell's left edge (second addendum). */
.hero .measure {
  margin-inline: auto;
  text-align: center;
}

/* ===========================================================================
   The definition section — the reference card as the first row, the text below
   =========================================================================== */

/* D-254 third addendum (founder, 2026-09-24, on this block: *"1. example frame +
   params first row. Frame first column (3/4), params second. 2. rest is below. full
   content width."*). The band is two rows on the shell, not two columns: the card
   first, spanning the whole width, and under it the eyebrow, the definition
   sentences and the four label/value rows — at the shell's full content width, not
   the 768px measure, because the founder asked for full width. The card's own two
   columns are the `.refcard` grid below; this rule only stacks the two rows.

   The class name is what D-255's two-column version left behind: the element is the
   definition band, and nothing else on the site uses it. */
.split {
  display: grid;
  gap: var(--space-xl);
}

/* Label/value rows: mono caps label left, sentence-case value right, hairline
   rules. The sheet's data rows, with no hover state because nothing here is a
   row you can open. */
.rows {
  margin-top: var(--space-lg);
  border-top: 1px solid var(--outline-variant);
}
.rows .row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  border-bottom: 1px solid var(--outline-variant);
  padding: 10px 0;
}
.rows dt { color: var(--on-surface-dim); }
.rows dd { color: var(--on-surface); text-align: right; }

/* The definition text row is two columns from 900px (D-254 fifth addendum, founder
   2026-09-28: "make it two column as well"): the eyebrow, headline and paragraphs in
   the first column, the four label/value rows in the second, top-aligned with the
   eyebrow. Below 900px the row stacks as before. */
@media (min-width: 900px) {
  .split .stack {
    display: grid;
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    column-gap: 4rem;
    align-items: start;
  }
  .split .stack > * { grid-column: 1; }
  /* The rows start level with the headline, not the eyebrow (founder, 2026-09-28:
     "make second column aligned to title from the first column"). */
  .split .stack > .rows {
    grid-column: 2;
    grid-row: 2 / span 3;
    /* Grid tracks start at an item's margin edge, and the headline carries the
       stack's --space-md top margin; its capitals then begin 11px into the
       32px line (4px half-leading plus Geist's ascender-to-cap gap, measured in
       the browser on 2026-09-28). The rows take the same offset so the hairline
       meets the top of the headline's letters, not the top of its track. */
    margin-top: calc(var(--space-md) + 11px);
  }
}

/* ===========================================================================
   Reference card — media well, chips, inspector strip
   =========================================================================== */

.refcard {
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-lg);
  background: var(--surface-container);
  overflow: hidden;
}
.refcard-media { position: relative; }

/* The card's two columns (D-254 third addendum): the picture at three quarters of the
   card, the record's strip and the credit line beside it at one quarter. Below 900px
   the card is what it always was — well, then strip, then credit, stacked.

   Three things this grid depends on, and every one of them is a constraint on
   `renderCard` in scripts/pick-frame.mjs and on src/worker.mjs:

   - the three blocks are **direct children** of `.refcard`, in document order. The
     Worker adds a missing credit line by inserting a `<p>` before the card's own end
     tag, so a wrapper element around the strip and the credit would put that
     inserted line outside the column it belongs to. Placed here by row and column
     instead, it lands in the second column whether the build baked it or the Worker
     put it there.
   - the third row is `1fr` and the media spans all three. A spanning item does not
     grow content-sized tracks it shares with a flexible one, so the strip and the
     credit keep their own heights at the top of the column and the slack under them
     collapses into the fr row — the credit line sits under the strip rather than
     being pushed to the bottom of a tall card.
   - the column hairline is the media column's right border rather than the well's
     bottom one, so it runs the full height of the card whichever column is taller
     (a 2.39:1 frame is shorter than six stacked rows; a 4:3 frame is taller). */
@media (min-width: 900px) {
  .refcard {
    display: grid;
    grid-template-columns: 3fr minmax(0, 1fr);
    grid-template-rows: auto auto 1fr;
  }
  .refcard-media {
    grid-column: 1;
    grid-row: 1 / -1;
    border-right: 1px solid var(--outline-variant);
  }
  /* The picture fills its column (founder, 2026-09-28: "use fit cover for image").
     The well arrives with the frame's own inline aspect-ratio for the stacked
     layout; here the media column is a flex column and the well takes the
     column's full height as a definite size, which an aspect-ratio never
     overrides, so the image covers it and crops rather than leaving card
     surface under itself. A frame taller than the strip still sets the card's
     height and is never cropped. */
  .refcard-media { display: flex; flex-direction: column; }
  .refcard-media .well { border-bottom: 0; flex: 1 1 auto; min-height: 0; }
  .refcard .inspector { grid-column: 2; grid-row: 1; }
  .refcard-credit {
    grid-column: 2;
    grid-row: 2;
  }
}

/* The media well holds one real published frame (D-255, superseding D-254 (3) and
   D-241's no-screenshots rule for this card alone). It keeps its tonal surface and
   its 8px gradient grid as the *background*: the picture is painted over them, so
   a well whose image has not arrived yet shows the same surface the first build
   showed, at the same size, and nothing on the card moves when it lands.

   The aspect ratio here is the fallback for the shape of the well itself; the
   frame's own ratio arrives as an inline `aspect-ratio` from pick-frame.mjs, which
   is why the image can fill the box without ever being letterboxed. */
.well {
  aspect-ratio: 2.39 / 1;
  border-bottom: 1px solid var(--outline-variant);
  background-color: var(--surface-container-lowest);
  background-image:
    linear-gradient(to right, rgba(241, 243, 247, 0.028) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(241, 243, 247, 0.028) 1px, transparent 1px);
  background-size: 8px 8px;
}
.well img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.chips-over {
  position: absolute;
  top: 12px;
  left: 12px;
}
.chips-over .pill { background: rgba(38, 42, 50, 0.9); }

.inspector { padding: var(--space-md) var(--space-lg) var(--space-lg); }
.inspector .row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  border-bottom: 1px solid var(--outline-variant);
  padding: 7px 0;
}
.inspector .row:last-child { border-bottom: 0; }
.inspector dt { color: var(--on-surface-dim); }
.inspector dd { color: var(--on-surface); text-align: right; }

/* In the card's narrow column the row stacks: label above value, both left-aligned.
   Measured rather than assumed, over the 22 label/value pairs the baked set actually
   holds: at 1280 the column is 255.5px wide and the strip's text 199.5px of it, while
   the worst pair on one line ("Interior/Exterior" beside "Exterior (EXT)") wants
   246.7px — so a label/value row would wrap in the middle of the strip, and only on
   some frames. Stacked, the widest label measures 133.9px and the widest value 119px,
   so every row of every frame in the set is two clean lines with room to spare. Below
   900px the strip has the whole card's width and keeps the label-left/value-right row
   it has always had. */
@media (min-width: 900px) {
  .refcard .inspector .row { display: block; }
  .refcard .inspector dd { text-align: left; }
}

/* Who made the picture: a band under the strip — the second column's foot from 900px,
   the card's own from below it — hairlined off the strip like the well is. Rendered
   only when the record holds a name (D-239), so the card ends at the last inspector
   row on a frame whose title carries no credits. */
.refcard-credit {
  border-top: 1px solid var(--outline-variant);
  padding: var(--space-md) var(--space-lg);
  color: var(--on-surface-dim);
}

/* ===========================================================================
   Cards — the four search modes and the three contribution lanes
   =========================================================================== */

.cards {
  display: grid;
  gap: var(--space-md);
  margin-top: var(--space-xl);
}
.cards li {
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-lg);
  background: var(--surface-container);
  padding: var(--space-lg);
}
.cards h3 { color: var(--on-surface); }
.cards p { margin-top: var(--space-sm); color: var(--on-surface-variant); }

/* The lanes go 3-up, one per lane; .cards-2 is the pair a Phase B page may want. */
@media (min-width: 720px) {
  .cards-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cards-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ===========================================================================
   Two ways to find the frame (D-253 addendum) — the Mood column, then Lexical
   =========================================================================== */

/* The two columns stretch to a shared height rather than each sizing to its own
   content: the lexical side carries the filter row, and two cards a hundred and
   sixty pixels apart at the bottom edge read as a mistake rather than a pair. */
.two-ways {
  display: grid;
  align-items: stretch;
  gap: var(--space-md);
  margin-top: var(--space-xl);
}
@media (min-width: 768px) {
  .two-ways { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.way {
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-lg);
  background: var(--surface-container);
  padding: var(--space-lg);
}
.way h3 { color: var(--on-surface); }
.way .way-body {
  margin-top: var(--space-sm);
  color: var(--on-surface-variant);
}
.way .eyebrow { margin-top: var(--space-lg); }
.way .pill-row { margin-top: var(--space-sm); }

/* The example run, dressed as the field it comes from — the sheet's input:
   recessed well, hairline, 12px inset, the examples in the placeholder tone. */
.searchfield {
  margin-top: var(--space-md);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius);
  background: var(--surface-container-lowest);
  padding: 10px 12px;
}

/* The rotation, with no JavaScript (D-241): the examples are stacked on one
   another and their opacity is cycled by one keyframe each, staggered four
   seconds apart — ten examples, a forty-second round. The row is two lines tall
   at every width, so a long example wraps instead of pushing the page sideways
   and the field's height never changes as the lines swap.

   Under prefers-reduced-motion the stack collapses: the animation is off and
   the four fixed examples render as a static wrapped row. */
.cycle {
  position: relative;
  height: 38px;
  overflow: hidden;
}
.cycle li {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  opacity: 0;
  color: var(--on-surface-dim);
  animation: example-cycle 40s linear infinite;
}
.cycle li:nth-child(1) { animation-delay: 0s; }
.cycle li:nth-child(2) { animation-delay: 4s; }
.cycle li:nth-child(3) { animation-delay: 8s; }
.cycle li:nth-child(4) { animation-delay: 12s; }
.cycle li:nth-child(5) { animation-delay: 16s; }
.cycle li:nth-child(6) { animation-delay: 20s; }
.cycle li:nth-child(7) { animation-delay: 24s; }
.cycle li:nth-child(8) { animation-delay: 28s; }
.cycle li:nth-child(9) { animation-delay: 32s; }
.cycle li:nth-child(10) { animation-delay: 36s; }

@keyframes example-cycle {
  0%, 10%, 100% { opacity: 0; }
  1.5%, 8.5% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .cycle {
    position: static;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs) var(--space-md);
    height: auto;
  }
  .cycle li {
    position: static;
    animation: none;
    opacity: 1;
    display: none;
  }
  .cycle li.fixed { display: flex; }
}

/* ===========================================================================
   The standards — a divided list, rules and never boxes
   =========================================================================== */

.divided {
  margin-top: var(--space-lg);
  border-top: 1px solid var(--outline-variant);
}
.divided > li {
  border-bottom: 1px solid var(--outline-variant);
  padding: var(--space-md) 0;
}

/* ===========================================================================
   FAQ — headed entries with dividers. A question sits closer to its own answer
   than either sits to the rule below.
   =========================================================================== */

.faq {
  margin-top: var(--space-lg);
  border-top: 1px solid var(--outline-variant);
}
.qa {
  border-bottom: 1px solid var(--outline-variant);
  padding: var(--space-lg) 0;
}
.qa h3 { color: var(--on-surface); }
.qa p { margin-top: var(--space-sm); color: var(--on-surface-variant); }

/* ===========================================================================
   Closing block and the quiet footer line
   =========================================================================== */

.closing { padding-top: var(--section-gap); }
.closing .closing-sub {
  margin-top: var(--space-md);
  color: var(--on-surface-variant);
}
.closing .closing-date {
  margin-top: var(--space-lg);
  color: var(--on-surface);
}

/* The one line a subpage ends on instead of repeating the closing call (D-258 (2)):
   search, the sheet, contributing and the standards are said once, on the home page, and
   every subpage points there. Same top gap as the `.closing` band it stands in for, so
   the last block of the page sits where a reader's eye already expects one. The link is
   the only thing on the line that is not the reading tier, because a link a reader cannot
   see is not a pointer. */
.pointer-home { padding-top: var(--section-gap); }
.pointer-home p { color: var(--on-surface-variant); }
.pointer-home a {
  color: var(--on-surface);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.pointer-home a:hover { text-decoration: none; }

.sitefoot {
  margin-top: var(--section-gap);
  border-top: 1px solid var(--outline-variant);
  background: var(--surface-dim);
  padding-block: var(--space-xl);
}

/* The directory of the site's own pages (D-252): same-origin only, and the
   only links anywhere on the page. */
.directory { margin-bottom: var(--space-xl); }
.directory-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-lg);
  margin-top: var(--space-md);
}

.colophon {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-lg);
  border-top: 1px solid var(--outline-variant);
  padding-top: var(--space-md);
  color: var(--on-surface-dim);
}

/* ===========================================================================
   Phase B page blocks (templates/page.html). Same components, no new tokens.
   =========================================================================== */

/* The three-step "how it works" row: the card surface, its three titles the
   sentence-case labels the copy gives ("Describe", "Find", "Take it"). The mono
   number line the template sketches is not built: "Step 1" is a visible string with
   no copy id, and Phase B ships ruled strings only. .steps stays as the hook for it
   if the founder rules one. */
.steps .step-no {
  display: block;
  margin-bottom: var(--space-sm);
  color: var(--on-surface-dim);
}

/* ===========================================================================
   The comparison table (/compare, C7) — the one tabular block on this site
   =========================================================================== */

/* Ten rows of prose across four products cannot fit the reading column, so the
   table is one of D-254's wider blocks and lives in its own scroller: the page
   never scrolls sideways, the table does, inside a hairline card. tabindex on the
   scroller is what gives a keyboard the same reach a trackpad has. */
.table-scroll {
  overflow-x: auto;
  margin-top: var(--space-xl);
  border: 1px solid var(--outline-variant);
  border-radius: var(--radius-lg);
  background: var(--surface-container);
}

/* min-width is the load-bearing number: below it the cells would wrap to a word a
   line, so the table keeps its width and the scroller takes the overflow. */
.ctable {
  width: 100%;
  min-width: 880px;
  border-collapse: collapse;
  text-align: left;
}
.ctable th,
.ctable td {
  border-bottom: 1px solid var(--outline-variant);
  padding: 12px var(--space-md);
  vertical-align: top;
}
.ctable thead th {
  border-bottom: 1px solid var(--outline);
  color: var(--on-surface);
  font-weight: 500;
  white-space: nowrap;
}
.ctable tbody th {
  width: 12.5rem;
  color: var(--on-surface);
  font-weight: 500;
}
.ctable tbody td { color: var(--on-surface-variant); }
.ctable tbody tr:last-child th,
.ctable tbody tr:last-child td { border-bottom: 0; }

/* The provenance line under the table (C7.12): a stamp, so the operational tone. */
.table-note {
  margin-top: var(--space-md);
  color: var(--on-surface-dim);
}

/* ===========================================================================
   404 — one centred block, and still not one link: before October 2026 there is
   nowhere on this site a lost reader would rather be than told the truth.
   =========================================================================== */

.notfound {
  display: flex;
  min-height: 100vh;
  align-items: center;
  justify-content: center;
  padding: var(--space-xl) var(--pad);
  text-align: center;
}
.notfound .inner { max-width: 480px; }
.notfound .wordmark { color: var(--on-surface-dim); }
.notfound h1 { margin-top: var(--space-lg); }
.notfound .notfound-status {
  margin-top: var(--space-md);
  color: var(--on-surface-variant);
}
.notfound .pill { margin-top: var(--space-xl); }
