/* TripStacker site v2 · Cerulean paper (light) with one Pacific glass (dark) section.
   Tokens verbatim from client/constants/theme3.ts. Motion: timing-only,
   cubic-bezier(.22,.61,.36,1). No red. No mint. */

/* Fonts are self-hosted. No request leaves this origin to render type, which is
   why the privacy policy can say the site contacts no third party on page load.
   Files came from Google Fonts (Inter v20, Newsreader v26), both under the SIL
   Open Font License 1.1, kept in css/fonts/.

   These are the variable builds: one file spans the whole weight range instead
   of five static cuts, and Newsreader keeps its 6..72 optical-size axis so
   display headings render exactly as they did on the hosted version.

   The latin and latin-ext faces are split on unicode-range, as Google splits
   them. Current copy is latin only (the middot is U+00B7), so latin-ext sits
   there unfetched until a character needs it. Cyrillic, Greek and Vietnamese
   were not downloaded. */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("fonts/inter-latin-var.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("fonts/inter-latin-ext-var.woff2") format("woff2");
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Newsreader is upright only. The site has no italics: no font-style: italic in
   any stylesheet, no <em> or <i> in any page. The italic face Google was being
   asked for was never used, so it is not hosted. */
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("fonts/newsreader-latin-var.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("fonts/newsreader-latin-ext-var.woff2") format("woff2");
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
    U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --bg: #f4f4f8;
  --wash1: #eef1f7;
  --wash2: #f2f4f9;
  --wash3: #f6f7fb;
  --ink: #141b28;
  --ink-rgb: 20 27 40;
  --ink2: #4c5666;
  --ink3: #88909e; /* theme3 token. Decorative use only (aria-hidden hero fragments,
                      separators). Fails AA for body text on the wash, so nothing a
                      sighted reader is asked to read may use it. Use --ink3-text. */
  --ink3-text: #626c7a; /* site-only darker ink3 for readable small text (>=4.5:1) */
  --card: #ffffff;

  /* One definition of each brand colour. The channel triples exist so every
     derived alpha is written as rgb(var(--accent-rgb) / .35) instead of being
     retyped as a literal, which is how the two CTA gradients drifted apart. */
  --accent-rgb: 34 116 165;
  --cyan-rgb: 42 183 202;
  --accent: rgb(var(--accent-rgb));
  --cyan: rgb(var(--cyan-rgb));
  --accent-shadow: rgb(var(--accent-rgb) / 0.28);
  --pill-bg: rgb(var(--accent-rgb) / 0.13);
  --on-accent: #ffffff;
  --hairline: rgba(30, 50, 80, 0.08);
  --hairline-invert: rgba(255, 255, 255, 0.16);
  --meter-track: #e5e8ef;
  --amber-text: rgb(154, 103, 24);

  /* Pacific (dark section): ink values verbatim from theme3.ts Pacific.
     Exception: p-ink3 alpha lifted 0.48 -> 0.56 so the mono receipt lines
     clear AA contrast on the site's gradient. */
  --p-bg: #0a1418;
  --p-bg-rgb: 10 20 24;
  --p-ink: #f4f0fb;
  --p-ink2: rgba(244, 240, 251, 0.68);
  --p-ink3: rgba(244, 240, 251, 0.56);
  --p-card: rgba(255, 255, 255, 0.055);
  --p-hairline: rgba(255, 255, 255, 0.08);
  --p-hairline-soft: rgba(255, 255, 255, 0.06);
  --p-accent-text: rgb(96, 208, 222);

  /* The one CTA gradient in the build. The scrim is not decoration: white text
     on bare cyan is 2.4:1, and the same two stops with this scrim measure
     4.86:1 at the cyan end and 8.98:1 at the accent end. Every white-on-brand
     surface (buttons, nav CTA, the close field) uses this pairing. */
  --cta-scrim: rgb(var(--p-bg-rgb) / 0.36);
  --grad-cta:
    linear-gradient(var(--cta-scrim), var(--cta-scrim)),
    linear-gradient(90deg, var(--cyan), var(--accent));

  /* Radii, from design/design-system/tokens.css. Only the rungs this site
     actually uses are declared; the app-only rungs (roux row 22, kind icon 13)
     are deliberately absent rather than sitting here unconsumed. */
  --r-phone: 44px;
  --r-hero: 30px;
  --r-card: 26px;
  --r-row: 20px;
  --r-cta: 16px;
  --r-chip: 14px;
  --r-mark: 12px;
  --r-meter: 6px;
  --r-focus: 6px;
  --r-pill: 999px;

  /* spacing, 4px base */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-5: 24px;
  --s-6: 32px;
  --s-8: 64px;
  --s-section: 96px;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --sans: "Inter", system-ui, -apple-system, sans-serif;
  --serif: "Newsreader", Georgia, serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --shadow-hero: 0 14px 40px rgba(40, 40, 70, 0.12), 0 2px 6px rgba(40, 40, 70, 0.05);
  --shadow-card: 0 10px 30px rgba(40, 40, 70, 0.1);
  --shadow-row: 0 6px 18px rgba(40, 40, 70, 0.08);
  --shadow-chip: 0 3px 10px rgba(40, 40, 60, 0.1);
  --shadow-nav: 0 4px 16px rgba(40, 40, 70, 0.07);
  --shadow-nav-scrolled: 0 10px 30px rgba(40, 40, 70, 0.13);
  --shadow-cta: 0 8px 22px var(--accent-shadow);
  --shadow-cta-hover: 0 10px 26px var(--accent-shadow);
  --shadow-cta-sm: 0 4px 14px var(--accent-shadow);
  --shadow-cta-sm-hover: 0 6px 18px var(--accent-shadow);
  /* theme3 Pacific heroSh */
  --p-shadow-hero: 0 20px 44px rgba(0, 0, 0, 0.4);
  --shadow-invert: 0 10px 26px rgba(0, 0, 0, 0.22);
}

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

