/* =========================================================================
   Lapka — the marketing site.

   One stylesheet, three pages, no build step. Every value here is ported from
   the app's own token layer rather than invented; the source is named beside it
   so a future change to `apps/mobile/src/theme/` can be traced here.

   Tier 1  apps/mobile/src/theme/tokens.ts
   Tier 2  apps/mobile/src/theme/build-theme.ts
   Palette apps/mobile/src/theme/palettes.ts  (the app ships four; this is the
           default, `teal`, which is also the app icon's palette)
   ========================================================================= */

/* --------------------------------------------------------------------------
   0 · Type — Geologica, self-hosted.

   ONE family name, four `font-weight` values. The app registers four *family
   names* instead, and that pattern must not be ported: it works around a React
   Native behaviour (a custom family plus a weight gives faux-bold on Android
   and a silently ignored weight on iOS) that browsers do not have. Given four
   @font-face rules a browser picks the right file and synthesises nothing.

   The files are copied from node_modules/@expo-google-fonts/geologica, under
   the SIL Open Font License 1.1. fonts/OFL.txt is the licence, verbatim.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: Geologica;
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url("fonts/Geologica_400Regular.ttf") format("truetype");
}
@font-face {
  font-family: Geologica;
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  src: url("fonts/Geologica_500Medium.ttf") format("truetype");
}
@font-face {
  font-family: Geologica;
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  src: url("fonts/Geologica_600SemiBold.ttf") format("truetype");
}
@font-face {
  font-family: Geologica;
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  src: url("fonts/Geologica_700Bold.ttf") format("truetype");
}

/* --------------------------------------------------------------------------
   1 · Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Ink — tokens.ts:31-54 */
  --ink900: #15161a; /* colors.text */
  --slate600: #4e6167; /* colors.textMuted */

  /* Accent — palettes.ts `teal`, DEFAULT_PALETTE_ID */
  --accent: #147b82; /* teal700 */
  --accent-pressed: #0c676d; /* teal800 */
  --accent-surface: #ddf9fb; /* teal050 */
  --accent-muted: rgba(20, 123, 130, 0.22); /* build-theme.ts — the unwalked path */

  /* The field — palettes.ts:56 */
  --ground: #f6fafa;

  /* Glass fills — tokens.ts:223-256 */
  --glass-thin: rgba(255, 255, 255, 0.5);
  --glass-regular: rgba(255, 255, 255, 0.62);
  --glass-thick: rgba(255, 255, 255, 0.78);

  /* The edge — tokens.ts:245-250. Not an even hairline: bright on top AND
     left, shaded right and bottom. "Brighter on top than at the bottom is what
     reads as thickness rather than as a border." */
  --edge-highlight: rgba(255, 255, 255, 0.55);
  --edge-shade: rgba(15, 23, 32, 0.06);

  /* Radii — tokens.ts:279-287. Glass wants a larger radius than a solid block,
     because the refraction at the edge needs room to be visible. */
  --r-control: 26px;
  --r-card: 30px;
  --r-sheet: 36px;

  /* Space — tokens.ts, the 4pt scale */
  --s-xs: 4px;
  --s-sm: 8px;
  --s-md: 12px;
  --s-lg: 16px;
  --s-xl: 24px;
  --s-xxl: 32px;
  --s-xxxl: 48px;

  /* Motion — tokens.ts. The bezier is an APPROXIMATION of theme.motion.spring
     and is flagged as one: RN springs are physical, CSS beziers are not. */
  --settle: 220ms cubic-bezier(0.2, 0.9, 0.25, 1.05);
  --answer: 110ms cubic-bezier(0.2, 0.9, 0.25, 1.05);

  /* Layout */
  --page: 1180px;
  --pane: 402px; /* the app's design width on an iPhone 17 Pro, 1:1 in CSS px */
  --measure: 68ch;

  color-scheme: light; /* All four app palettes are light. There is no dark
                          Lapka, so there is no dark site — see the README. */
}

