/* Velisa — hosted pages (Privacy / Support / Home)
   Palette lifted from Velisa/Design/Palette.swift (`.ember`, the app's default theme).
   Type roles lifted from Velisa/Design/Theme.swift (`Theme.F`): serif / mono / sans,
   all system fonts, approximated here with web-safe system stacks since the app
   itself ships zero bundled fonts either. Dark-only, matching the app
   (Info.plist: UIUserInterfaceStyle = Dark) — there is no light variant to match. */

:root {
  --bg: #151310;
  --card: #1c1915;
  /* Was rgba(255, 255, 255, 0.07) — pure white on a warm base desaturates
     the hairline toward cold grey. Re-derived from --text's off-white at a
     matched visual weight (1.21:1 -> 1.22:1 vs --card) instead, so the
     border stays warm. Kept identical to site.css's copy of this token —
     see docs/site-build/COLOUR.md Part C (2026-08-01 colour pass). This file
     does not add --sunken/--well/--track: privacy.html and support.html
     have no nested-card surfaces or progress bars to consume them. */
  --card-border: rgba(240, 234, 228, 0.08);
  --tab: #181510;

  /* Was #f4efe6 until the 2026-08-01 colour pass — softer, less saturated
     off-white, unified with site.css and Palette.swift's Ember `text` so
     all three live pages agree on the product's primary text colour.
     docs/site-build/COLOUR.md Part A has the full A/B against the runner-up
     candidate and the contrast table against every surface. */
  --text: #f0eae4;
  --text-dim: #8b8478;
  --text-faint: #5f5a51;

  --accent: #f0a93b;       /* Amber Gold — the app's one hero hue */
  --accent-dim: rgba(240, 169, 59, 0.15);
  --negative: #e05a5a;
  --negative-dim: rgba(224, 90, 90, 0.15);

  --font-serif: ui-serif, "New York", Georgia, "Times New Roman", serif;
  --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue",
    Arial, sans-serif;

  color-scheme: dark;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

/* Subtle warm vignette, same idea as the app's card-on-black surfaces —
   pure CSS, no image asset. */
body {
  background-image: radial-gradient(
    ellipse 900px 500px at 50% -10%,
    rgba(240, 169, 59, 0.05),
    transparent 60%
  );
  background-repeat: no-repeat;
}

/* THE PAGE RAIL, made the same one as / (2026-08-01).

   These two pages used to centre a 720px column, which put their left text edge
   at x=380 on a 1440 viewport. index.html puts its content at x=187 and its
   chrome at x=87. That was a THIRD rail, undocumented, on the two pages an App
   Store reviewer is sent to — and "one consistent left edge" is the organising
   idea of the whole site.

   The container now uses index.html's exact `.v-wrap--shell` formula, so the
   two cannot drift apart, and the 720px PROSE MEASURE moves off the container
   and onto the children — which is the same move site.css §3 made when it
   retired `.v-wrap--prose` in favour of `.v-stack--measure`. Same line length,
   correct left edge. `.site-nav` opts back out to the full rail, because page
   chrome is allowed to be wider than the reading column; that distinction is
   deliberate on / and it should read the same here.

   Only the container and the type below are touched. NO COPY ON THESE TWO PAGES
   IS CHANGED — they are live, App-Store-referenced, and the wording is not this
   pass's to edit. */
:root {
  --w-shell: 960px;
  --gutter:  clamp(18px, 4vw, 40px);
  --w-measure: 720px;
}
@media (min-width: 1200px) {
  :root { --w-shell: clamp(960px, 74vw, 1200px); }
}

.wrap {
  /* Was shell-width (up to 1200px), which parked the ≤720px prose column on
     the page's left edge on wide screens. The wrap is now the reading column
     itself, centered — everything inside stays left-aligned but the column
     sits mid-page (2026-08-04 owner request). The nav rides the same column
     so header and prose share edges. */
  max-width: calc(var(--w-measure) + var(--gutter) * 2);
  margin-inline: auto;
  padding: 56px var(--gutter) 96px;
}
.wrap > * { max-width: var(--w-measure); }
.wrap > .site-nav { max-width: none; }

/* ---------- Header / nav ---------- */

.site-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 48px;
  flex-wrap: wrap;
}