html { scroll-behavior: smooth; }
/* the home page carries a fixed pill nav, so in-page anchors need clearance.
   Subpages use .site-header.static-header and want no offset. */
html.home { scroll-padding-top: 96px; }
@media (max-width: 640px) { html.home { scroll-padding-top: 84px; } }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0ms !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
  }
}

body {
  font-family: var(--sans);
  background-color: var(--bg);
  background-image: linear-gradient(180deg, var(--wash1) 0%, var(--wash2) 44%, var(--wash3) 100%);
  color: var(--ink);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
a:focus-visible, button:focus-visible, input:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-focus);
}
/* the skip-link target. Focusable for AT, but never draws a ring on click. */
main:focus { outline: none; }

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

/* screen-reader-only text. Replaces the inline clip hack. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 12px;
  top: -60px;
  z-index: 60;
  background: var(--card);
  color: var(--accent);
  border: 1px solid var(--hairline);
  border-radius: var(--r-cta);
  box-shadow: var(--shadow-card);
  padding: 10px 18px;
  font-size: 0.9rem;
  font-weight: 600;
  transition: top 150ms var(--ease);
}
.skip-link:focus { top: 12px; text-decoration: none; }

.wrap { max-width: 1080px; margin: 0 auto; padding: 0 24px; }
.wrap-narrow { max-width: 780px; margin: 0 auto; padding: 0 24px; }

/* ---------- type ---------- */

.kicker {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}

.serif-h {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.8rem, 4.2vw, 2.6rem);
  line-height: 1.18;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.section-sub {
  color: var(--ink2);
  font-size: 1.02rem;
  max-width: 34em;
}

.num { font-variant-numeric: tabular-nums; }

/* ---------- header ---------- */

.site-header {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 30;
  padding: 18px 0;
}
.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.brand-lockup {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}
.brand-lockup:hover { text-decoration: none; }
.brand-lockup .bm { width: 30px; height: 30px; color: var(--accent); }

.site-nav { display: flex; align-items: center; gap: 22px; }
/* inline-flex with a 44px floor, not a bare inline link: a 0.88rem link is a
   21px-tall tap target, and every one of these sits next to another one. */