/* --------------------------------------------------------------------------
   2 · The field

   ONE fixed layer, sized to the VIEWPORT and never to the document.

   The composition is fractional (tokens.ts:265-277 gives cx/cy/r as fractions),
   which is what holds it from an SE to a Pro Max. Painted across a 6,000px
   document it stretches into meaningless bands; repeated per section it gives
   ten identical compositions. Fixed, the page scrolls OVER a stationary field
   and every block's tint changes as it travels — which is the one thing the web
   gets that the phone cannot.

   Layer order is REVERSED from the SVG in ambient-background.tsx, because CSS
   paints the first background-image on top and SVG paints in array order.
   The 55% midpoint stop is stopOpacity × 0.3, straight from that file — it is
   what keeps the falloff from banding over a large area.

   Do NOT strengthen these opacities. tokens.ts:262-264: they are low on purpose,
   so the field gives glass something to bend without competing with content.
   -------------------------------------------------------------------------- */

html {
  background: var(--ground); /* so overscroll never flashes white */
}

.field {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--ground);
  background-image:
    /* deep — cx .50 cy 1.12 r 1.00 @ .42 · #147B82 */
    radial-gradient(
      ellipse 100% 100% at 50% 112%,
      rgba(20, 123, 130, 0.42) 0%,
      rgba(20, 123, 130, 0.126) 55%,
      rgba(20, 123, 130, 0) 100%
    ),
    /* warm — cx 1.06 cy .30 r .72 @ .60 · #C9E86B */
      radial-gradient(
        ellipse 72% 72% at 106% 30%,
        rgba(201, 232, 107, 0.6) 0%,
        rgba(201, 232, 107, 0.18) 55%,
        rgba(201, 232, 107, 0) 100%
      ),
    /* cool — cx .02 cy -.04 r .90 @ .72 · #5FD7DE */
      radial-gradient(
        ellipse 90% 90% at 2% -4%,
        rgba(95, 215, 222, 0.72) 0%,
        rgba(95, 215, 222, 0.216) 55%,
        rgba(95, 215, 222, 0) 100%
      );
}

/* --------------------------------------------------------------------------
   3 · Base
   -------------------------------------------------------------------------- */

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

body {
  margin: 0;
  font-family: Geologica, system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 17px;
  line-height: 27px;
  color: var(--ink900);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden; /* the body never scrolls sideways */
}

img {
  max-width: 100%;
  display: block;
}

/* Table and column names appear in the privacy notice, and a lawyer reading it
   needs to see which words are identifiers. A tinted pill rather than a
   monospace face: the page has one typeface and adding a second for four words
   would be louder than the words. */
code {
  font-family: inherit;
  font-weight: 500;
  font-size: 0.94em;
  background: var(--accent-surface);
  color: var(--accent-pressed);
  border-radius: 6px;
  padding: 1px 6px;
  white-space: nowrap;
}

