/* ============================================================
   v3 — RESET + ELEMENT BASE
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; overflow-x: clip; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth;
  /* ⚠️ THE OS TAP FLASH GOES, AND SOMETHING DELIBERATE REPLACES IT. iOS paints
     `rgba(0,0,0,0.18)` over the whole box of anything tappable on touch — the
     founder hit it on the section-01 accordion, where it lands as a dark slab
     across a full-width row for the length of the tap.
     ⚠️ REMOVING IT ALONE WOULD BE WORSE THAN LEAVING IT. That flash is the only
     confirmation a finger gets that the tap registered, and a control with no
     acknowledgement reads as broken rather than as clean. Every element that
     loses it here is given its own `:active` state, in the palette, at a weight
     the design chose — see `.hzp__lab:active`, `.btn:active`, `.mnav a:active`.
     Set on `html` rather than on `*`: it inherits, so one declaration covers
     everything, and a component that wants the flash back can say so. */
  -webkit-tap-highlight-color: transparent; }

/* ⚠️ THE CANVAS IS THE COLOUR THE PAGE ENDS ON. Founder-directed: "fix the end
   of footer in mobiles".

   The footer slab ends EXACTLY at the document end — measured, there is no
   stray element under it — so the pale strip below it was the browser's own
   overscroll, revealing the canvas. The canvas takes `html`'s background when
   it has one and `body`'s otherwise, and body's was `--paper`: a light band
   under a black footer every time the page is bounced past its end.

   Setting it on `html` moves ONLY the overscroll. Body still paints `--paper`
   across the whole document, so nothing inside the page changes — verified
   against the rendered page rather than reasoned about.

   ⚠️ IT IS ONE COLOUR FOR BOTH ENDS. There is no way to give the top and the
   bottom of the canvas different colours; a gradient on `html` is sized to the
   document, not to the bounce. So this is a choice, not a fix that costs
   nothing: the page ends on black and starts light, and the end is the one
   being pulled past. A pull-down at the very top now shows black above the
   hero. iOS Safari has no pull-to-refresh, so that gesture is rare there; if it
   ever looks wrong, the alternative is `overscroll-behavior-y: none`, which
   removes the bounce altogether and changes how the page feels. */
html { background: var(--slab); }

body {
  background: var(--paper);
  color: var(--ink-2);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color-scheme: light;
}

h1, h2, h3, h4 {
  margin: 0; color: var(--ink);
  font-family: var(--display); font-weight: var(--w-display);
  letter-spacing: var(--track); text-wrap: balance;
}
h1 { font-size: var(--t-mega); line-height: 0.98; }
h2 { font-size: var(--t-xl);   line-height: 1.02; }
h3 { font-size: var(--t-lg);   line-height: 1.08; letter-spacing: -0.028em; }
h4 { font-size: var(--t-md);   line-height: 1.2;  letter-spacing: -0.02em; }
/* blockquote, figure, ul and ol carry UA margins too — blockquote's default
   margin-inline-start: 40px silently indented the quote 40px off the panel it
   sits under, and no author rule was responsible. Reset the set, not just p. */