.site-nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--ink2);
  font-size: 0.88rem;
  font-weight: 500;
  transition: color 150ms var(--ease);
}
.site-nav a:hover { color: var(--ink); text-decoration: none; }
.site-nav a[aria-current="page"] { color: var(--ink); }
.site-nav .nav-cta {
  color: var(--accent);
  background: var(--pill-bg);
  padding: 8px 16px;
  border-radius: var(--r-pill);
}
.site-nav .nav-cta:hover { color: var(--accent); }
@media (max-width: 640px) {
  .site-nav a:not(.nav-cta) { display: none; }
}

/* subpages: header sits on the wash, not a hero */
.site-header.static-header { position: static; }

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 0.95rem;
  padding: 14px 26px;
  border-radius: var(--r-cta);
  border: none;
  cursor: pointer;
  color: var(--on-accent);
  background: var(--grad-cta);
  box-shadow: var(--shadow-cta);
  transition: transform 150ms var(--ease), box-shadow 150ms var(--ease);
}
.btn:hover {
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: var(--shadow-cta-hover);
}
.btn-quiet {
  background: var(--card);
  color: var(--accent);
  box-shadow: var(--shadow-chip);
  border: 1px solid var(--hairline);
}

/* ---------- generic sections ---------- */

.section { padding: var(--s-section) 0; }
.section-head { margin-bottom: 40px; }
.section-head .kicker { display: block; margin-bottom: 12px; }
.section-head .serif-h { margin-bottom: 14px; }

/* ---------- audit teaser ---------- */

.audit-card {
  background: var(--card);
  border-radius: var(--r-card);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-hero);
  padding: 34px;
}
.audit-search {
  width: 100%;
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--ink);
  background: var(--wash3);
  border: 1px solid var(--hairline);
  border-radius: var(--r-chip);
  padding: 13px 16px;
  margin-bottom: 20px;
}
.audit-search::placeholder { color: var(--ink3-text); }

.audit-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 12px;
  margin-bottom: 26px;
}
/* Both ship with the hidden attribute and js/audit.js drops it once the card
   list has loaded. The attribute, not a class, so the field cannot appear as a
   live control if this stylesheet is the thing that fails to arrive. A
   declared display always beats the UA sheet's [hidden] rule, so grid has to
   say so explicitly; the wrapper is listed with it to keep the pair together. */
.audit-grid[hidden], .audit-searchwrap[hidden] { display: none; }
/* below 640 the 230px minimum track no longer fits inside the card's own
   padding, so the chips overhang. Drop to one explicit column and pull the
   card padding in with it. */
@media (max-width: 640px) {
  .audit-card { padding: var(--s-5) 18px; }
  .audit-grid { grid-template-columns: 1fr; }
}
.card-chip {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--r-chip);
  box-shadow: var(--shadow-row);
  padding: 13px 14px;
  cursor: pointer;
  transition: border-color 150ms var(--ease), box-shadow 150ms var(--ease), background 150ms var(--ease);
}
.card-chip:hover { border-color: var(--accent-shadow); }
/* the focus ring belongs on the 285x66 label a user sees, not on the 16px
   checkbox in its corner. The box keeps its own outline for hit clarity. */
.card-chip:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-shadow);
}
.card-chip input {
  margin-top: 3px;
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
  flex-shrink: 0;
}
.card-chip.checked {
  border-color: var(--accent);
  background: rgb(var(--accent-rgb) / 0.06);
}
.card-chip .chip-name {
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--ink);
  line-height: 1.3;
}
.card-chip .chip-meta {
  font-size: 0.78rem;
  color: var(--ink3-text);
  line-height: 1.4;
  margin-top: 2px;
}
.card-chip .chip-meta strong { color: var(--ink2); font-weight: 600; font-variant-numeric: tabular-nums; }