.wordmark {
  font-family: var(--font-serif);
  font-size: 22px;
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
  letter-spacing: 0.01em;
}

/* The wordmark is ONE colour. It used to set "sa" in the accent, which spent the
   brand's single hero hue on decoration — amber means "favourable" everywhere
   else in this product, and a two-tone logotype quietly drains that of meaning
   before a visitor has read anything. The mark beside it carries the amber; the
   type does not need to. */
.wordmark span {
  color: inherit;
}

/* The mark was removed from every page on 2026-08-01 (owner's call). The nav is
   the plain serif wordmark now. `.wordmark-lockup`, `.vmark` and the two
   `.vmark__*` tint rules are deleted here as well as in site.css — the two
   stylesheets stay deliberately decoupled, so this had to be done twice by
   hand; there is no build step. The favicon on / is unaffected and stays.
   Keep these two blocks in sync by hand. */
.wordmark-link {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  color: var(--text);
}
.wordmark-link .wordmark { font-size: inherit; }

.nav-links {
  display: flex;
  gap: 20px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.nav-links a {
  color: var(--text-dim);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: color 0.15s ease, border-color 0.15s ease;
}

/* The `:hover` half of this rule moved to "Pointer feedback" at the foot of
   this file on 2026-08-01, behind `@media (hover: hover) and (pointer: fine)`.
   `:focus-visible` deliberately did not go with it — it is a keyboard
   affordance and has nothing to do with pointers. site.css:846 is the same
   split, made for the same reason; the two files stay in step. */
.nav-links a:focus-visible {
  color: var(--accent);
  border-color: var(--accent);
}

.nav-links a[aria-current="page"] {
  color: var(--text);
}

/* ---------- Typography ---------- */

.eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 10px;
}

/* Tracking and leading were missing here and present on / — the same 40px
   heading rendered with `letter-spacing: normal` on these two pages and at
   -0.02em / 1.05 on the landing page. Type at this size reads too loose without
   the optical correction, and "size-specific tracking" is a rule the rest of
   this site holds to in 46 places. Values copied from site.css `.v-h1`, not
   re-invented, so the two files say the same thing. */
h1 {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: clamp(30px, 5vw, 40px);
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;
  color: var(--text);
  margin: 0 0 8px;
}

h1 + .meta {
  font-family: var(--font-mono);
  font-size: 13px;
  /* --text-faint (2.71:1 on --bg) fails WCAG AA for readable text — it is
     reserved for non-text marks only, per site.css §1's tokens comment and
     docs/site-build/COLOUR.md. This line carries a date someone is meant to
     read, so it takes --text-dim (5.01:1 on --bg) instead, matching the
     equivalent role in site.css (.v-footer__sig etc). Found + fixed in the
     2026-08-01 audit pass — the twin file was never brought in line with
     that policy when site.css adopted it. */
  color: var(--text-dim);
  margin: 0 0 40px;
}

h2 {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: 22px;
  color: var(--text);
  margin: 44px 0 14px;
  padding-top: 4px;
}

h2:first-of-type {
  margin-top: 8px;
}

h3 {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 16px;
  color: var(--text);
  margin: 22px 0 8px;
}

p {
  color: var(--text-dim);
  margin: 0 0 14px;
}

p.lead {
  color: var(--text);
  font-size: 17px;
}

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

strong {
  color: var(--text);
  font-weight: 600;
}

ul,
ol {
  color: var(--text-dim);
  padding-left: 22px;
  margin: 0 0 16px;
}

li {
  margin-bottom: 8px;
}

li::marker {
  color: var(--accent);
}

code,
.mono {
  font-family: var(--font-mono);
  font-size: 0.92em;
}

hr {
  border: none;
  border-top: 1px solid var(--card-border);
  margin: 40px 0;
}

/* ---------- Cards ---------- */

.card {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: 16px;
  padding: 22px 22px 8px;
  margin: 0 0 20px;
}

.card > p:last-child,
.card > ul:last-child,
.card > ol:last-child {
  margin-bottom: 14px;
}

