/* Skippr /compare — page styles
   Built entirely on the Skippr Design System tokens in ds/colors_and_type.css.
   No colour, radius, weight or spacing value is invented here; if something is
   missing it belongs upstream in the design system, not in this file.

   The design-tool export of this page styles everything inline. That is right for
   a canvas and wrong for a repo, so the same patterns live here as classes: one
   place to change, and the Next.js port becomes a copy of this file. */

/* ---------- one deliberate deviation from the design system ----------

   --muted-foreground at L 0.553 is 4.81:1 on white but only 4.41:1 on a --muted
   surface, and this page puts secondary text on --muted in six places: the hero
   band, table headers, the panel bar, the end-stack and the footer. L 0.54 clears
   4.5 on both (4.66 muted / 5.08 white) with no perceptible change in tone.

   Kept here rather than edited into ds/colors_and_type.css so that file stays a
   clean mirror of skippr-hq/design-system. Worth fixing upstream. */

:root { --muted-foreground: oklch(0.54 0.013 58.071); }
.dark { --muted-foreground: oklch(0.72 0.01 56.259); }

/* ---------- reset ---------- */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
body { margin: 0; }
img { max-width: 100%; }

/* ---------- shell ---------- */

.shell {
  max-width: 76rem;
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 5vw, 3.5rem);
}

/* The masthead's own height, published as a token because the full-height hero below
   has to subtract it: the masthead is sticky but still sits in flow, so a band of
   `100svh` under it would run exactly this far past the fold. Measured rather than
   guessed, and it is the same at every breakpoint because the 22px logomark is taller
   than the text beside it: 22px + 2 x .875rem padding + the 1px rule. Keep in step with
   the two values below. */
:root { --masthead-h: 51px; }

/* sticky masthead, translucent over whatever scrolls beneath it */
header.masthead {
  position: sticky; top: 0; z-index: 20;
  background: oklch(from var(--background) l c h / 0.88);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--border);
}
.masthead .shell {
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
  padding-top: .875rem; padding-bottom: .875rem;
}
.wordmark { display: flex; align-items: center; gap: .625rem; text-decoration: none; }
.wordmark img { height: 22px; width: auto; display: block; }
.wordmark span {
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  color: var(--muted-foreground); letter-spacing: var(--tracking-snug);
}

nav.crumbs {
  display: flex; align-items: center; gap: .5rem;
  font-size: var(--text-sm); color: var(--muted-foreground); min-width: 0;
}
nav.crumbs a { color: var(--muted-foreground); text-decoration: none; white-space: nowrap; }
nav.crumbs a:hover { color: var(--primary); }
nav.crumbs .sep { opacity: .5; }
nav.crumbs em { font-style: normal; color: var(--foreground); font-weight: var(--weight-medium); }

/* The full trail does not fit beside the wordmark on a phone: at 375px it pushed the
   document 21px wider than the viewport, giving the whole page a horizontal scroll.
   The wordmark already reads "/ compare", so the ancestors can go and the current page
   stays. The BreadcrumbList in JSON-LD is unaffected, so the structured trail survives. */
@media (max-width: 40rem) {
  nav.crumbs a, nav.crumbs .sep { display: none; }
}

/* ---------- hero band ---------- */

.hero {
  background: var(--muted);
  border-bottom: 1px solid var(--border);
}
.hero .shell {
  padding-top: clamp(3rem, 7vw, 5.5rem);
  padding-bottom: clamp(2.5rem, 5vw, 3.5rem);
}

/* the kicker: a hard rule with the page type left and its freshness right */
.kicker {
  max-width: 34rem; margin-bottom: 1.75rem;
  border-top: 2px solid var(--foreground); padding-top: .625rem;
  display: flex; justify-content: space-between; gap: 1rem;
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  letter-spacing: .09em; text-transform: uppercase;
}
.kicker .what { color: var(--foreground); }
.kicker .when { color: var(--muted-foreground); }

h1 {
  margin: 0;
  font-size: clamp(2.25rem, 5.4vw, 3.75rem);
  font-weight: var(--weight-bold);
  letter-spacing: -0.035em;
  line-height: 1.03;
  max-width: 24ch;
  text-wrap: balance;
}

.standfirst {
  margin: 1.5rem 0 0;
  font-size: clamp(1.0625rem, 1.6vw, 1.3125rem);
  line-height: 1.55;
  color: var(--muted-foreground);
  max-width: 56ch;
  text-wrap: pretty;
}