a {
  color: var(--accent-pressed);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

a:hover {
  color: var(--accent);
}

/* Keyboard focus is the ONE place an accent ring is correct here.
   The app's rule is "selection is light, not a ring" (build-theme.ts:169-192) —
   but that governs a *selection state*. This is a browser affordance, and it
   has to be unmistakable. Do not "correct" it to match the app. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--accent);
  color: #fff;
  padding: 12px 20px;
  border-radius: 0 0 var(--r-control) 0;
  z-index: 20;
}
.skip:focus {
  left: 0;
}

/* --------------------------------------------------------------------------
   4 · Glass

   NO ANCESTOR of a glass block may carry `transform`, `filter`, `will-change`
   or `contain`. Any one of those makes that ancestor the backdrop root, and the
   glass silently stops sampling the field — it is the number-one way this
   effect breaks on the web, and it fails without an error.

   The blur numbers are not guessed. expo-blur's own web renderer is
   `saturate(180%) blur(intensity * 0.2px)` (node_modules/expo-blur/build/
   BlurView.web.js:42), applied to the app's intensities 24 / 40 / 68.

   NO box-shadow on glass, ever. GlassSurface applies none, and the reason is
   written down in build-theme.ts:225-244: "A shadow was never really honest
   here anyway. You can see there is nothing solid there to cast one."
   -------------------------------------------------------------------------- */

.glass {
  position: relative;
  border-radius: var(--r-card);
  background-color: var(--glass-thin);
  -webkit-backdrop-filter: saturate(180%) blur(4.8px);
  backdrop-filter: saturate(180%) blur(4.8px);
  border: 1px solid var(--edge-shade);
  border-top-color: var(--edge-highlight);
  border-left-color: var(--edge-highlight);
}

.glass--regular {
  background-color: var(--glass-regular);
  -webkit-backdrop-filter: saturate(180%) blur(8px);
  backdrop-filter: saturate(180%) blur(8px);
}

.glass--thick {
  background-color: var(--glass-thick);
  -webkit-backdrop-filter: saturate(180%) blur(13.6px);
  backdrop-filter: saturate(180%) blur(13.6px);
}

/* Without this, older Firefox paints the fill alone and every block reads as
   washed-out paper rather than as a surface. Raising the fill keeps them
   surfaces; measured, muted text at 0.72 alone still clears 4.5:1. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass {
    background-color: rgba(255, 255, 255, 0.72);
  }
  .glass--regular {
    background-color: rgba(255, 255, 255, 0.8);
  }
  .glass--thick {
    background-color: rgba(255, 255, 255, 0.88);
  }
}

/* GLASS NEVER NESTS. A backdrop-filter inside a backdrop-filter samples the
   outer element's already-composited output: it compounds cost and produces a
   milky patch that no longer relates to the field.

   So a surface inside a surface uses the app's own answer — `state.selected`
   from build-theme.ts:245: a pane of the SAME glass rather than a new one.
   Full-strength specular top edge, lower fill, no filter, no shadow. */
.pane-inset {
  background: rgba(255, 255, 255, 0.4);
  border: 1px solid var(--edge-shade);
  border-top-color: rgba(255, 255, 255, 1);
  border-radius: var(--r-control);
  padding: var(--s-lg) var(--s-xl);
}

/* The seam — build-theme.ts:271-291. TWO hairlines, not one, and both ends of
   each gradient are stated so the rule fades to nothing instead of terminating
   on a hard stop or passing through grey. */
.seam {
  height: 2px;
  margin: var(--s-xxl) 0;
  position: relative;
}
.seam::before,
.seam::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
}
.seam::before {
  top: 0;
  background: linear-gradient(
    90deg,
    rgba(21, 22, 26, 0) 0%,
    rgba(21, 22, 26, 0.26) 18%,
    rgba(21, 22, 26, 0.26) 82%,
    rgba(21, 22, 26, 0) 100%
  );
}
.seam::after {
  top: 1px;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.85) 18%,
    rgba(255, 255, 255, 0.85) 82%,
    rgba(255, 255, 255, 0) 100%
  );
}

/* --------------------------------------------------------------------------
   5 · Layout
   -------------------------------------------------------------------------- */

.page {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--s-xl); /* space.xl — the app's screen gutter */
}

.band {
  padding: var(--s-xxl) var(--s-xl);
  margin-bottom: var(--s-xxxl); /* at least 48px of bare field between blocks */
}

/* The header capsule floats over the page, so an anchored block has to stop
   clear of it rather than under it. */
[id] {
  scroll-margin-top: 96px;
}

@media (min-width: 720px) {
  .band {
    padding: 40px;
  }
}

.band--narrow {
  max-width: 820px;
  margin-left: auto;
  margin-right: auto;
}

.prose {
  max-width: var(--measure);
}