/* The 3px amber slab down the left edge is gone (2026-08-01).

   This file already makes the argument against it, one screen up, about the
   wordmark: setting "sa" in the accent "spent the brand's single hero hue on
   decoration — amber means 'favourable' everywhere else in this product, and a
   two-tone logotype quietly drains that of meaning before a visitor has read
   anything." A 3px amber bar on a callout is the same spend for the same
   nothing: it marks a box as important with the colour that is supposed to mean
   a number came out well. index.html carries no box-shadow and no accent slabs
   at all — depth there is a 1px hairline and a one-step luminance change, and
   this box now says the same thing the same way. The amber survives where it
   earns its keep: the eyebrow, the links, and the primary button inside.

   The full border keeps `--accent-dim`, so the callout is still visibly warmer
   than an ordinary `.card` and still reads as the answer-first box it is. */
.tldr {
  background: var(--card);
  border: 1px solid var(--accent-dim);
  border-radius: 14px;
  padding: 22px 24px;
  margin: 4px 0 40px;
}

.tldr .eyebrow {
  margin-bottom: 8px;
}

.tldr p {
  color: var(--text);
  font-size: 17px;
  margin: 0 0 10px;
}

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

.tldr ul {
  margin: 10px 0 0;
}

.tldr li {
  color: var(--text-dim);
}

/* ---------- Placeholder highlighting ---------- */

.placeholder {
  color: var(--negative);
  background: var(--negative-dim);
  border-radius: 5px;
  padding: 1px 6px;
  font-family: var(--font-mono);
  font-size: 0.88em;
  white-space: nowrap;
}

/* ---------- FAQ ---------- */

.faq-item {
  border-bottom: 1px solid var(--card-border);
  padding: 20px 0;
}

.faq-item:first-child {
  padding-top: 0;
}

.faq-item:last-child {
  border-bottom: none;
}

.faq-q {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 16px;
  color: var(--text);
  margin: 0 0 8px;
}

.faq-a p:last-child,
.faq-a ul:last-child,
.faq-a ol:last-child {
  margin-bottom: 0;
}

.faq-a table {
  width: 100%;
  border-collapse: collapse;
  margin: 10px 0 16px;
  font-size: 14px;
}

.faq-a th,
.faq-a td {
  text-align: left;
  padding: 7px 10px 7px 0;
  border-bottom: 1px solid var(--card-border);
  color: var(--text-dim);
}

.faq-a th {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* --text-faint fails AA for text (see h1 + .meta above) — not currently
     rendered (support.html's .faq-a markup has no <th>), but fixed so it is
     never a live trap the day someone adds one. */
  color: var(--text-dim);
  font-weight: 500;
}

.faq-a td:first-child,
.faq-a th:first-child {
  color: var(--text);
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--accent);
  color: #1a1408;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 15px;
  text-decoration: none;
  padding: 12px 20px;
  border-radius: 12px;
  border: none;
  /* `transform` joined the list when the press state below was added. This is a
     shorthand: adding a property means restating every property it already
     carried, or the ones left out are silently deleted. The `all` keyword
     appears zero times in either stylesheet and is not going to start here.

     `ease` -> `var(--v-ease-swift)` is not a retune: `--v-ease-swift` IS
     `ease-in-out`, and this now matches site.css:921's `.v-btn` declaration
     character for character, which is the point — the two files describe the
     same button and are kept in step by hand. The 0.15s literal is left as a
     literal for the same reason: site.css writes 0.15s here too. */
  transition: opacity 0.15s var(--v-ease-swift),
              transform 0.15s var(--v-ease-swift);
}

/* `:hover` moved to "Pointer feedback" below; `:focus-visible` stays. See the
   note on .nav-links above for why the two were separated. */
.btn:focus-visible {
  opacity: 0.88;
}

.btn-secondary {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--card-border);
}

.btn-secondary:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
  opacity: 1;
}

/* ---------- Footer ---------- */