/* editorial illustration: ring elevation, never a drop shadow.
   Width is locked to the standfirst's measure so the hero reads as one block.
   `ch` depends on font-size, so the figure carries the standfirst's own size to
   make 56ch resolve identically at every breakpoint. */
figure.hero-art {
  margin: 2.25rem 0 0;
  font-size: clamp(1.0625rem, 1.6vw, 1.3125rem);
  max-width: 56ch;
}
figure.hero-art img {
  display: block; width: 100%; height: auto;
  border-radius: var(--radius-2xl);
  box-shadow: var(--ring-1);
}
figure.hero-art figcaption {
  margin-top: .75rem; font-size: var(--text-xs); color: var(--muted-foreground);
}

.byline {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1.5rem;
  margin: 2.25rem 0 0; padding-top: 1.5rem;
  border-top: 1px solid var(--border);
  font-size: var(--text-sm); color: var(--muted-foreground);
}
.byline b, .byline strong { font-weight: var(--weight-semibold); color: var(--foreground); }

/* ---------- the full-height opener ----------

   An illustrated page opens on exactly one screen: kicker, headline and standfirst at
   the top, byline on the fold, and the illustration taking whatever height is left.
   Locking the *band* rather than the picture is what makes every illustrated page open
   identically however long its headline runs, which a fixed image height would not.

   Illustrated pages only. A full screen of empty tint is a bug rather than an opener,
   so a page with no artwork keeps its natural height until it has some.

   `min-height`, not `height`: on a short viewport the text alone can be taller than the
   band, and clipping a headline to keep a promise about height is the wrong trade. */
.hero-full {
  display: flex;
  /* `svh`, not `vh`: mobile browser chrome makes `100vh` taller than the screen, which
     would push the byline out of view on the one device where the whole point is that it
     is the last thing you see before scrolling. The `vh` line is the fallback. */
  min-height: calc(100vh - var(--masthead-h));
  min-height: calc(100svh - var(--masthead-h));
  /*
    One screen is the floor and there is deliberately no ceiling any more. A 120% cap lived
    here and had to go when the bubbles shipped: bounding the band bounds the picture, and a
    smaller picture means smaller dialogue. It was rendering speech at 8.4px on a 1280x800
    screen. The opener now runs as far past the fold as the picture needs, which on a short
    screen is about a third of one.
  */
}

/*
  On a very short screen the ceiling is not a promise that can be kept. At 320x568 the
  kicker, headline, standfirst and byline are together taller than 120% of the screen before
  the picture gets a single pixel, so holding the ceiling would push the byline out of the
  band and over the article beneath it. The height of the opener is a preference; not
  rendering copy on top of other copy is not. Below this the band goes back to sizing itself.
*/
@media (max-height: 700px) {
  .hero-full { max-height: none; }
}
.hero-full > .shell {
  flex: 1;
  /* The rail is a grid and sets `align-items: start` at the wide breakpoint, which would
     leave the main column at its natural height inside a taller band. */
  align-items: stretch;
  /*
    A single row that fills the band and can be compressed by it.

    Without this the ceiling above is unenforceable below the grid. A grid's auto rows size
    to their content and simply overflow a container that is shorter, rather than passing the
    shortfall down: at 1280x600 the shell was clamped to 668px while the row inside it stayed
    765px, so the flex column never went over-full, the picture was never asked to give
    anything back, and the byline rendered over the article beneath. `minmax(0, 1fr)` makes
    the row take the band's height exactly, in both directions, which is what lets
    `flex-shrink` on the picture do its job.
  */
  grid-template-rows: minmax(0, 1fr);
  /*
    Padding on viewport height rather than width, for this band only.

    The band is a fixed budget and everything in it competes: a five-line standfirst on a
    1280x800 screen leaves the picture 126px, under its own floor, so the hero stops fitting
    the screen at all. 144px of that budget was padding sized on `vw`, which is the wrong
    axis, since widening the window does not create vertical room.

    On `vh` the padding gets out of the way exactly when the room is scarce: 48px top at
    800px tall against the full 88px at 1300, which hands 64px back to the picture on the
    screens that need it and changes nothing on the screens that do not. Non-illustrated
    pages keep the original `vw` padding, since they have no picture to protect.
  */
  padding-top: clamp(2.5rem, 6vh, 5.5rem);
  padding-bottom: clamp(1.75rem, 4vh, 3.5rem);
}
.hero-full .rail-main {
  display: flex;
  flex-direction: column;
  /* A grid item's automatic minimum size is its content, which would re-introduce the
     overflow the row track above exists to stop. */
  min-height: 0;
  /* A size container purely so the figure's cap below can be written against the column's
     real width. Nothing here queries it. */
  container-type: inline-size;
}