.prose > p {
  margin: 0 0 var(--s-lg);
}
.prose > p:last-child {
  margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   6 · Type scale

   The app's ratios, expressed relatively so they survive being scaled up.
   Tracking −0.018em / −0.01em are the app's −0.5 @ 28 and −0.2 @ 20.

   ONE deviation, stated: body runs 17/27 rather than the app's 15/22. Fifteen
   is right at phone reading distance and thin on a desktop page at arm's
   length. The step relationships and the tracking are kept.
   -------------------------------------------------------------------------- */

h1,
h2,
h3 {
  margin: 0 0 var(--s-md);
  font-weight: 700;
}

h1 {
  font-size: clamp(34px, 4.6vw, 50px);
  line-height: 1.1;
  letter-spacing: -0.018em;
}

h2 {
  font-size: clamp(26px, 3vw, 32px);
  line-height: 1.2;
  letter-spacing: -0.018em;
}

h3 {
  font-size: 20px;
  line-height: 26px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* The app's eyebrows are `caption` weight in the accent and are NOT uppercase
   ("Today", "What we're going on"). Uppercasing them here would also print
   "IOS" for "iOS", which is the kind of detail that gives a page away. */
.eyebrow {
  font-size: 13px;
  line-height: 18px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--accent-pressed);
  margin: 0 0 var(--s-sm);
}

.lead {
  font-size: 19px;
  line-height: 29px;
  margin: 0 0 var(--s-lg);
}

.muted {
  color: var(--slate600);
}

.caption {
  font-size: 13px;
  line-height: 19px;
  color: var(--slate600);
}

.numeral {
  font-size: 13px;
  line-height: 18px;
  font-weight: 600;
  color: var(--accent-pressed);
  letter-spacing: 0.04em;
  margin: 0 0 var(--s-xs);
}

/* CONTRAST, measured rather than assumed.

   Worst case on this page is text on `thin` glass over the strongest reachable
   part of the deep wash. The deep blob sits at cy 112% with ry 100%, so the
   viewport's bottom edge is 12% along the ramp — the reachable alpha is ~0.356,
   not the 0.42 at its centre, which is off-canvas.

   Composited, worst case:
     ink900   #15161A on thin ....... ~14.6 : 1   ✓
     slate600 #4E6167 on thin .......  ~5.25: 1   ✓
     accent   #147B82 on thin .......  ~4.05: 1   ✗  — fails body text
     teal800  #0C676D on thin .......  ~5.34: 1   ✓

   Two rules follow, and I would have got both wrong by eye:

   1. LINK TEXT IS teal800 (`--accent-pressed`), never teal700. The app's own
      TextLink is `accent`, and on the app's bare rendered field that measures
      fine; a web block puts a 50% white fill in between and it does not.
   2. NOTHING BUT ink900 SITS ON THE BARE FIELD. slate600 there measures ~3.79
      and fails. Where text sits outside a block its softness comes from weight
      and size, never from colour — see `.on-field` below. */

.on-field {
  color: var(--ink900);
}
.on-field .muted {
  color: var(--ink900);
  opacity: 0.78; /* opacity over the field composites toward ink, not grey */
}

/* --------------------------------------------------------------------------
   7 · Header — a capsule sized to its contents, not a navbar

   The app's floating tab bar hugs its tabs (~340pt). Stretched to a 1180px
   column with a wordmark at one end and a link at the other it stops being the
   app's capsule and becomes a website navbar with a big radius. So this is
   inline-flex, centred, and only as wide as what is in it.
   -------------------------------------------------------------------------- */

.header {
  position: sticky;
  top: var(--s-md);
  z-index: 10;
  display: flex;
  justify-content: center;
  padding: var(--s-md) var(--s-xl) 0;
  /* No transform, filter, will-change or contain here or on any ancestor —
     see the note at §4. `position: sticky` is safe; `transform` is not. */
}

.header__bar {
  display: inline-flex;
  align-items: center;
  gap: var(--s-xl);
  border-radius: var(--r-control);
  padding: var(--s-sm) var(--s-lg) var(--s-sm) var(--s-xl);
  max-width: 100%;
  flex-wrap: wrap;
  justify-content: center;
  row-gap: var(--s-xs);
}

.wordmark {
  font-size: 20px;
  line-height: 28px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink900);
  text-decoration: none;
}

.header__link {
  font-size: 15px;
  line-height: 22px;
  font-weight: 600;
}

/* --------------------------------------------------------------------------
   8 · The screenshot pane — and the page's only shadow

   Not a picture of a phone: no bezel, no notch, no hand, no tilt. A pane
   wearing the app's own sheet radius, so it reads as a piece of the app rather
   than as a photograph of one.

   It carries `shadow.control` (tokens.ts:86-92), and it is the ONLY shadow on
   the site. The reason is an argument rather than a flourish: glass casts no
   shadow because there is nothing solid there to cast one. A screen is solid,
   so it does.
   -------------------------------------------------------------------------- */

.shot {
  width: 100%;
  max-width: var(--pane);
  margin: 0;
}

.shot img {
  width: 100%;
  height: auto;
  border-radius: var(--r-sheet);
  background: var(--ground); /* never a white flash while it loads */
  border: 1px solid var(--edge-shade);
  border-top-color: var(--edge-highlight);
  border-left-color: var(--edge-highlight);
  box-shadow: 0 8px 16px rgba(11, 18, 32, 0.14);
}