.site-footer {
  margin-top: 64px;
  padding-top: 24px;
  border-top: 1px solid var(--card-border);
  font-family: var(--font-mono);
  font-size: 12px;
  /* --text-faint measures 2.71:1 on --bg here — fails WCAG AA for readable
     text (site.css §1's tokens comment; docs/site-build/COLOUR.md). This is
     real copy plus the page's own nav links (Home/Privacy/Support), not a
     decorative mark, so it takes --text-dim (5.01:1), the same token
     site.css's .v-footer__sig / .v-footer__links a already use for the
     identical role. Found + fixed in the 2026-08-01 audit pass. */
  color: var(--text-dim);
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.site-footer a {
  color: var(--text-dim);
  /* Had no transition at all, so its hover snapped. Same declaration the prose
     links below carry — this is a colour change on something already on screen,
     which is exactly what `--v-ease-swift` is for. */
  transition: color var(--v-d-instant) var(--v-ease-swift);
}

/* `:hover` moved to "Pointer feedback" below. `:focus-visible` is NEW: these
   three links were the only ones in the file with a hover and no keyboard
   equivalent, and splitting the hover off without adding it would have left a
   keyboard user with strictly less than they had. */
.site-footer a:focus-visible {
  color: var(--accent);
}

/* ---------- Home page ---------- */

.hero {
  padding: 64px 0 40px;
  text-align: left;
}

.hero h1 {
  font-size: clamp(36px, 7vw, 52px);
  letter-spacing: -0.022em;   /* tighter again, because it is larger again */
}

.hero .lead {
  font-size: 18px;
  max-width: 46ch;
}

.link-row {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 28px;
}

.pillars {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px;
  margin: 48px 0;
}

.pillar {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: 14px;
  padding: 18px;
}

.pillar .glyph {
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  display: block;
  margin-bottom: 8px;
}

.pillar p {
  color: var(--text-dim);
  font-size: 14px;
  margin: 0;
}

@media (max-width: 480px) {
  .wrap {
    padding: 40px 16px 72px;
  }
  .card,
  .tldr {
    padding-left: 16px;
    padding-right: 16px;
  }
}


/* ---------- Motion: tokens ---------- */

/* Added 2026-08-01. docs/site-build/MOTION-V2.md item L.
   Everything below this line is motion; nothing below this line changes a
   single pixel of geometry, and nothing below this line is capable of hiding
   content. Both claims are structural rather than careful — see the reveal
   block for why.

   THE BRIEF FOR THESE TWO PAGES IS "QUIETER". Someone opening a privacy policy
   is not here to be impressed; they are here to check something, on a page an
   App Store reviewer also opens. So the mechanism is exactly site.css §11b's —
   the same two animations, the same two ranges, the same fail-open shape — run
   smaller, finished sooner, and with every additional layer the landing page
   carries left out. There is no stagger ladder, no section clock, no seam
   draw, no nav weight, no progress rule and no load entrance here. One
   mechanism, applied uniformly. Uniformity is most of what quiet means: a page
   where some blocks arrive and others do not reads as inconsistent, and
   inconsistency is louder than motion.

   These five tokens use site.css §1's NAMES so the two sheets read as one
   system. Three of the VALUES deliberately differ, and that difference is the
   whole design. style.css has no build step and shares no token file with
   site.css — line 144 above says so in as many words — so, as with every other
   value that exists in both files, these are kept in step by hand. */
:root {
  /* THE METHOD IS COPIED FROM site.css; THE NUMBER IS NOT, AND THAT IS THE
     POINT. site.css:1770-1791 ships 44px, set by sweeping the range and
     counting elements that were fully inside the viewport at a 16px margin —
     the strictest reading, where an element counts the instant it is inside
     the viewport at all — while still transparent. At 56px one element was
     still readable at 0.94 and that was rejected; 44px was clean at zero.

     Re-running the same criterion against THIS element set gives a different
     answer, because these two documents contain shorter blocks than the
     landing page does. An element of height h first satisfies "fully inside at
     a 16px margin" exactly 16 + h pixels into its own cover range, so the fade
     must finish inside 16 + h or that element is legible and translucent at
     once. Measured, Chrome, both pages, 1440x900 and 390x844, ignoring the
     four blocks that sit above the fold at first paint and therefore never
     fade at all:

         subject                              h     16 + h   opacity at 44px
         p (support footnote, 1440)          22px    38px        0.86
         p (privacy, single line, 1440)      26px    42px        0.95
         h1 / h2 / everything else          >=33px   >=49px      1.00

     0.86 is worse than the 0.94 that got 56px rejected upstream, so 44px does
     not survive contact with this page. 36px clears every text block on both
     pages at both viewports with 2px to spare, and costs 8px of scroll on a
     channel that was never the visible one — its job is anti-pop at the
     viewport boundary, not the reveal (the reveal is the lift below).

     THE ONE MEASURED EXCEPTION, stated rather than hidden: `hr` is 1px tall,
     so 16 + h = 17px and no usable fade range clears it — it passes through
     roughly 0.47 at the moment it technically counts as "fully inside". It is
     a decorative rule with no words on it, and the guarantee this sweep exists
     to protect is that nothing LEGIBLE is ever translucent. Giving the seam its
     own faster fade would make it arrive on a different clock from the blocks
     either side of it, which is a visible inconsistency traded for an
     invisible one. */
  --v-settle-fade-range: 36px;

  /* site.css ships 180px / 36px. Both come down by a third here. The RATIO
     does not: 24 / 120 = 0.200px of lag per pixel scrolled — the identical 20%
     velocity differential against a page that is itself moving at 1px/px.

     That ratio is the whole reason a scroll-scrubbed reveal is visible at all.
     Two layers separate somewhere around a 10-15% differential; the landing
     page shipped 30 / 420 = 7.1% and the owner reported it as "nothing moves".
     Buying quiet by dropping under that floor would reproduce that exact defect
     on two more pages, so quiet is bought with amplitude and duration instead —
     a third less travel, over a third less scroll, at the same honest rate.

     The geometry is most of the difference and is worth stating. `cover 0%` is
     the moment an element's top edge sits at the viewport bottom; after R
     pixels of scroll it sits at V - R. At R = 120 on a 900px viewport the
     settle finishes with the element's top at 780px — 87% down the screen, in
     the arrival band at the bottom edge where the eye is tracking the incoming
     boundary. The landing page finishes at 80%; the old 420px finished at 53%,
     dead centre of the reading zone, where the eye is tracking words and not
     motion. Finishing lower is the quiet: the movement is over before the block
     reaches the part of the screen anybody reads from.

     At a typical 26px line height, R = 120 means a paragraph is roughly four
     lines onto the screen when it stops moving. In practice one or two blocks
     are ever mid-settle at once, both of them in the bottom 120px band — not
     sixty things moving, a soft edge at the bottom of the window. */
  --v-settle-range: 120px;
  --v-settle-dist:   24px;

  /* Copied from site.css §1 unchanged, for the link transitions below.
     `--v-ease-swift` is a MOVEMENT curve — something already on screen changing
     state, where symmetric acceleration is right — which is exactly what a link
     colour under a cursor is. It is never an entrance curve.

     Note the file now carries both 160ms and 0.15s. That is inherited, not
     invented: site.css writes 0.15s on `.nav-links a` and `.v-btn` and 160ms on
     its prose links. Mirroring it faithfully is better than unilaterally
     harmonising one of two hand-synced sheets. */
  --v-d-instant:  160ms;
  --v-ease-swift: ease-in-out;
}


/* ---------- Motion: page reveal ---------- */

/* Same two keyframe names as site.css §11b, because they are the same two
   animations. There is no collision to worry about: no page loads both sheets
   (index.html and 404.html take site.css, privacy.html and support.html take
   this one). The only difference between the two definitions is which token the
   lift reads — site.css's reads `--v-dist`, which has three other consumers
   over there and must not be dragged around by a reveal. If a page ever loads
   both files, these two blocks have to be reconciled first.

   FAIL-OPEN, AND HERE IT IS ABSOLUTE. site.css can afford a `@supports not`
   fallback because site.js is there to arm a pre-state and, crucially, to
   disarm it. THESE TWO PAGES LOAD NO JAVASCRIPT AT ALL — no boot.js, no
   site.js, no inline script. So there is nothing that could ever un-hide
   anything, and the `@supports not` branch is therefore not "a fallback
   reveal", it is nothing at all:

     no `animation-timeline`  -> @supports is false -> visible, no motion
     reduced motion           -> media query false  -> visible, no motion
     CSS fails to load        -> unstyled but complete HTML

   No rule in this file sets `opacity` or `transform` on a resting element. The
   resting state IS the finished state, so there is no state — none — in which a
   failure leaves a word of either document invisible. That is the rule the
   landing page learned the hard way when a fail-closed reveal blanked 41 of 41
   elements the day site.js was blocked.

   SUBJECTS: every direct child of `.wrap` except the nav. Stated as an
   exclusion rather than a list on purpose — "everything in the reading column
   settles; the page chrome does not" is the actual intent, and it keeps working
   when a heading or a card is added to either document. `h1` and `p.meta` are
   in scope and are no-ops: both sit above the fold at first paint on every
   viewport measured, so their range has already passed at scroll 0 and they
   render at the finished state. Including them costs nothing and means the rule
   does not have to be re-reasoned if the header ever grows.

   Block granularity, never per-paragraph-inside-a-block: `.tldr`, `.card` and
   `.faq-item` arrive as single objects. Their hairlines are their own
   `border-bottom`s rather than a parent's background showing through a `gap`,
   so a translated block takes its own rule with it and nothing tears. (That is
   the trap MOTION-V2 §9.2 documents for the landing page's `gap: 1px` grids;
   it does not exist here.)

   Nothing in this file creates a scroll container — there is not one `overflow`
   or `position: sticky` declaration in it — so `view()` resolves against the
   page for every subject, which is the other trap that had to be checked. */
@keyframes v-settle-fade { from { opacity: 0; }                                 to { opacity: 1; } }
@keyframes v-settle-lift { from { transform: translateY(var(--v-settle-dist)); } to { transform: none; } }

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    /* TWO animations, TWO ranges, split by FUNCTION rather than by taste.
       Opacity and transform are answering different questions — one is a
       legibility guarantee, the other is the reveal — and each is measured
       against its own criterion above rather than sharing a number.

       `linear`, because on a scrubbed timeline the scroll position IS the
       timing. An easing curve here would make the page feel like it is
       dragging against the finger, which is the one thing a scroll animation
       must never do. */
    .wrap > :not(.site-nav) {
      animation: v-settle-fade linear both, v-settle-lift linear both;
      animation-timeline: view(), view();
      animation-range: cover 0% cover var(--v-settle-fade-range),
                       cover 0% cover var(--v-settle-range);
    }

    /* The one measured exception, and it is arithmetic rather than taste.
       An element whose `cover 0%` sits closer to the document's maximum scroll
       than `--v-settle-range` can never finish its lift, and would rest low
       forever. Measured, Chrome, four viewports: the footer's headroom is
       140px (privacy 1440x900), 141px (support 1440x900) and 151px on both at
       390x844 — every other child of `.wrap` clears 220px. So the footer, and
       only the footer, is short.

       80px / 16px rather than just a shorter range: 24px over 80px would be a
       0.30 differential, which is the far edge of the band where a scrubbed
       element stops reading as lag and starts reading as content stuck to the
       viewport. Scaling both keeps the rate at 0.200 — the same gesture, made
       smaller. It completes with 60px of scroll to spare on the tightest of the
       four measurements.

       It is the right editorial call independently: the end of a page should
       land, not drift. RE-MEASURE IF THE FOOTER OR THE PAGE PADDING CHANGES. */
    .site-footer {
      --v-settle-range: 80px;
      --v-settle-dist:  16px;
    }
  }
}