/* The illustration is the elastic element, and it only ever shrinks. Growing it past its
   natural height would make the box taller than 3:2, and `cover` would then crop the
   sides, which is exactly what `scripts/prepare_art.py` exists to protect: these
   compositions put one figure left and one right. Losing height instead takes sky and
   foreground, and the briefs already reserve the upper third as empty sky. */
.hero-full figure.hero-art {
  /*
    Basis `auto`, so the picture's own height counts towards the band's and the band grows
    to hold it. This is the give in the lock. On basis 0 the picture contributed nothing,
    the band sat at exactly one screen, and the picture absorbed every squeeze on its own:
    a five-line standfirst on a 1280x800 screen left it a 4.6:1 strip. Now the squeeze is
    shared, and a page runs a little past the fold rather than crushing its illustration.
  */
  flex: 1 1 auto;
  /*
    The floor shrinks with the viewport, because it is the last thing standing between the
    ceiling above and text spilling out of the band. On a 600px-tall window a fixed 8rem plus
    a six-line standfirst does not fit inside 120%, and something has to give: better a
    thinner picture than a byline rendered over the article below it.
  */
  min-height: min(8rem, 12svh);
  /*
    Capped at its own natural 3:2 and nothing else.

    There was a second term here, `42svh`, which bounded how far the picture could push the
    band and kept the opener inside 120% of a screen. It cannot survive the bubbles. Squeezing
    the picture squeezes the dialogue with it: at 1280x800 the image came out 379px wide, a
    quarter of the source, and the bubble type rendered at **8.4px**. A hero whose speech is
    too small to read is not a hero with a caveat, it is a broken joke, so the picture now
    takes its natural size and the opener runs past the fold on a short screen instead.
    Measured: legible from about 1080px of viewport height, marginal on a 15in laptop.

    The first cap stays and is the load-bearing one.

    The first is the picture's own natural 3:2, which it must never exceed: the figure is as
    wide as `min(56ch, the column)`, so that width over 1.5 is the height at which it is
    showing in full, and past that `cover` starts cropping the sides. `cqw` reads the column
    the figure is actually in, which a flat `37.3333ch` (56ch / 1.5) does not: that is only
    correct while 56ch binds, and on a phone the column is narrower, so the cap came out 94px
    too generous and cropped 29% off the sides.

  */
  max-height: calc(min(56ch, 100cqw) / 1.5);
  margin-bottom: 2.25rem;
}
/*
  `contain`, not `cover`, and the whole frame or nothing.

  These heroes carry composited speech bubbles in the upper third, so cropping the picture
  crops the joke. `cover` trimmed 17% off the top at 1280x800, which cut the top half of both
  bubbles: the page would have shipped punchlines with their heads sliced off, and looked
  merely careless rather than broken.

  So the picture keeps its full 3:2 and gives up width instead of content: the image sizes
  itself from the height the band can spare, and simply gets smaller on a short screen. The
  radius and ring live on the `img`, not the figure, so the figure being wider than the
  picture leaves no visible gap and the left edge still meets the rail. */
.hero-full figure.hero-art img {
  height: 100%;
  width: auto;
  max-width: 100%;
  object-fit: contain;
}

/* On a tall screen the picture hits its cap with height still to spare, and that slack has
   to go somewhere. Two auto margins split it evenly, which sets the whole block down from
   the masthead instead of stranding one 276px void above the byline. The byline still
   lands on the fold, which is the part that matters.

   Both are inert when the band is tight: auto margins take only free space, so at 800px
   tall there is none to take and the figure's own bottom margin holds the gap open. */
.hero-full .kicker,
.hero-full .byline { margin-top: auto; }

/* ---------- the rail ----------

   One grid shared by the hero and the body, so the left rail is reserved down the
   whole page and the hero text sits on the same left edge as the article. The rail
   cannot hang out in the page margin instead: at 1280 the content box starts at
   96px and the rail needs about 240px, so it would run off-screen. Below 62rem the
   rail collapses and both revert to a single full-width column. */