p, blockquote, figure, ul, ol, dl { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
::selection { background: var(--brand-wash); color: var(--ink); }
:focus-visible { outline: 2px solid var(--brand-hot); outline-offset: 3px; }

.mono {
  font-family: var(--mono); font-size: var(--t-mono);
  letter-spacing: 0.05em; color: var(--ink-4);
  font-variant-numeric: tabular-nums;
}
.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

.wrap { max-width: var(--max); margin-inline: auto; padding-inline: var(--gutter); }
.sec  { padding-block: var(--s8); position: relative; }
.sec--tight { padding-block: var(--s6); }

/* ⚠️ THE SECTION RHYTHM IS NOT THE SAME ON A PHONE. `--s8` is 128px at every
   width, so a phone spent 256px — a third of an iPhone screen — on the gap
   between two sections, six times over. White space is proportional to the
   surface it sits on; a fixed value is only ever right at one width.

   Done HERE, on the section rule, rather than by shrinking `--s7`/`--s8` in
   the token file. The tokens are load-bearing inside solved compositions — the
   hero's numbers were measured rather than chosen and several of them are
   spacing tokens — so moving the scale globally would silently re-open every
   one of those. This moves the page's rhythm and nothing else. */
@media (max-width: 760px) {
  .sec { padding-block: var(--s6); }
  .sec--tight { padding-block: var(--s5); }
}

/* ============================================================
   A BLACK SLAB.  Pair with `data-theme="dark"` on the same element.

   The attribute does the work: dark.css already carries the whole token layer
   AND the paper scope that restores light tokens inside white artefacts, so a
   section drops onto black without anyone hunting down its text colours. Doing
   it by hand is how 77 elements went light-on-white the first time the dark
   theme went page-wide.

   The greys are overridden NEUTRAL here, founder-directed. dark.css's inks are
   warm by design (#EDE7E1, #9A9089, #7E756E) — built to sit under a mocha
   accent on the old all-dark page — and on a black slab inside a light page
   that warmth reads as a colour cast rather than as a family. De-tinted at
   matched or better luminance, so it is a hue change and not a contrast one:
   #EDE7E1 → #FFFFFF goes 15.42 → 18.93, #9A9089 → #9A9A9A stays 6.72. Every
   value has a channel spread of ZERO, which is the only definition of "not
   warm" that can be measured rather than argued about.
   --accent-ink is deliberately left alone: the accent is a signal, not body
   text, and a mocha eyebrow on black is the one warm thing that should stay. */
.sec--ink { background: var(--slab);
  /* THE SLIGHTEST WARM TINT, founder-directed — and the heading takes the same
     token as the panels below it (--paper-warm, #FFFEFC) rather than a hex that
     merely looks like them, so the two cannot drift apart in a later edit.
     Pure #FFFFFF was a correction of the ORIGINAL warm cream (#EDE7E1), which
     read as mocha; this lands between the two. Channel spread 3, against 12 for
     the cream it replaced — warm enough to belong to this palette, far too
     little to read as a colour.
     The greys carry the same 3-unit bias rather than a bigger one, so the whole
     ramp is tinted by the same amount instead of the heading being white and
     the body being beige. */
  --ink:   var(--paper-warm); --ink-2: #C8C6C4; --ink-3: #9C9B99; --ink-4: #858381;
  /* ⚠️ AND `color`, NOT ONLY THE TOKENS. `color` inherits as a RESOLVED value:
     `body` computes `var(--ink-2)` to #3F3F3F once, and every descendant
     inherits that grey — re-pointing the token inside the slab does nothing for
     anything that never declares `color` of its own. The home page hid this
     because its footer's legal row declares one; the first page that did not
     load home.css painted #3F3F3F on black at 1.99:1, invisible.
     Declaring `color` here re-resolves the token at the slab, so anything
     dropped on the black is legible by default rather than by luck. */
  color: var(--ink-2); }

/* ⚠️ THE BRAND BUTTON INVERTS ON A SLAB. On the light page `--brand` is the
   green signal and white type on it is correct; under `data-theme="dark"` the
   same token becomes the MOCHA (#DECBB6), which is a light colour — so the
   button kept its white label and measured 1.61:1. Mocha is the right fill on
   black; the label has to be ink, not white. 12:1 rather than 1.61.

   ⚠️ AND IT IS A GRADIENT NOW, founder-directed. The two stops are the ENDS OF
   THE SITE'S OWN RAMP — `--paper-warm` and `--accent`, the same pair the hero
   and section 02 run between — rather than two colours picked for a button. A
   button with its own private palette is how a page ends up with four warms
   that nearly match.
   It is safe at every point along it because the label is ink: #0F0F0F measures
   18.9:1 on the pale end and 12:1 on the mocha, so there is no position on the
   ramp where the type is the marginal one. That is the whole reason a gradient
   fill is defensible here and would not be under a white label. */
.sec--ink .btn--brand {
  color: #0F0F0F;
  background: linear-gradient(135deg, var(--paper-warm) 0%, #F1E4D8 45%, var(--accent) 100%);
}
.sec--ink .btn--brand:hover {
  background: linear-gradient(135deg, #FFFFFF 0%, #F6EDE4 45%, #EADCCB 100%);
}

/* the footer's slab: full-bleed, and it supplies the vertical rhythm the
   <footer> itself cannot because it is a width-capped column.
   ⚠️ padding-TOP is deliberately small. The close section above it is on the
   same black, so the two read as one field — and the gap between them was the
   SUM of three values nobody was looking at together: `.sec`'s 128px bottom
   padding, this slab's 96px top, and the close's own trailing space. 242px of
   nothing between the last line and the footer. Change either number and
   re-measure the gap, not the number. */
.foot-slab { padding-block: var(--s5) var(--s5); }

/* THE CLOSE, tightened. Founder-directed: smaller and nearer the footer.
   The heading was the global h2 clamp, which tops out at 60px — right for a
   section that opens an argument, too loud for one that ends the page with a
   single ask. The bottom padding comes off for the same reason the slab's top
   did: black meeting black should not have 128px of it. */
#book h2 { font-size: clamp(26px, 3.4vw, 42px); }
#book { padding-bottom: var(--s5); }

/* ⚠️ SECTION 05 RAMPS INTO THE BLACK. Founder-directed: white at the top,
   the footer's --slab at the bottom, so the page darkens INTO the close rather
   than hitting a hard edge at it.
   The stops are weighted, not even. Its content ends about 83% down, so a
   linear ramp would put real text on a mid-brown ground that no single ink
   colour survives — the same trap the marquee label fell into. Held at paper
   through the top half, the descent lives in the last third where there is
   nothing but padding, and it arrives at exactly the value #book starts on so
   the two meet with no seam. */
/* ============================================================
   THE ENDING — a direct section cut.  Founder-locked.

   Section 04 carries the site's white-to-mocha ramp and simply STOPS; the close
   and the footer are both --slab. No fade, no crossing headline.

   Three attempts came before this one and all of them were trying to apologise
   for an edge rather than make one: a ramp squeezed into a single section (too
   harsh, because the distance was too short), the ramp spread across two (which
   worked but needed the close inverted to light ink), and the closing headline
   split across the boundary (which worked, and cost a stacking context, two
   clipped copies of the same sentence, and a right-hand column that had to be
   re-aligned to stop it straddling the line). A cut needs none of that. It is
   also the only version with no mid-ground for a grey to fail on, because there
   is no mid-ground.
   ============================================================ */
/* ⚠️ GENTLER THAN THE HERO'S, and on purpose. The hero runs white to the full
   mocha over ~950px; this section is a 200vh pinned container, nearly twice
   that, so the SAME four stops put its content in the deep end of the ramp for
   most of the scroll — the section read as heavy brown rather than as a page
   warming up. Holding near-white through the first half and stopping short of
   the full mocha keeps the copy on a light ground the whole way down, which is
   what the ramp is for. */
#trades { background: linear-gradient(180deg,
  #FFFFFF 0%, #FDFBF8 34%, #F7EEE4 68%, #E9DCCC 100%);
  /* ⚠️ the section ends on the mocha, which is far darker than the paper
     --ink-3/--ink-4 were solved against — and #585858, which clears on the
     nominal #DECBB6, measured 4.39 against the pixel actually painted here.
     Solve against the pixel, not the token. */
  --ink-3: #525252; --ink-4: #525252; }

.lede { font-size: var(--t-md); line-height: 1.45; color: var(--ink-3); letter-spacing: -0.008em; }


/* ---- THE DELIBERATE TAP STATE ------------------------------------------
   What replaces the OS flash. It is a colour shift on the thing that was
   pressed, not a block painted over it: the ink deepens for the length of the
   press and returns. `:active` only — no hover equivalent, because on a touch
   screen hover is a state that never ends. */
.btn:active, .cta:active, .cta2:active { transform: translateY(1px); }
@media (prefers-reduced-motion: reduce) {
  .btn:active, .cta:active, .cta2:active { transform: none; opacity: 0.85; }
}


/* ============================================================
   PAGE-TO-PAGE TRANSITIONS.

   Founder-directed: "animate page changes cause its harsh appear rn". Every
   navigation was an instant repaint — the old page vanished and the new one
   was simply there, which on a phone reads as a flicker rather than as a move.

   ⚠️ NATIVE CROSS-DOCUMENT VIEW TRANSITIONS, NOT A SCRIPTED FADE. The scripted
   version of this is the reason so many sites feel broken: it has to CANCEL the
   click, fade the page out, then navigate — so if the script fails, is still
   loading, or the fade-out never completes, the reader is left looking at a
   half-faded page that no longer responds to the link they tapped. The browser
   version cannot do that. It snapshots the outgoing page itself, navigates
   normally, and animates once the new document is ready; a browser that does
   not support it simply navigates, which is exactly what happens today.

   ⚠️ IT NEEDS THE RULE IN BOTH DOCUMENTS — the one being left and the one being
   entered — which is why it lives in base.css, the only stylesheet every page
   on this site loads. Putting it in a page-specific file would give a
   transition that works in one direction and not the other.

   Same-origin navigations only, and never on the first load: arriving from
   outside is not a "change", and animating it would delay the first paint of
   the page someone is waiting for.
   ============================================================ */
@view-transition { navigation: auto; }

/* Out is faster than in, and only the incoming page moves. A symmetrical
   cross-fade reads as a dissolve — two pages briefly on screen at once. This
   reads as one page replacing another: the old one is gone before the new one
   has finished arriving, and the 6px rise gives it a direction. */
::view-transition-old(root) { animation: vt-out 120ms var(--ease) both; }
::view-transition-new(root) { animation: vt-in 260ms var(--ease) both; }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(6px); } }

/* ⚠️ THE WHOLE FEATURE COMES OFF UNDER REDUCED MOTION, rather than the
   animations being set to `none`. Cancelling the keyframes alone leaves the
   browser's own default cross-fade running, so the reader who asked for less
   motion would still get a transition — just not the one written here. */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}