.shot figcaption {
  margin-top: var(--s-md);
  font-size: 13px;
  line-height: 19px;
  color: var(--slate600);
}

/* Two columns, alternating, from the first pane block onwards. Below 1080 the
   1:1 relationship between the pane and the page breaks, so it stacks — and
   the pane always follows the text, because alternation only reads as rhythm
   when the columns are side by side. */
.split {
  display: grid;
  gap: var(--s-xxl);
  align-items: start;
}

@media (min-width: 1080px) {
  .split {
    grid-template-columns: minmax(0, 1fr) var(--pane);
    gap: 56px;
    /* Centred, not top-aligned. A phone screenshot is ~870px tall and a text
       column beside it is rarely more than 500, so `start` opens an L-shaped
       void of empty glass under the text — the same failure CLAUDE.md names
       when it forbids pinning an action with `marginTop: auto`. Centring
       distributes the difference and reads as composition. */
    align-items: center;
  }
  .split--flip {
    grid-template-columns: var(--pane) minmax(0, 1fr);
  }
  .split--flip .split__text {
    order: 2;
  }
  .split--flip .shot {
    order: 1;
  }
}

/* --------------------------------------------------------------------------
   9 · Components
   -------------------------------------------------------------------------- */

/* A status row. The `accentMuted` left rule is the app's token for an unwalked
   path (build-theme.ts:329-339) — a translucent accent, used because the
   neutral greys read PINK as a long line over a mint field. */
.rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-md);
}

.row {
  border-radius: var(--r-control);
  padding: var(--s-lg) var(--s-xl);
  background: rgba(255, 255, 255, 0.4);
  border: 1px solid var(--edge-shade);
  border-top-color: rgba(255, 255, 255, 1);
}

/* `accentMuted` — the app's token for a path not yet walked. It exists because
   the neutral greys read PINK as a long line over a mint field (simultaneous
   contrast, build-theme.ts:329-339). Three pixels rather than two: at two it
   disappears against the pane's own fill. */
.row--notyet {
  border-left: 3px solid var(--accent-muted);
}

.row h3 {
  font-size: 17px;
  line-height: 24px;
  margin-bottom: var(--s-xs);
}

.row p {
  margin: 0;
  font-size: 15px;
  line-height: 23px;
  color: var(--slate600);
}

/* A quotation and the thing that weakens it, in the same pane. Never a
   footnote: a sample size in a footnote is a sample size nobody reads. */
.quote {
  margin: 0 0 var(--s-lg);
}

.quote blockquote {
  margin: 0;
  font-size: 19px;
  line-height: 29px;
  font-weight: 500;
}

.quote cite {
  display: block;
  margin-top: var(--s-md);
  font-style: normal;
  font-size: 13px;
  line-height: 19px;
  color: var(--slate600);
}

.refusals {
  display: grid;
  gap: var(--s-xl);
}