.rail { display: grid; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 62rem) {
  .rail {
    grid-template-columns: 200px minmax(0, 1fr);
    column-gap: clamp(2rem, 5vw, 4.5rem);
    align-items: start;
  }
  .rail > .rail-side { grid-column: 1; }
  .rail > .rail-main { grid-column: 2; }
}

.body-grid { padding-bottom: clamp(3rem, 6vw, 5rem); }
.toc { display: none; }
@media (min-width: 62rem) {
  .toc {
    display: block;
    position: sticky; top: 5.25rem;
    padding-top: clamp(2.5rem, 5vw, 4rem);
  }
}
.toc .label {
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wider); text-transform: uppercase;
  color: var(--muted-foreground); margin-bottom: .875rem;
}
.toc nav { display: flex; flex-direction: column; gap: .5rem;
  border-left: 1px solid var(--border); padding-left: .875rem; }
.toc a { font-size: var(--text-sm); line-height: 1.4; color: var(--muted-foreground); text-decoration: none; }
.toc a:hover { color: var(--primary); }

article.doc { min-width: 0; }

/* ---------- prose ---------- */

section { padding: clamp(2.75rem, 5vw, 4rem) 0 0; scroll-margin-top: 5rem; }

h2 {
  margin: 0 0 1rem;
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
  line-height: 1.15; max-width: 32ch; text-wrap: balance;
}
h3 {
  margin: 2rem 0 .625rem;
  font-size: var(--text-lg); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-snug); color: var(--foreground);
}
p {
  margin: 0 0 1.125rem;
  font-size: 1.0625rem; line-height: 1.7;
  max-width: 68ch; text-wrap: pretty;
}
p:last-child { margin-bottom: 0; }
.lede { font-size: var(--text-xl); line-height: 1.55; color: var(--muted-foreground); }

a { color: var(--primary); text-decoration-thickness: 1px; text-underline-offset: .2em; }
a:hover { color: var(--foreground); }
::selection { background: oklch(from var(--primary) l c h / 0.15); }

ul.plain { list-style: none; padding: 0; margin: 0 0 1.125rem; max-width: 68ch; }
ul.plain > li { position: relative; padding-left: 1.375rem; margin-bottom: .75rem;
  font-size: 1.0625rem; line-height: 1.7; }
ul.plain > li::before {
  content: ""; position: absolute; left: .25rem; top: .7em;
  width: 5px; height: 5px; border-radius: 50%;
  background: oklch(from var(--primary) l c h / 0.45);
}
ul.plain > li:last-child { margin-bottom: 0; }

/* numerals carry weight and tabular alignment instead of a monospace costume */
.fig { font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; }

/* citations sit in the brand colour, quiet but obviously clickable */
.cite {
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  vertical-align: .45em; margin-left: .125rem;
  color: var(--primary); text-decoration: none;
}
.cite:hover, .cite:focus-visible { text-decoration: underline; }

/* ---------- the eyebrow, used on every labelled block ---------- */

.eyebrow {
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wider); text-transform: uppercase;
  color: var(--primary); margin-bottom: .75rem;
}

/* ---------- verdict card ---------- */

.verdict {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  background: var(--card); border-radius: var(--radius-2xl);
  box-shadow: var(--ring-1), var(--shadow-md);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  max-width: 62ch;
}
.verdict p { margin: 0; font-size: var(--text-xl); line-height: 1.55; color: var(--foreground); }

/* ---------- at-a-glance: a cell grid, hairlines made of the gap ---------- */

/* Explicit column counts, not auto-fit. The 1px gap is the hairline, so an
   incomplete final row exposes the grid background as empty grey blocks. With
   four cells, 1 / 2 / 4 all divide evenly and can never orphan one. */
.glance {
  display: grid; grid-template-columns: 1fr;
  gap: 1px; background: var(--border);
  border-radius: var(--radius-xl); overflow: hidden;
  box-shadow: var(--ring-1);
}
@media (min-width: 34rem) { .glance { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 60rem) { .glance { grid-template-columns: repeat(4, 1fr); } }
.glance > div { background: var(--card); padding: 1.25rem 1.25rem 1.375rem; }
.glance dt {
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wider); text-transform: uppercase;
  color: var(--muted-foreground); margin-bottom: .5rem;
}
.glance dd { margin: 0; font-size: var(--text-sm); line-height: 1.45; color: var(--muted-foreground); }
/* a headline figure where the cell has one, with its unit set quieter */
.glance .display {
  display: block; font-size: 1.75rem; font-weight: var(--weight-bold);
  letter-spacing: -0.03em; color: var(--foreground);
  font-variant-numeric: tabular-nums; margin-bottom: .375rem;
}
.glance .display .unit {
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  letter-spacing: normal; color: var(--muted-foreground);
}
/* the cell that carries the argument leans on the brand colour */
.glance .lead-cell dt, .glance .lead-cell .display { color: var(--primary); }
/* the export fades this unit to primary/0.7, which is 2.72:1 on card and fails.
   The unit is already subordinate by scale and weight (14px medium against 28px
   bold), so it keeps full opacity instead. */