.audit-line {
  font-family: var(--serif);
  font-size: clamp(1.2rem, 2.6vw, 1.55rem);
  font-weight: 500;
  color: var(--ink);
  line-height: 1.35;
  min-height: 2.2em;
}
.audit-line .num { color: var(--accent); }
.audit-state {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 14px;
  margin-top: 10px;
}
.audit-state[hidden] { display: none; }
.audit-count {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink3-text);
  font-variant-numeric: tabular-nums;
}
.audit-clear {
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--accent);
  background: none;
  border: 0;
  border-radius: var(--r-focus);
  padding: 11px 0;
  min-height: 44px;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.audit-clear:hover { color: var(--ink); }
.audit-fineprint {
  font-size: 0.8rem;
  color: var(--ink3-text);
  margin-top: 8px;
}
.audit-cta { margin-top: 26px; }
.audit-empty {
  font-size: 0.9rem;
  color: var(--ink3-text);
  padding: 8px 2px;
}

/* ---------- receipts (Pacific dark) ---------- */

.pacific {
  background:
    radial-gradient(130% 80% at 50% -6%, #12303a 0%, #0d222b 38%, #0a181e 66%, #0a1418 100%),
    var(--p-bg);
  color: var(--p-ink);
  padding: var(--s-section) 0;
}
.pacific .kicker { color: var(--p-accent-text); }
.pacific .serif-h { color: var(--p-ink); }
.pacific .section-sub { color: var(--p-ink2); }

.ledger {
  background: var(--p-card);
  border: 1px solid var(--p-hairline);
  border-radius: var(--r-card);
  box-shadow: var(--p-shadow-hero);
  overflow: hidden;
  margin-top: 36px;
}
.ledger-label {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--p-ink3);
  padding: 18px 26px 0;
}
.ledger-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  padding: 20px 26px;
  border-bottom: 1px solid var(--p-hairline-soft);
}
.ledger-row:last-of-type { border-bottom: none; }
.ledger-row .row-title {
  font-weight: 600;
  font-size: 0.98rem;
  color: var(--p-ink);
}
.ledger-row .row-receipt {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--p-ink3);
  font-variant-numeric: tabular-nums;
  margin-top: 4px;
}
.ledger-row .row-value {
  font-weight: 700;
  font-size: 1.08rem;
  color: var(--p-accent-text);
  white-space: nowrap;
}
.ledger-sum {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  padding: 20px 26px;
  background: var(--p-card);
  border-top: 1px solid var(--p-hairline);
}
.ledger-sum .sum-label {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--p-ink2);
}
.ledger-sum .sum-value {
  font-family: var(--serif);
  font-size: 1.5rem;
  font-weight: 500;
  color: var(--p-ink);
}
.ledger-note {
  font-size: 0.8rem;
  color: var(--p-ink3);
  margin-top: 14px;
}

/* guarantee */
.guarantee {
  margin-top: var(--s-8);
  background: var(--p-card);
  border: 1px solid var(--p-hairline);
  border-radius: var(--r-hero);
  box-shadow: var(--p-shadow-hero);
  padding: 44px 40px;
}
.guarantee .serif-h { font-size: clamp(1.5rem, 3.4vw, 2.1rem); margin-bottom: 18px; }
.guarantee-promise {
  color: var(--p-ink2);
  font-size: 1.02rem;
  max-width: 42em;
  margin-bottom: 24px;
}
.guarantee-conditions {
  list-style: none;
  display: grid;
  gap: 10px;
  margin-bottom: 24px;
}
.guarantee-conditions li {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  color: var(--p-ink2);
  font-size: 0.92rem;
}
/* Same drawn tick as the price lists, see .price-list .tick. This row is
   0.92rem at the body's 1.6, so the line box is 23.6px and 13px centres at 5. */
.guarantee-conditions .tick {
  color: var(--p-accent-text);
  width: 13px;
  height: 13px;
  margin-top: 5px;
  flex-shrink: 0;
}
/* prose, not a label: Inter. The mono voice is reserved for kickers and for
   the receipt lines, where the monospace is doing real alignment work. */
.guarantee-foot {
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--p-ink3);
  border-top: 1px solid var(--p-hairline-soft);
  padding-top: 18px;
}
.guarantee-foot span { display: inline-block; margin-right: 14px; }