/* ---------- Pointer feedback ---------- */

/* Added 2026-08-01 alongside the reveal. Four `:hover` rules in this file were
   ungated and three of them were paired with `:focus-visible`; all four are
   collected here, behind the pointer query, and not one value changed on the
   way. This is the same move site.css §23 made, made for the same measured
   reason: a touchscreen synthesises `:hover` on tap and then LEAVES IT STUCK
   until you tap something else. So on a phone — the device an App Store
   reviewer opens both these pages on — tapping "Email support" left the button
   dimmed to 0.88 and tapping a nav link left it amber and underlined, as though
   a cursor were still resting on it. `:focus-visible` stayed behind at each
   original site: it is a keyboard affordance and has nothing to do with
   pointers.

   Then press feedback, which neither page had at all. apple-design §1: respond
   on pointer-DOWN, not on release — `:active` does exactly that, with no
   JavaScript, so it cannot be delayed by a busy main thread. These rules are
   deliberately NOT gated, because `:active` is not a hover and touch is the
   case they exist for. */
@media (hover: hover) and (pointer: fine) {
  .nav-links a:hover             { color: var(--accent); border-color: var(--accent); }
  .btn:hover                     { opacity: 0.88; }
  .btn-secondary:hover           { border-color: var(--accent); color: var(--accent); opacity: 1; }
  .site-footer a:hover           { color: var(--accent); }

  /* The wordmark is the one link on either page that ignored a cursor
     completely — no hover, no press, no transition, since it shipped. It is
     also the only way back to the landing page from the top of the document. */
  .wordmark-link:hover .wordmark { color: var(--accent); }

  /* Prose links — every `mailto:`, the in-page waitlist anchor, and the
     cross-links between the two pages. A mix toward `--text` rather than a
     different hue, so the accent's reserved meaning is not extended and no new
     colour enters either page. Same declaration site.css uses for the identical
     role. `color-mix` degrades to "no declaration" where it is unsupported,
     which leaves the link at its resting amber — a lost hover, not a bug. */
  p a:hover,
  li a:hover                     { color: color-mix(in srgb, var(--accent) 78%, var(--text)); }
}