.glance .lead-cell .display .unit { color: var(--primary); }

/* Strongest / weakest are judgements, not measurements, so they sit outside the
   metric grid rather than occupying a numeric slot in it. */
.assessment {
  display: grid; gap: 1.5rem 2.5rem; grid-template-columns: 1fr;
  margin-top: 1.75rem; max-width: 68ch;
}
@media (min-width: 40rem) {
  .assessment { grid-template-columns: 1fr 1fr; gap: 0 2.5rem; }
  .assessment > div + div { border-left: 1px solid var(--border); padding-left: 2.5rem; }
}
.assessment dt {
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wider); text-transform: uppercase;
  color: var(--muted-foreground); margin-bottom: .5rem;
}
.assessment dd { margin: 0; font-size: var(--text-base); line-height: 1.5; color: var(--foreground); }

/* ---------- the call: a per-section conclusion, tinted in brand ---------- */

.call {
  margin-top: 1.75rem; max-width: 62ch;
  background: oklch(from var(--primary) l c h / 0.05);
  border-radius: var(--radius-xl);
  padding: 1.25rem 1.375rem;
}
.call .eyebrow { margin-bottom: .375rem; }
.call p {
  margin: 0; font-size: var(--text-base); line-height: 1.6;
  font-weight: var(--weight-medium); color: var(--foreground);
}

/* ---------- tables ---------- */

.tablewrap {
  overflow-x: auto; margin: 0 0 .75rem; -webkit-overflow-scrolling: touch;
  border-radius: var(--radius-xl); box-shadow: var(--ring-1);
}
/* Source notes sit below the card, not inside it. As a <caption> they were both
   cramped against the ring and inside the horizontal scroll container, so they
   dragged sideways with the table. Linked back by aria-describedby. */
.tablenote {
  margin: 0 0 1.5rem; font-size: var(--text-xs); line-height: 1.6;
  color: var(--muted-foreground); max-width: 68ch;
}
/* "Last verified 31 July 2026." A provenance stamp rather than something to read, so it
   sits quieter than the note it follows. Its own element, so the note can be absent and
   the stamp still renders. */
.tablenote .verified:not(:first-child) { margin-left: .25rem; }
table { border-collapse: collapse; width: 100%; min-width: 34rem;
  font-size: var(--text-sm); background: var(--card); }
th, td { text-align: left; padding: .875rem 1rem; border-bottom: 1px solid var(--border);
  vertical-align: top; }
thead th {
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wider); text-transform: uppercase;
  color: var(--muted-foreground); background: var(--muted);
}
tbody th { font-weight: var(--weight-semibold); color: var(--foreground); }
td.num, th.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
tbody tr:last-child th, tbody tr:last-child td { border-bottom: none; }
.tier-name { font-weight: var(--weight-semibold); }
.tier-name small { display: block; font-weight: var(--weight-regular);
  color: var(--muted-foreground); font-size: var(--text-xs); margin-top: .1875rem; }

table.decision td:first-child { width: 46%; }
table.decision .route { font-weight: var(--weight-semibold); color: var(--foreground); }
table.decision .route.us { color: var(--primary); }

/* ---------- quotes ---------- */

.quote { margin: 0 0 1.5rem; max-width: 62ch; }
.quote blockquote {
  margin: 0; padding-left: 1.25rem;
  border-left: 2px solid oklch(from var(--primary) l c h / 0.35);
  font-size: var(--text-lg); line-height: 1.55; color: var(--foreground);
}
.quote figcaption { margin: .625rem 0 0 1.25rem; font-size: var(--text-sm); color: var(--muted-foreground); }
.quote figcaption b, .quote figcaption strong { font-weight: var(--weight-semibold); color: var(--foreground); }

/* ---------- notes ---------- */