/* ---------- the app ---------- */

/* This section used to be four empty "coming soon" surfaces: three blank phone
   frames and a blank film slot. Four admissions of incompleteness in a row,
   sitting between the two strongest arguments on the page, said less than
   nothing. It now carries one phone drawn in the site's own type and colour,
   beside three plain lines about the three screens. The render is labelled as
   an example next to it, the same device the ledger rows already use, so it
   never reads as live data or as a photograph of a shipped build. */
.app-show {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: 56px;
  align-items: center;
  margin: 48px 0 44px;
}
@media (max-width: 900px) { .app-show { gap: 36px; } }
@media (max-width: 760px) {
  .app-show {
    grid-template-columns: 1fr;
    gap: 34px;
    justify-items: center;
    max-width: 420px;
    margin-inline: auto;
  }
}

.phone-frame {
  aspect-ratio: 9 / 19;
  width: 100%;
  max-width: 300px;
  border-radius: var(--r-phone);
  background: var(--card);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-hero);
  padding: 12px;
  position: relative;
}
.phone-screen {
  height: 100%;
  border-radius: var(--r-hero);
  border: 1px solid var(--hairline);
  background:
    radial-gradient(120% 90% at 92% 0%, rgb(var(--accent-rgb) / 0.09), rgb(var(--accent-rgb) / 0) 60%),
    var(--wash3);
  padding: 22px 15px 18px;
  display: flex;
  flex-direction: column;
  gap: 13px;
  overflow: hidden;
}

.ts-top { display: flex; align-items: baseline; justify-content: space-between; }
.ts-kicker,
.ts-quiet-label {
  font-family: var(--mono);
  font-size: 0.58rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink3-text);
}
.ts-date { font-family: var(--mono); font-size: 0.62rem; color: var(--ink3-text); }

.ts-card {
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--r-row);
  box-shadow: var(--shadow-row);
  padding: 15px 14px 13px;
}
.ts-tag {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.55rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--pill-bg);
  border-radius: var(--r-pill);
  padding: 3px 9px;
  margin-bottom: 10px;
}
.ts-title {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 1.02rem;
  line-height: 1.28;
  color: var(--ink);
  margin-bottom: 6px;
}
.ts-text { font-size: 0.73rem; line-height: 1.5; color: var(--ink2); margin-bottom: 11px; }
.ts-receipt {
  font-family: var(--mono);
  font-size: 0.62rem;
  color: var(--accent);
  border-top: 1px solid var(--hairline);
  padding-top: 9px;
}

.ts-quiet-label { margin-top: 2px; }
.ts-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.72rem;
  color: var(--ink2);
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 11px;
}
.ts-row:last-of-type { border-bottom: 0; padding-bottom: 0; }
.ts-row .num { font-family: var(--mono); font-size: 0.62rem; color: var(--ink3-text); }
/* pinned to the foot of the screen, with a rule of its own, so the air above it
   reads as the point rather than as a gap where content should have been */
.ts-foot {
  margin-top: auto;
  padding-top: 13px;
  border-top: 1px solid var(--hairline);
  text-align: center;
  font-family: var(--serif);
  font-size: 0.84rem;
  color: var(--ink2);
}

.app-note-label {
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink3-text);
  margin-bottom: 22px;
}
.app-note-list { list-style: none; display: grid; gap: 22px; }
.app-note-list li { border-left: 2px solid var(--hairline); padding-left: 18px; }
.app-note-name {
  display: block;
  font-family: var(--serif);
  font-weight: 500;
  font-size: 1.18rem;
  color: var(--ink);
  margin-bottom: 4px;
}
.app-note-text { display: block; color: var(--ink2); font-size: 0.95rem; }

.trust-block { text-align: center; margin-top: 20px; }
.trust-line {
  font-family: var(--serif);
  font-size: clamp(1.3rem, 3vw, 1.7rem);
  font-weight: 500;
  color: var(--ink);
  margin-bottom: 8px;
}
.trust-sub { color: var(--ink2); font-size: 0.95rem; margin-bottom: 28px; }