/* A press on a filled button is a NUDGE, matching site.css:923's `.v-btn`
   exactly. Not `opacity`, which is already what hover does — on a mouse that
   would mean pressing the button changed nothing at all — and not a scale,
   because this is a wide bar and a scale distorts the label. */
.btn:active { transform: translateY(1px); }

/* A press on a text link is a COLOUR change, never a scale: scaling inline text
   distorts the type and, on a link inside a sentence, reflows the reader's eye
   mid-word. */
.nav-links a:active            { color: var(--accent); border-color: var(--accent); }
.site-footer a:active          { color: var(--accent); }
.wordmark-link:active .wordmark { color: var(--accent); }
p a:active,
li a:active                    { color: color-mix(in srgb, var(--accent) 78%, var(--text)); }

/* The two transitions the states above need. `.nav-links a` and `.btn` already
   carry theirs (unchanged, and byte-matched to site.css's copies on purpose);
   these two elements had none, so their hover would have snapped. */
.wordmark,
p a,
li a {
  transition: color var(--v-d-instant) var(--v-ease-swift);
}


/* ---------- Motion: reduced ---------- */

/* A visitor who asked for no motion is not asking for less of it: the end state
   arrives immediately, never a shortened tween.

   ONE PRONG, NOT TWO, AND THAT IS DELIBERATE. site.css pairs every reduce media
   query with a `:root[data-motion="reduced"]` mirror, because boot.js writes
   that attribute in the <head> before anything paints. These two pages run no
   JavaScript, so `<html>` never receives the attribute and a `data-motion`
   selector here could never match — it would be dead code claiming to be a
   safeguard, which is worse than not writing it. The media query is the only
   signal available on these pages, and unlike the attribute it also reflects
   the setting being changed mid-visit.

   The reveal lock is strictly belt-and-braces: the mechanism above already
   lives inside `@media (prefers-reduced-motion: no-preference)`, so under
   `reduce` it never matches and there is nothing to switch off. It is written
   anyway, with `!important`, for the reason site.css §11b keeps its own copy
   explicit — `@media` and `@supports` add ZERO specificity, so a future rule
   written outside those wrappers would win under reduced motion too, silently.
   This is the only structural defence against that, and on a page with no
   script to correct anything it is worth four lines. */
@media (prefers-reduced-motion: reduce) {
  .wrap > :not(.site-nav) {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }

  /* The colour changes survive; only the tween goes. Removing the confirmation
     that the interface heard a press would be removing information, not
     movement (apple-design §14) — so nothing above is switched off here except
     the `.btn` nudge, which is the one thing in this file that is movement. */
  .nav-links a,
  .btn,
  .site-footer a,
  .wordmark,
  p a,
  li a { transition: none; }

  .btn:active { transform: none; }
}