.note {
  background: var(--muted); border-radius: var(--radius-xl);
  box-shadow: var(--ring-1);
  padding: 1.25rem 1.375rem; margin: 0 0 1.5rem; max-width: 62ch;
}
.note > :last-child { margin-bottom: 0; }
.note p { font-size: var(--text-sm); line-height: 1.6; }
.note .label {
  display: block; font-size: var(--text-sm); font-weight: var(--weight-semibold);
  color: var(--foreground); margin-bottom: .5rem;
}

/* ---------- the Skippr panel: featured, never ranked ----------
   An inverted --primary block. This is the one place on the page that flips
   ground and figure, which is what earns it attention without a ranked row.
   Shadow rather than ring here: a filled block has no hairline to carry. */

.skippr {
  margin-top: clamp(2rem, 4vw, 3rem);
  scroll-margin-top: 5rem;
  background: var(--primary); color: var(--primary-foreground);
  border-radius: var(--radius-2xl); overflow: hidden;
  box-shadow: var(--shadow-lg);
  max-width: 68ch;
}
.skippr .bar {
  padding: .875rem 1.75rem;
  border-bottom: 1px solid oklch(from var(--primary-foreground) l c h / 0.15);
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wider); text-transform: uppercase;
  color: oklch(from var(--primary-foreground) l c h / 0.7);
}
.skippr .body { padding: clamp(1.5rem, 3vw, 2rem) 1.75rem; }
.skippr h2 {
  margin: 0 0 .875rem; font-size: var(--text-2xl);
  font-weight: var(--weight-semibold); letter-spacing: var(--tracking-tight);
  line-height: 1.2; color: var(--primary-foreground); max-width: none;
}
.skippr p {
  margin: 0 0 1.125rem; font-size: 1.0625rem; line-height: 1.65;
  color: oklch(from var(--primary-foreground) l c h / 0.88);
}
/* the closing line is dimmed, but :not(.model) matters: on the pricing page the
   model paragraph is the last child, and :last-child would otherwise out-specify
   .model and drop it to 0.72 alpha, which is 4.0:1 and fails */
.skippr p:last-child:not(.model) {
  margin-bottom: 0; font-size: var(--text-sm); line-height: 1.6;
  color: oklch(from var(--primary-foreground) l c h / 0.72);
}
.skippr .model:last-child { margin-bottom: 0; }
.skippr .model {
  margin: 0 0 1.125rem; padding: 1rem 1.125rem;
  background: oklch(from var(--primary-foreground) l c h / 0.1);
  border-radius: var(--radius-lg);
  font-size: var(--text-sm); line-height: 1.6;
  color: oklch(from var(--primary-foreground) l c h / 0.92);
}
.skippr .model b, .skippr .model strong { font-weight: var(--weight-semibold); color: var(--primary-foreground); }
/* The relationship label: a sentence, not a badge. It sits between the pricing model and
   the closing line so it reads as part of the disclosure rather than as page furniture.
   The lead-in carries full opacity; the gloss after the colon inherits .skippr p. */
.skippr .rel b, .skippr .rel strong { font-weight: var(--weight-semibold); color: var(--primary-foreground); }
.skippr .cite { color: var(--primary-foreground); }
.skippr a { color: var(--primary-foreground); }

/* ---------- faq ---------- */

.faq { max-width: 62ch; border-top: 1px solid var(--border); }
.faq details { border-bottom: 1px solid var(--border); }
.faq summary {
  cursor: pointer; list-style: none;
  padding: 1.0625rem 2rem 1.0625rem 0;
  font-size: var(--text-base); font-weight: var(--weight-medium);
  color: var(--foreground); position: relative;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; position: absolute; right: .375rem; top: 1.5rem;
  width: 8px; height: 8px;
  border-right: 1.5px solid var(--muted-foreground);
  border-bottom: 1.5px solid var(--muted-foreground);
  transform: rotate(45deg);
  transition: transform .3s cubic-bezier(.16, 1, .3, 1);
}
.faq details[open] summary::after { transform: rotate(-135deg); }
.faq summary:hover { color: var(--primary); }
.faq summary:hover::after { border-right-color: var(--primary); border-bottom-color: var(--primary); }
.faq .answer { padding: 0 0 1.25rem; }
.faq .answer p { font-size: var(--text-base); line-height: 1.65; color: var(--muted-foreground); margin-bottom: .625rem; }
.faq .answer p:last-child { margin-bottom: 0; }