.store-badge {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  background: var(--ink);
  color: var(--on-accent);
  border-radius: var(--r-cta);
  padding: 13px 24px;
  font-weight: 600;
  font-size: 0.92rem;
}
.store-badge .badge-note {
  font-weight: 400;
  font-size: 0.8rem;
  color: rgba(255, 255, 255, 0.6);
}

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

.site-footer {
  border-top: 1px solid var(--hairline);
  padding: 44px 0 36px;
  margin-top: 0;
}
.site-footer .wrap {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.footer-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
}
.footer-links { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* same 44px floor as the header links: these wrap onto two rows on a phone */
.footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--ink2);
  font-size: 0.85rem;
}
.footer-links .dot { color: var(--ink3-text); font-size: 0.85rem; }
.footer-tag { color: var(--ink3-text); font-size: 0.85rem; }
.footer-legal {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  color: var(--ink3-text);
  font-size: 0.78rem;
}

/* ---------- legal pages ---------- */

.page-hero { padding: 64px 0 40px; text-align: center; }
.page-hero .kicker { display: block; margin-bottom: 12px; }
.page-hero h1 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(2rem, 5vw, 2.8rem);
  letter-spacing: -0.01em;
  margin-bottom: 10px;
}
.page-hero .page-dates { color: var(--ink3-text); font-size: 0.85rem; }

.legal-layout {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 40px;
  align-items: start;
  padding-bottom: 80px;
}
/* The TOC used to be display:none below 860px, which left a 12,000px privacy
   policy with no jump links on the one device where the scroll is longest. It
   is now a disclosure at every width: open and chrome-free in the sticky
   desktop column, collapsed into a tappable card on phones and tablets. The
   markup carries `open`, so a reader with no JS always gets the jump list. */
@media (max-width: 860px) { .legal-layout { grid-template-columns: 1fr; gap: 0; } }

.legal-toc { position: sticky; top: 28px; }
.legal-toc summary {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink3-text);
  margin-bottom: 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  list-style: none;
  cursor: pointer;
}
.legal-toc summary::-webkit-details-marker { display: none; }
.legal-toc .toc-chev {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  color: var(--ink3-text);
  transition: transform 250ms var(--ease);
}
.legal-toc details[open] .toc-chev { transform: rotate(180deg); }
.legal-toc a {
  display: block;
  color: var(--ink2);
  font-size: 0.85rem;
  padding: 5px 0 5px 12px;
  border-left: 2px solid transparent;
  transition: color 150ms var(--ease), border-color 150ms var(--ease);
}
.legal-toc a:hover { color: var(--accent); border-left-color: var(--accent); text-decoration: none; }
/* the section sitting at the top of the viewport, set by js/legal.js */
.legal-toc a[aria-current="true"] { color: var(--accent); border-left-color: var(--accent); }

@media (max-width: 860px) {
  .legal-toc { position: static; margin-bottom: 26px; }
  .legal-toc .toc-disclose {
    background: var(--card);
    border: 1px solid var(--hairline);
    border-radius: var(--r-row);
    box-shadow: var(--shadow-row);
    padding: 0 18px;
  }
  .legal-toc summary { margin-bottom: 0; padding: 15px 0; min-height: 44px; }
  .legal-toc .toc-chev { width: 17px; height: 17px; }
  .legal-toc nav { border-top: 1px solid var(--hairline); padding: 8px 0 12px; }
  /* 12px each side clears the 44px tap floor at this type size */
  .legal-toc a { padding: 12px 0 12px 12px; }
}