@media (min-width: 900px) {
  .refusals {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* The vet refusal spans both columns. It is the most consequential block on
     the page and it is four times the length of the shortest one; two-up would
     put a safety statement in half a column beside a one-liner. */
  .refusals > .refusal--wide {
    grid-column: 1 / -1;
  }
}

.refusal h3 {
  margin-bottom: var(--s-sm);
}

.refusal p {
  margin: 0 0 var(--s-md);
  font-size: 16px;
  line-height: 25px;
}

.refusal p:last-child {
  margin-bottom: 0;
}

.q-list {
  margin: 0 0 var(--s-lg);
  padding-left: var(--s-xl);
}

.q-list li {
  margin-bottom: var(--s-sm);
}

.footer {
  margin-bottom: var(--s-xxxl);
  font-size: 15px;
  line-height: 23px;
}

.footer p {
  margin: 0 0 var(--s-sm);
  color: var(--slate600);
}

.footer p:first-child {
  color: var(--ink900);
  font-weight: 500;
}

.footer nav {
  margin: var(--s-md) 0;
  display: flex;
  gap: var(--s-lg);
  flex-wrap: wrap;
}

/* --------------------------------------------------------------------------
   10 · The legal pages
   -------------------------------------------------------------------------- */

.legal .band {
  max-width: 860px;
  margin-left: auto;
  margin-right: auto;
}

.legal .prose,
.legal p,
.legal li {
  max-width: var(--measure);
}

.legal h2 {
  font-size: 24px;
  line-height: 32px;
}

.legal section > p,
.legal section > ul {
  font-size: 16px;
  line-height: 26px;
  color: var(--slate600);
}

.legal section > p.first {
  font-size: 18px;
  line-height: 28px;
  color: var(--ink900);
}

.legal h2 a {
  text-decoration: none;
  opacity: 0;
  font-weight: 400;
  margin-left: var(--s-sm);
}

.legal h2:hover a,
.legal h2 a:focus-visible {
  opacity: 1;
}

/* "Not decided yet" — a component, because a policy with countable holes is
   more use to the lawyer who has to fill them than boilerplate that hides them.
   It wears `accentMuted`, the app's colour for a path not yet walked, so an
   undecided clause is recognisable by colour alone while scrolling. */
.undecided {
  border-left: 2px solid var(--accent-muted);
  padding: 2px 0 2px var(--s-lg);
  margin: var(--s-lg) 0;
}

.undecided b {
  display: block;
  font-size: 13px;
  line-height: 18px;
  font-weight: 600;
  color: var(--accent-pressed);
  letter-spacing: 0.01em;
}

.undecided span {
  display: block;
  font-size: 15px;
  line-height: 23px;
  color: var(--ink900);
  margin-top: var(--s-xs);
}

.undecided span + span {
  color: var(--slate600);
}

/* "Decided" — the same component pointed the other way, added 2026-08-12 when
   the first two of these questions got answers.

   It exists so that an answer is not delivered by *silence*. Deleting an
   `.undecided` block would make the page quietly shorter, and a reader who came
   back to it could not tell a decision from an omission — which on a privacy
   page is the difference between a promise and a thing somebody stopped
   mentioning. So a settled question keeps its place in the count and changes
   colour instead.

   The rule is full `--accent` rather than `--accent-muted`, which is the app's
   own distinction and not a decorative one: muted is the path not yet walked
   (build-theme.ts:329-339), and these have been. Same geometry as `.undecided`
   so the two read as one pair down the left-hand edge. */
.settled {
  border-left: 2px solid var(--accent);
  padding: 2px 0 2px var(--s-lg);
  margin: var(--s-lg) 0;
}

.settled b {
  display: block;
  font-size: 13px;
  line-height: 18px;
  font-weight: 600;
  color: var(--accent-pressed);
  letter-spacing: 0.01em;
}

.settled span {
  display: block;
  font-size: 15px;
  line-height: 23px;
  color: var(--ink900);
  margin-top: var(--s-xs);
}

.settled span + span {
  color: var(--slate600);
}

.toc {
  margin-bottom: var(--s-xxxl);
}

.toc summary {
  cursor: pointer;
  font-weight: 600;
  padding: var(--s-md) 0;
}

.toc ol {
  margin: var(--s-sm) 0 0;
  padding-left: var(--s-xl);
  columns: 2;
  column-gap: var(--s-xxl);
}

.toc li {
  margin-bottom: var(--s-xs);
  break-inside: avoid;
  font-size: 15px;
  line-height: 23px;
}

@media (max-width: 640px) {
  .toc ol {
    columns: 1;
  }
}

/* --------------------------------------------------------------------------
   11 · Motion

   Hover strengthens the EDGE, never the fill — build-theme.ts's rule, and the
   reason a hard accent border is wrong on glass: it is the only crisp, opaque
   line on a page made of diffuse edges.

   No scroll-reveal animation. On a page whose whole effect is blocks travelling
   across a fixed field, a fade-in fights the thing it would be decorating —
   and it would need JavaScript, which this site does not have.
   -------------------------------------------------------------------------- */

/* Nothing on this page is a glass control, so nothing gets a hover state. An
   inert block that lights up under the cursor promises an interaction it does
   not have, which is worse than being still. The rule survives here as a note:
   when there IS a glass control, hover strengthens the edge (0.55 → 0.95 over
   `--settle`) and never the fill. */

.header__link,
.wordmark,
.footer a,
.legal a {
  transition: color var(--answer);
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
    scroll-behavior: auto !important;
  }
}