/* ---------- end-stack: the only CTAs on the page ---------- */

.endstack {
  margin-top: clamp(2.75rem, 5vw, 4rem);
  padding: clamp(1.5rem, 3vw, 2rem);
  border-radius: var(--radius-2xl);
  background: var(--muted); box-shadow: var(--ring-1);
  max-width: 62ch;
}
.endstack h2 { font-size: var(--text-xl); margin-bottom: .625rem; max-width: none; }
.endstack p { font-size: var(--text-base); line-height: 1.65; color: var(--muted-foreground); }
.endstack .actions { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1.25rem; }
.endstack a.btn {
  display: inline-flex; align-items: center;
  padding: .625rem 1.125rem; border-radius: var(--radius-lg);
  font-size: var(--text-sm); font-weight: var(--weight-medium); text-decoration: none;
  background: var(--primary); color: var(--primary-foreground);
  border: 1px solid var(--primary);
  transition: background .18s ease, color .18s ease;
}
.endstack a.btn:hover { background: transparent; color: var(--primary); }
.endstack a.btn.ghost {
  background: var(--card); color: var(--foreground); border-color: var(--border);
}
.endstack a.btn.ghost:hover { background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); }

/* ---------- mesh ---------- */

.mesh { margin-top: clamp(2.5rem, 5vw, 3.5rem); border-top: 1px solid var(--border); padding-top: 1.5rem; }
.mesh h2 { font-size: var(--text-base); margin-bottom: 1rem; }
.mesh ul { list-style: none; padding: 0; margin: 0;
  display: grid; gap: .625rem 2rem; grid-template-columns: 1fr; }
@media (min-width: 44rem) { .mesh ul { grid-template-columns: 1fr 1fr; } }
.mesh li { font-size: var(--text-sm); }
.mesh .kind { color: var(--muted-foreground); }
/* a mesh target that is in the manifest but not built yet. Rendered as text, not
   a link, so the page ships with no dead internal hrefs. */
.mesh .pending { color: var(--foreground); }
/* the separator is presentation, so it lives here and not in the markup, which
   keeps scripts/no_em_dashes.py able to gate page copy at zero */
.mesh .kind::before { content: "\2014\00a0"; }
.mesh .soon { font-style: italic; }

/* ---------- sources ---------- */

.sources { border-top: 2px solid var(--foreground); margin-top: clamp(3rem, 6vw, 5rem); }
.sources ol { padding-left: 0; list-style: none; counter-reset: s; margin: 0; }
.sources li {
  counter-increment: s; position: relative;
  padding: .875rem 0 .875rem 2.5rem; border-bottom: 1px solid var(--border);
  font-size: var(--text-sm); line-height: 1.5;
}
.sources li::before {
  content: counter(s) "."; position: absolute; left: .375rem; top: .875rem;
  font-size: var(--text-sm); font-weight: var(--weight-semibold);
  color: var(--muted-foreground); font-variant-numeric: tabular-nums;
}
.sources li:target { background: oklch(from var(--primary) l c h / 0.06); }
.sources a { word-break: break-word; }
.sources .meta { display: block; font-size: var(--text-xs); color: var(--muted-foreground); margin-top: .1875rem; }

/* ---------- footer ---------- */

footer.foot {
  border-top: 1px solid var(--border); background: var(--muted);
  padding: 2rem 0 4rem;
  font-size: var(--text-sm); color: var(--muted-foreground);
}
footer.foot p { max-width: 62ch; margin-bottom: .625rem; font-size: var(--text-sm); }
footer.foot .stamp { font-size: var(--text-xs); }

/* ---------- utility ---------- */

.pair { display: grid; gap: clamp(1.5rem, 4vw, 3rem); grid-template-columns: 1fr; }
@media (min-width: 56rem) { .pair { grid-template-columns: 1fr 1fr; } }
.pair > div > h3:first-child { margin-top: 0; }

.checked { display: block; margin: 0 0 1.75rem; font-size: var(--text-sm); color: var(--muted-foreground); }
.checked b, .checked strong { font-weight: var(--weight-semibold); color: var(--foreground); }

:focus-visible { outline: none; box-shadow: var(--ring-focus); border-radius: var(--radius-sm); }
.skip { position: absolute; left: -9999px; top: 0;
  background: var(--primary); color: var(--primary-foreground); padding: .75rem 1rem; z-index: 30; }
.skip:focus { left: .5rem; top: .5rem; }