.legal-card {
  background: var(--card);
  border-radius: var(--r-card);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-card);
  padding: 44px;
}
@media (max-width: 640px) { .legal-card { padding: 28px 22px; } }
.legal-card h2 {
  font-size: 1.3rem;
  font-weight: 700;
  color: var(--ink);
  margin: 2.4rem 0 0.7rem;
  padding-bottom: 0.45rem;
  border-bottom: 1px solid var(--hairline);
}
.legal-card h2:first-of-type { margin-top: 1.4rem; }
.legal-card h3 {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--ink);
  margin: 1.6rem 0 0.4rem;
}
.legal-card p { color: var(--ink2); font-size: 0.94rem; margin-bottom: 0.9rem; line-height: 1.75; }
.legal-card ul, .legal-card ol { color: var(--ink2); font-size: 0.94rem; line-height: 1.75; padding-left: 1.3rem; margin-bottom: 0.9rem; }
.legal-card li { margin-bottom: 0.35rem; }
.legal-card strong { color: var(--ink); }

/* contact page */
.contact-stack { display: grid; gap: 20px; padding-bottom: 80px; max-width: 640px; margin: 0 auto; }
.contact-card {
  background: var(--card);
  border-radius: var(--r-card);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-card);
  padding: 36px;
}
/* Same step-down .legal-card takes at this width, and for a stronger reason:
   the About waitlist puts two 48px controls inside this box, so the padding
   is costing interactive width rather than only text measure. */
@media (max-width: 640px) { .contact-card { padding: 28px 22px; } }
.contact-card h2 { font-size: 1.15rem; font-weight: 700; margin-bottom: 12px; }
.contact-card p { color: var(--ink2); font-size: 0.94rem; }
/* The About page runs several paragraphs to a card. The contact page only
   ever ran one or two and set the gap inline, so nothing here was spaced by
   default. Direct children only: .contact-facts stacks its paragraphs on a
   grid gap and must not collect a margin on top of it. Anything that already
   carries an inline margin-top still wins, so the contact page is unchanged. */
.contact-card > p + p { margin-top: 12px; }
/* Named sub-blocks inside a card, the two beliefs. Without a size the UA
   default sets h3 larger than the .contact-card h2 sitting above it. */
.contact-card h3 { font-size: 1rem; font-weight: 600; margin-bottom: 6px; }
.contact-card p + h3 { margin-top: 18px; }
.contact-card .contact-email {
  font-size: 1.15rem;
  font-weight: 600;
}
.contact-steps { display: grid; gap: 10px; margin-top: 14px; }
.contact-steps .step { display: flex; gap: 12px; align-items: baseline; }
.contact-steps .step-num {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--accent);
}
.contact-steps p { font-size: 0.9rem; }
.contact-facts { display: grid; gap: 6px; }
.contact-facts p { font-size: 0.9rem; }

/* 404 */
.notfound {
  min-height: 62vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 60px 24px;
}
.notfound .bm { width: 72px; height: 72px; color: var(--accent); margin-bottom: 22px; }
.notfound h1 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.8rem, 5vw, 2.5rem);
  margin-bottom: 10px;
}
.notfound p { color: var(--ink2); margin-bottom: 26px; max-width: 30em; }

/* ---------- the founding waitlist, the form itself ---------- */
/* Moved out of sections.css so it is available to every page that
   loads site.css alone. The home page carries two placements of it
   (#pricing and #close) and the About page a third; the placement
   wrappers stay in sections.css, this is the component. */

/* One Netlify form name, several placements. The field is the same
   opaque light surface everywhere, borrowed verbatim from
   .audit-search. Restyling it against the #close gradient would mean
   re-deriving the placeholder contrast; this way the ink-on-wash
   pairing it was measured at travels with it. */

/* The lead line above the form. Part of the component, not a
   placement: it moved here with the rest of it when About became
   the third placement, because it was the one member left behind
   in sections.css and the About build inlined a margin instead of
   reusing it. #close overrides the colour for the gradient field
   in sections.css; nowhere else needs to. */
.wait-lead {
  font-size: 0.92rem;
  color: var(--ink2);
  margin-bottom: 12px;
}

/* Netlify's honeypot. Hidden two ways on purpose. The hidden
   attribute in the markup is the one that matters: it holds even
   if this stylesheet is truncated or never arrives, and a field
   labelled "leave this field empty" that renders as a real input
   is one a sighted reader can fill in, after which Netlify
   discards the submission and still reports success. The rule
   below is the belt to that pair of braces. Either way the field
   stays in the DOM and still submits, which is the whole point,
   and it stays out of the accessibility tree, so nobody reading
   with a screen reader is asked to fill in a trap.

   !important because this rule has to win against selectors that
   have not been written yet. A declared display always beats the
   UA sheet's [hidden] rule, the same trap already documented for
   .audit-grid above, and the About placement sits inside a
   .contact-card, where a card-scoped `.contact-card p` outranks a
   bare class. One display value added there and the trap renders
   as a live text input labelled "leave this field empty": a
   sighted reader fills it, sees the full success message, and
   Netlify discards the address. Nothing may ever need to show
   this field, so nothing needs to outrank this. */
.wait-pot { display: none !important; }

.wait-row {
  display: flex;
  align-items: stretch;
  gap: 10px;
}

/* An <input> carries an intrinsic minimum width of roughly twenty
   characters. Inside a flex row that floor outranks the available
   space, so without min-width:0 the button is pushed past the
   right edge of a 375px viewport and the page scrolls sideways.
   text-align is set because #close centres its text and that
   inherits straight into the field. */
.wait-input {
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  min-height: 48px;
  font-family: var(--sans);
  font-size: 0.95rem;
  text-align: left;
  color: var(--ink);
  background: var(--wash3);
  border: 1px solid var(--hairline);
  border-radius: var(--r-chip);
  padding: 13px 16px;
}
.wait-input::placeholder { color: var(--ink3-text); }

.wait-btn {
  flex: 0 0 auto;
  min-height: 48px;
  justify-content: center;
}

/* Below 560px the two controls stack. Nothing has to be measured
   against a shrinking track, so the row cannot overflow. */
@media (max-width: 560px) {
  .wait-row { flex-direction: column; }
  .wait-btn { width: 100%; }
}

.wait-fine {
  font-size: 0.8rem;
  color: var(--ink3-text);
  margin-top: 10px;
}

/* The live region is present and rendered from first paint, so a
   screen reader has it registered before any text arrives into
   it. Empty, a block box with no content generates no line box,
   and the margin is dropped with it, so it occupies nothing until
   the script writes to it. It is never display:none, which would
   take it back out of the accessibility tree. */
.wait-status {
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--ink2);
  margin-top: 14px;
}
.wait-status:empty { margin-top: 0; }
/* On success the script moves focus to the block, not into the
   live region: focus landing on a region in the same tick its
   text changes gets the message announced twice by some readers
   and dropped by others. The block is the container the message
   sits in, so the reader lands beside it either way.

   :not(:focus-visible) is load-bearing. The ring is noise on a
   block nobody navigated to, which is the pointer case, but a
   reader who submitted with Enter has just had focus relocated
   and does need to see where it went. Unqualified, this rule tied
   the global [tabindex]:focus-visible ring on specificity and won
   on source order, taking the ring away in both cases. */
.wait-block:focus:not(:focus-visible) { outline: none; }

/* Success. The class is added by the script and by nothing else,
   so with JavaScript off it is never applied and the form is
   never hidden: that path submits and lands on /thanks instead.
   The status rule carries .wait-block as well as .wait-done so it
   outranks the .contact-card restatement below on specificity
   rather than on source order: at equal weight, reordering this
   block would silently flatten the success line back to card body
   type with no rule failing loudly. */
.wait-done .wait-form { display: none; }
.wait-block.wait-done .wait-status {
  font-family: var(--serif);
  font-size: 1.08rem;
  color: var(--ink);
}

/* The About placement sits inside a .contact-card, and `.contact-card p`
   carries one element selector more than `.wait-fine`, `.wait-status` and
   `.wait-lead` do, so it would silently reset all three to card body type.
   Restated here at matching weight, same values, so the fine print stays
   fine print. */
.contact-card .wait-fine { font-size: 0.8rem; color: var(--ink3-text); }
.contact-card .wait-status { font-size: 0.92rem; }
.contact-card .wait-lead { font-size: 0.92rem; }
