/* ==========================================================================
   AdminLess, agency.adminless.ai, the landing page

   ROUND 5 (2026-09-17). This file is the family's REFERENCE BUILD of the
   visual system in website/ROUND5.md §7. Six sections, four background
   tiers, one five-step type scale, two families, one warm second accent.

   What round 5 removed, and why it is not coming back:
   - IBM Plex Mono, its four @font-face rules and its four woff2 files. The
     page had three families doing two jobs. Label roles (nav, buttons, form
     labels, footer, credentials) are Plex Sans 500/600 at <= 0.06em, and
     --font-label survives as a TOKEN NAME ONLY because the shared
     book-picker.css consumes it; it resolves to the Plex Sans stack.
   - The 560x570 hero drawing (.story, .sig-*, .hd*, .b1/.b2/.b3-*, its two
     keyframe families and its Replay button) and the 177 words of labels
     inside it. The arithmetic it was drawing is §9 now: one legible row.
   - .eyebrow. Ten section labels above ten headings said what the heading
     already said. The hero's audience line is .hero__who, and it is
     sentence case: it is a sentence about the reader, not a label.
   - .cnt / .is-counting and the count-up it needed. The grant figure sits in
     a sentence now, and a number that animates inside prose is a toy.
   - .steps/.step (the five steps) and .closing. Both moved to the one-pager
     in website/collateral/.
   - book.css. It existed only to undo the ungated `opacity: 0` below; §7
     gates it on html.js, so the file and its <link> in book.html are gone.
     THE HAZARD IT DOCUMENTED IS STILL REAL, so it is recorded here: the
     `.is-in` class is set by the inline controller at the end of
     index.html, while html.js is stamped by the external motion.js. Drop
     'unsafe-inline' from script-src in nginx.conf while that controller is
     still inline and html.js arrives while .is-in never does, which blanks
     every section heading. Externalise the controller before tightening the
     CSP. A JS-OFF reader is safe either way: no html.js, nothing hidden.

   Tokens and devices from website/LANDING_PAGES.md; sections from
   website/ssa-landing/DESIGN_SPEC.md §4 and its Round 5 appendix.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Type, self-hosted. No Google Fonts request: this family sells keeping
      your data in Singapore, and a third-party font origin is a third party.
      TWO FAMILIES: Fraunces (display, h1, h2, h3, the figures) and IBM Plex
      Sans (everything else, labels included).

      ⚠ THESE FILES ARE SUBSET TO WHAT THIS SITE SETS, AND THAT HAS TEETH.
      Each face carries only the characters the four pages use, Fraunces
      keeps opsz 16 to 72 and wght 400 to 500, Plex Sans keeps wght 400 to
      600. SO: new copy in a new alphabet, a new weight, or a Fraunces size
      outside 16 to 72 needs the faces re-subset, or that text falls through
      to the next family in the stack. Round 5 copy therefore avoids the
      glyphs the subset never carried: no multiplication sign, no rightwards
      arrow (the arrow in §9 is a stroked path, not a glyph), no em dash.
      NOTHING USES THE ITALIC ANY MORE. It was subset to the 17 characters in
      the hero drawing's three pencil notes, and the drawing is gone. The two
      files stay declared and are never fetched, because an unmatched
      @font-face costs no request: anyone who wants italic on this site must
      re-subset first, or get Georgia.
      Re-subset with pyftsubset (fonttools + brotli), keeping tnum.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: Fraunces;
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("fonts/fraunces-var-latin.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: Fraunces;
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("fonts/fraunces-var-latin-ext.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;
}
@font-face {
  font-family: Fraunces;
  font-style: italic;
  font-weight: 400 500;
  font-display: swap;
  src: url("fonts/fraunces-var-italic-latin.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: Fraunces;
  font-style: italic;
  font-weight: 400 500;
  font-display: swap;
  src: url("fonts/fraunces-var-italic-latin-ext.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;
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("fonts/plex-sans-var-latin.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: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("fonts/plex-sans-var-latin-ext.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;
}

/* --------------------------------------------------------------------------
   1. Design tokens.
   -------------------------------------------------------------------------- */

/* THE PAPER RAMP, WIDENED (round 5). #FAF8F3 against #FFFDF9 is five units
   of 255: two bands that the design calls different and the eye calls one,
   which is how a page with four tiers came to read as two. The ramp is now
   spread over the same warm cream - NO NEW HUE, the only other colour on the
   page is still the green - so paper against raised is 13-24 units and a
   band edge is a band edge. Every ratio quoted below was re-measured against
   these values, not the old ones. */
:root {
  --paper:   #F2EDE1;   /* the page's ground */
  --paper-2: #FFFDF9;   /* raised surface on paper */
  --paper-3: #E8E1D2;   /* recessed surface (the footer) */
  --ink:     #1F1D1A;
  --accent:  #24504A;   /* deep green. Green means AdminLess. */
  --accent-deep: #1B3D38;
  --muted:   #8C877D;   /* decoration, hairlines, never body text */
  /* one step deeper than round 4's #6B655C: the ground it sits on came down
     13 units, and this is what keeps it over 4.5:1 on the tint band (5.00)
     as well as on paper (5.49) and in the footer (4.92). */
  --muted-ink: #645E55; /* muted at text sizes */
  --rule:    #D8D1C0;   /* a hairline that is still a hairline on #F2EDE1 */
  --on-accent: #FFFFFF;
  --logo-blend: multiply;
  --logo-filter: none;

  /* THE SECOND ACCENT (round 5 §7). Warm, and nothing like the green: green
     is AdminLess, this is emphasis. It was a duplicate of #24504A, which
     meant the page had one accent pretending to be two.
     --accent2-ink is the text colour and clears 4.5:1 on every tier it can
     sit on, re-measured on the widened ramp, WCAG 2.1, against the opaque
     result of its own 12% wash over each: paper 5.24, raised 5.93, tint 4.81
     (bare tier: 6.28 / 7.23 / 5.73). --accent2-lift is the same role inside
     the inverse band, where a dark ink cannot go: 6.39 on the green, 5.18 on
     its own 10% ground. Change either and re-run the ratios. */
  --accent2:     #8F3E14;
  --accent2-ink: #8F3E14;
  --accent2-lift: #F7D3A8;

  /* FOUR BACKGROUND TIERS. Every section carries exactly one, no two
     adjacent sections share one, and exactly one section is inverse.
     --bg-inverse is the accent's hex written out, NOT var(--accent): the
     inverse band re-points --accent for its own contents, and a token that
     referred to it would resolve to the re-pointed value and paint the band
     the wrong colour. Keep the two in step by hand.
     The tint is 6%, not 8%: over the deeper paper 6% already lands 12 units
     below it, and 8% took --muted-ink under 4.5:1 on that band. */
  --bg-paper:   var(--paper);
  --bg-raised:  var(--paper-2);
  --bg-tint:    color-mix(in srgb, var(--accent) 6%, var(--paper));
  --bg-inverse: #24504A;

  /* Every anything-a-user-can-type boundary. --rule is a hairline for
     dividers; a control's edge is meaningful UI, so it is derived from ink
     and paper instead and clears 3:1. */
  --field-line: color-mix(in srgb, var(--ink) 55%, var(--paper));

  --font-display: Fraunces, "Iowan Old Style", Georgia, serif;
  --font-body: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  /* A TOKEN NAME, NOT A FAMILY. book-picker.css is byte-identical on three
     sites and asks for --font-label; on this site the answer is Plex Sans. */
  --font-label: var(--font-body);
  --display-weight: 400;         /* h1, h2, the figures */
  --display-weight-strong: 500;  /* h3 and the smaller display sizes */
  --display-style: normal;

  /* THE TYPE SCALE, FIVE STEPS, AND THAT IS ALL THERE IS.
     Every font-size in this file, and in any file that styles this site, is
     one of these five. There is no sixth size and no raw px size: a page
     that has fourteen sizes has no scale, and round 4 had fourteen.
       --fs-1  the h1, and the pilot fee on the green band
       --fs-2  h2, and any figure that is the evidence (a price, a count)
       --fs-3  h3, a price, a name, a question
       --fs-4  all prose: lede, body, rows, buttons, inputs
       --fs-5  labels and fine print
     Emphasis is NEVER a size: it is weight + colour + ground (.em, §3).

     ⚠ --fs-1 IS 58px ON THE DESK, NOT 72, AND IT IS A MEASUREMENT. The
     promise of this hero's composition is that the copy sits on PAPER while
     the desk is a photograph beside it, which means the headline has to
     finish inside the veil's solid 46%. The longest beat, "More people
     helped.", is 8.95em of Fraunces at wght 400 less 0.02em of tracking a
     character: 495px at 58, 594px at 72. The solid zone leaves 518px at
     1440, 525px at 1280 and 520px at 1200, so 72 wraps the beat and gives a
     four-line h1 at every desk width and 58 does not. Re-measure before
     raising it, and re-measure against the LONGEST BEAT, not the shorter
     one. --fs-1 is also the pilot fee's size on the green band: that figure
     and the h1 are never on screen together. */
  --fs-1: 58px;
  --fs-2: 42px;
  --fs-3: 23px;
  --fs-4: 18px;
  --fs-5: 13px;
}

/* One mobile override of each of the five. --fs-1 is a measured curve, not a
   step: 44px was sized before the hero gained its 28px gutters and broke the
   two beats into four lines at 375. The longest beat fits one line up to
   0.104 x (viewport - 56px of gutter), measured at 320/360/375/414, so the
   curve is the viewport MINUS a constant, which a bare vw cannot express. At
   481px and up it reaches the 44px charter size and stops. */
@media (max-width: 880px) {
  :root {
    --fs-1: clamp(26px, calc(10.4vw - 6px), 44px);
    --fs-2: 30px;
    --fs-3: 21px;
    --fs-4: 17px;
    --fs-5: 12.5px;
  }
}

/* The middle tier tops out at the same 58px for the same reason: at 1199 the
   copy column is 597px and the longest beat needs 495. */
@media (min-width: 881px) and (max-width: 1199px) {
  :root { --fs-1: clamp(44px, 5vw, 58px); }
}

/* --------------------------------------------------------------------------
   2. Paper, grain, the grid, registration marks
   -------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-snap-type: y proximity;
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
    scroll-snap-type: none;
  }
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
}
/* site.js adds this for the length of one jump: a fragment that arrives on
   the URL is honoured instantly, never animated across the whole page. */
html.no-smooth { scroll-behavior: auto; }

body {
  margin: 0;
  background: var(--bg-paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-4);
  line-height: 1.6;
  overflow-x: clip;
}

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

/* Text selection wears the page's own colours, not the browser's blue. The
   background is the accent at 22% over paper written out flat (#24504A ×
   0.22 + #F2EDE1 × 0.78), never an alpha over text. Re-derived when the
   paper ramp widened. The inverse band gets the same recipe in its own
   terms: accent2-lift at 22% over the green, so the white ink stays
   readable on it. */
::selection { background: #C5CAC0; color: var(--ink); }
.tier--inverse ::selection { background: #526D5F; color: var(--ink); }

/* Fine paper grain, inline feTurbulence, ~4%, one layer for the whole page */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  opacity: 0.04;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/></filter><rect width='200' height='200' filter='url(%23g)'/></svg>");
}

.wrap {
  position: relative;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 clamp(24px, 4vw, 40px);
  box-sizing: border-box;
}

/* registration-mark crosshairs at the grid corners */
.regs { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.regs i { position: absolute; width: 13px; height: 13px; }
.regs i::before,
.regs i::after { content: ""; position: absolute; background: var(--rule); }
.regs i::before { left: 6px; top: 0; width: 1px; height: 13px; }
.regs i::after  { top: 6px; left: 0; height: 1px; width: 13px; }
.regs i:nth-child(1) { left: -7px;  top: -7px; }
.regs i:nth-child(2) { right: -7px; top: -7px; }
.regs i:nth-child(3) { left: -7px;  bottom: -7px; }
.regs i:nth-child(4) { right: -7px; bottom: -7px; }

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 80;
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 12px 18px;
  text-decoration: none;
}
.skip:focus { left: 12px; top: 12px; }

/* --------------------------------------------------------------------------
   3. Type primitives
   -------------------------------------------------------------------------- */

.h1 {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: var(--display-weight);
  font-style: var(--display-style);
  font-size: var(--fs-1);
  line-height: 0.98;
  letter-spacing: -0.02em;
  margin: 0;
  color: var(--ink);
}
/* Four words, two beats: each beat owns a line, so the headline never wraps mid-phrase. */
.h1 .h1__beat,
.h1 .h1__upright { display: block; }

.h2 {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: var(--display-weight);
  font-style: var(--display-style);
  font-size: var(--fs-2);
  line-height: 1.12;
  letter-spacing: -0.012em;
  margin: 0;
  /* every heading is a second-person sentence now: balance keeps the second
     line off a widow */
  text-wrap: balance;
  color: var(--ink);
}

.h3 {
  font-family: var(--font-display);
  font-weight: var(--display-weight-strong);
  font-style: var(--display-style);
  font-size: var(--fs-3);
  line-height: 1.2;
  letter-spacing: -0.008em;
  margin: 0;
  color: var(--ink);
}

/* text-wrap: pretty on every prose role the page sets in a narrow measure.
   A ragged last word is a wrapped .em on a line of its own, and a wrapped
   .em used to be a second and third box; the browser's own better line
   breaker is what stops most of them happening at all. */
.lede { font-size: var(--fs-4); line-height: 1.55; margin: 20px 0 0; color: var(--ink); max-width: 48ch; text-wrap: pretty; }
.body { font-size: var(--fs-4); line-height: 1.6; color: var(--ink); margin: 0; text-wrap: pretty; }
.small { font-size: var(--fs-5); line-height: 1.55; color: var(--muted-ink); margin: 0; text-wrap: pretty; }

/* THE ONE EMPHASIS DEVICE, and it is never a size (§7). Weight, the warm
   accent, and a ground it sits on. ONE PHRASE PER SECTION: a page with four
   highlights per band has none. Never inside a heading, the display face is
   subset to weights 400-500, so a 600 there falls through to Georgia. */
.em {
  font-weight: 600;
  color: var(--accent2-ink);
  background: color-mix(in srgb, var(--accent2) 12%, transparent);
  padding: 0.06em 0.24em;
  margin: 0 -0.08em;
  border-radius: 2px;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* --------------------------------------------------------------------------
   4. Nav + buttons. Label roles: Plex Sans 500/600, tracking <= 0.06em,
      uppercase only where the words are a label and not a sentence.
   -------------------------------------------------------------------------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg-paper);
  border-bottom: 1px solid var(--rule);
}

/* THE BAR OVER THE PLATE. On the desk the first screen is a photograph that
   runs under the bar (#hero pulls itself up by --navh and pads itself back
   down by the same), so the bar has nothing to sit on and no business
   painting a cream stripe across the top of the picture. It is transparent
   while the hero is still under it and takes paper + the hairline the moment
   the hero has gone past; site.js stamps .is-stuck off one
   IntersectionObserver on #hero.

   ⚠ GATED ON html.js, AND THE DIRECTION OF THE GATE IS THE POINT. Paper is
   the DEFAULT and transparency is what JavaScript adds. With no JavaScript
   the bar never turns transparent, so it can never end up as ink-on-deep-
   green over the pricing band - a no-JS reader gets the round-4 bar, which
   works everywhere. The labels go to full ink while the bar is on the
   plate: --muted-ink is a colour measured against paper, and a photograph
   is not paper. */
html.js .nav {
  background: transparent;
  border-bottom-color: transparent;
  transition: background-color 160ms cubic-bezier(.23, 1, .32, 1),
              border-color 160ms cubic-bezier(.23, 1, .32, 1);
}
html.js .nav.is-stuck {
  background: var(--bg-paper);
  border-bottom-color: var(--rule);
}
html.js .nav:not(.is-stuck) .nav__links a:not(.btn) { color: var(--ink); }
/* Three items: the wordmark, the labels and the CTA. The wordmark's auto
   margin does the separating, so the container gap is only ever the one
   between the labels and the CTA - and below 880 the labels drop to a second
   row without the CTA following them. */
.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
  height: 78px;
}

.nav__mark {
  display: inline-flex;
  align-items: center;
  min-height: 44px;                 /* the family's hit-box floor, on every width */
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  margin-right: auto;
}
.nav__mark span,
.nav__mark .nav__slash { color: var(--accent); }

.nav__links { display: flex; align-items: center; gap: 28px; }

.nav__links a:not(.btn) {
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted-ink);
  text-decoration: none;
  padding: 12px 0;                  /* 33px -> 45px tall without moving the underline */
  border-bottom: 1px solid transparent;
  white-space: nowrap;
}
.nav__links a:not(.btn):hover { color: var(--ink); border-bottom-color: var(--accent); }
.nav__links a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 44px;
  padding: 12px 22px;
  border-radius: 2px;
  border: 1px solid transparent;
  font-family: var(--font-body);
  font-size: var(--fs-4);
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.btn--primary { background: var(--accent); color: var(--on-accent); }
.btn--primary:hover { background: var(--accent-deep); }

.btn--ghost { border-color: var(--field-line); color: var(--ink); background: transparent; }
.btn--ghost:hover { border-color: var(--accent); color: var(--accent); }

.btn--sm { min-height: 38px; padding: 9px 16px; font-size: var(--fs-5); font-weight: 600; }
.btn--block { width: 100%; }

/* ONE BUTTON, TWO LABELS, AND ONLY ONE OF THEM IN THE DOCUMENT AT A TIME.
   display:none is deliberate rather than a clip: the hidden span leaves the
   accessibility tree with it, so the button's accessible name is always
   exactly the label a reader can see. The swap is in the phone tier (§17). */
.btn__wide { display: inline; }
.btn__narrow { display: none; }

.link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  color: var(--accent);
  font-size: var(--fs-4);
  font-weight: 500;
  text-decoration: none;
  border-bottom: 1px solid var(--accent);
  align-self: center;
  padding-bottom: 1px;
}
.link:hover { color: var(--accent-deep); border-bottom-width: 2px; }
.link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.cta-row { display: flex; align-items: center; gap: 26px; margin-top: 28px; flex-wrap: wrap; }

/* --------------------------------------------------------------------------
   5. Logo rows (supported by, in the hero; the programmes in #why-us)
   -------------------------------------------------------------------------- */

/* THE HERO'S FOOT ROW. .hero__wrap is a two-row grid and this is the auto
   one, so the first screen ends on the four funders instead of on a pillow
   of empty paper under the copy: whatever slack the screen has becomes air
   BETWEEN the two rows, and the band's own foot air is this row's 26px of
   padding. The plate's veil goes to solid paper across its bottom 96px,
   which is the ground this row and its logo blends sit on. */
.supported {
  position: relative;
  display: flex;
  align-items: center;
  gap: 40px;
  padding: 22px 0 26px;
  border-top: 1px solid var(--rule);
}
.supported__label,
.creds__label {
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted-ink);
  white-space: nowrap;
}
.supported__logos { display: flex; align-items: center; gap: 44px; flex-wrap: wrap; }
.supported__logos img {
  height: 27px;
  width: auto;
  opacity: 0.82;
  filter: var(--logo-filter);
  mix-blend-mode: var(--logo-blend);
}
.supported__logos img.is-tall { height: 34px; }
.supported__logos img.is-square { height: 36px; }

/* --------------------------------------------------------------------------
   6. Section scaffolding and the four tiers
   -------------------------------------------------------------------------- */
.sec {
  position: relative;
  border-top: 1px solid var(--rule);
  scroll-margin-top: 0;
}

@media (min-width: 601px) {
  #hero,
  .sec[data-section] {
    scroll-snap-align: start;
    scroll-snap-stop: normal;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    box-sizing: border-box;
    padding: clamp(28px, 4vh, 60px) 0;
  }
  .sec[data-section] .sec__inner {
    width: 100%;
    position: relative;
  }
  #pricing {
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    box-sizing: border-box;
    padding-top: calc(var(--navh, 72px) + clamp(14px, 2.5vh, 28px));
    padding-bottom: clamp(14px, 2.5vh, 28px);
  }
  #pricing .sec__inner {
    padding: 0 !important;
  }
  #book {
    scroll-margin-top: var(--navh, 78px) !important;
    scroll-snap-align: none !important;
    min-height: auto;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    padding-top: calc(var(--navh, 72px) + 8px) !important;
    padding-bottom: 8px !important;
  }
  #book .sec__inner { padding: 0 !important; }
  #book .sec__head { margin-bottom: 4px; }
  #book .nextup { display: flex; gap: 24px; margin: 4px 0 8px; }
  #book .nextup li { margin: 0; flex: 1; font-size: 13px; }
  #book .bk__tabs { margin-bottom: 8px; }
  #book .bk__card { padding: 12px 18px; }
  #book .bk__panes { display: grid; grid-template-columns: 240px 1fr; gap: 32px; align-items: start; }
  #book #form-review { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 16px; }
  #book #form-review .field:first-child { grid-column: 1 / -1; }
  #book #form-review .field textarea { min-height: 42px; height: 42px; }
  #book #form-review .form__action { grid-column: 1 / -1; margin-top: 4px; }
  #book #form-review .btn { height: 40px; }
  #book .onepager { margin-top: 6px; padding: 6px 14px; }
  #book .onepager__fields { margin: 0; }
  #close {
    scroll-snap-align: start;
    min-height: calc(100svh - 100px);
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
}
@media (max-width: 600px) {
  html { scroll-snap-type: none; }
  #hero, .sec[data-section], #close {
    scroll-snap-align: none;
    min-height: auto;
    padding: 56px 0;
  }
}

/* #cases is unified into a single full-bleed photo section with green veil */

/* site.js moves focus to the section it scrolled to, so the next Tab starts
   there. The scroll IS the feedback: a ring drawn around a whole section is
   noise, and these targets are not tab stops (tabindex -1). */
#main:focus,
[data-section]:focus,
#form-onepager:focus { outline: none; }
/* the skip link's target: without the offset it lands under the sticky bar */
#main { scroll-margin-top: var(--navh, 78px); }

/* Round 4 spent 96px of padding holding a hairline apart from a hairline.
   A BAND needs less: the tier's own colour is the separation, and the
   one-screen floor above supplies the air. */
.sec__inner { position: relative; width: 100%; }

/* THE CLOSING BAND. The ask, repeated, and nothing else: the hero's own
   button and the footer's own email, on one line where there is room and
   stacked where there is not. It is a coda, not a room - no [data-section],
   no one-screen floor, its own tighter rhythm - so the page ends on the ask
   rather than trailing off into the legal address. */
.sec__inner--close {
  padding: 56px 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 20px 32px;
}
.close__mail {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--fs-4);
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
}
.close__mail:hover { border-bottom-color: var(--accent); }
.close__mail:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* One heading per section, and it is a sentence: 30ch, not 22, or a
   second-person sentence sets as four short lines. */
.sec__head { display: block; }
.sec__head .h2 { max-width: 30ch; }
.sec__head .small { margin-top: 12px; max-width: 60ch; }

/* --- the bands ------------------------------------------------------------
   Assigned in index.html in this order, cream alternating with the green:
     #hero    photograph (cream veil)   #work   cream
     #pricing DEEP GREEN, full bleed    #cases  cream + the second plate
     #why-us  tint                      #book   cream
     #faq     cream                     #close  tint
   #book and #faq share cream on purpose: the ask and the five questions
   before it are one conversation, and a colour change between them would
   say they are two. --bg-raised is a SURFACE colour now, not a band: the
   only things wearing it are the loop card and the booking card.          */
.tier--paper  { background: var(--bg-paper); }
.tier--raised { background: var(--bg-raised); }
.tier--tint   { background: var(--bg-tint); }

/* THE ONE INVERSE BAND (the price), and on the desk it is the darkest point
   of the page. Its contents are not restyled one by one: the tokens they
   already read are re-pointed, so the price rows, the grant, the rules and
   the figures all come out right. The accent role becomes the warm lift,
   because a deep green accent on a deep green ground is nothing at all.

   --ink IS CREAM, NOT WHITE (the desk). Pure white on #24504A is a
   screenshot of a slide; the page is printed on #F2EDE1 and the prices are
   the same paper, lit from behind. #F4F0E6 on #24504A is 7.9:1, and the
   inverse ::selection ground keeps it at 4.9:1. --on-accent stays #FFFFFF:
   that token is the label colour INSIDE an accent button, which is a
   different job. Change either and re-run both ratios. */
.tier--inverse {
  background: var(--bg-inverse);
  --ink: #F4F0E6;
  --muted-ink: color-mix(in srgb, var(--on-accent) 76%, var(--bg-inverse));
  --rule: color-mix(in srgb, var(--on-accent) 26%, var(--bg-inverse));
  --paper: var(--bg-inverse);
  --paper-2: color-mix(in srgb, var(--on-accent) 7%, var(--bg-inverse));
  --accent: var(--accent2-lift);
  --field-line: color-mix(in srgb, var(--on-accent) 55%, var(--bg-inverse));
  color: var(--ink);
}
/* the same phrase-level emphasis, in the one place a dark ink cannot go */
.tier--inverse .em {
  color: var(--accent2-lift);
  background: color-mix(in srgb, var(--accent2-lift) 10%, transparent);
}

/* ⚠ THE GROUND IS FOR A PHRASE THAT FITS ONE LINE. box-decoration-break
   clones the box onto every line the phrase takes, which is right on a desk
   (two boxes, one phrase) and falls apart in a 319px column, where "It stops
   being typed." came out as three separate tinted boxes on three lines and
   read as three separate things. Below 880 the ground is dropped for a 1px
   underline in the same ink: one continuous mark per line, no box to
   shatter, and the emphasis is still weight + colour + a drawn mark. */
@media (max-width: 880px) {
  .em {
    background: none;
    padding: 0;
    margin: 0;
    border-radius: 0;
    box-shadow: 0 1px 0 0 var(--accent2-ink);
  }
  .tier--inverse .em { background: none; box-shadow: 0 1px 0 0 var(--accent2-lift); }
}

/* --------------------------------------------------------------------------
   7. Section-level motion. One reveal, once, when the section is reached.

      ⚠ GATED ON html.js (round 5, ROUND5.md §7). The hidden state is what
      JavaScript undoes, so only a reader who HAS JavaScript may ever see it.
      Ungated, this file shipped a booking page with no heading and would
      blank every heading the moment the inline controller failed. Do not
      remove the html.js prefix, and do not add a rule that hides content
      without it.
   -------------------------------------------------------------------------- */

@keyframes rv-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }

/* Content is visible by default (no artificial scroll-reveal opacity delays) */

/* --------------------------------------------------------------------------
   8. hero
   -------------------------------------------------------------------------- */

/* THE FIRST SCREEN IS THE PHOTOGRAPH. The section pulls itself up under the
   sticky bar and pads itself back down by the same --navh, so the plate runs
   edge to edge AND top to bottom and there is no cream stripe above it. A
   screen tall, with a 720px floor for a short laptop window; a FLOOR, never
   a cap, so nothing clips and the wheel is still the reader's.
   align-content overrides [data-section]'s centring: the hero centres its
   own first row and pins the funders to the foot, which is not the same
   thing as centring the pair and leaving 200px of nothing underneath. */
.hero {
  position: relative;
  background: var(--bg-paper);
  margin-top: calc(-1 * var(--navh, 78px));
  padding-top: calc(var(--navh, 78px) + clamp(24px, 4vh, 44px));
  padding-bottom: clamp(44px, 6vh, 64px);
  scroll-margin-top: var(--navh, 78px);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}

.hero__wrap {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  width: 100%;
}

.hero__grid {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(16px, 2.6vh, 40px) 0;
}
.hero__copy  { order: 0; }
.hero__cycle { order: 1; margin-top: 16px; width: 100%; }
.hero__grid .cta-row { order: 2; margin-top: 20px; }

/* THE AUDIENCE LINE. Sentence case on purpose: it names who the page is for,
   which is prose, and §7 allows uppercase only on a genuine label. */
.hero__who {
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--accent);
  margin: 0 0 16px;
  max-width: 46ch;
  line-height: 1.4;
}
.lede { margin-top: 14px; }

@media (min-width: 881px) {
  .hero__grid {
    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(380px, 520px);
    grid-template-areas:
      "copy fig"
      "cta  fig";
    column-gap: clamp(48px, 4.5vw, 64px);
    row-gap: 24px;
    align-content: center;
    align-items: center;
    padding: clamp(28px, 4vh, 52px) 0;
  }
  .hero__copy  { grid-area: copy; align-self: end; position: relative; z-index: 10; max-width: 560px; }
  .hero__grid .cta-row { grid-area: cta; align-self: start; margin-top: 0; }
  .hero__cycle { grid-area: fig; align-self: center; margin: 0; width: 100%; max-width: 520px; }
  .lede { margin-top: 18px; max-width: 52ch; }
}

@media (min-width: 1200px) {
  .hero__grid {
    grid-template-columns: minmax(0, 560px) minmax(420px, 520px);
    column-gap: 56px;
    justify-content: space-between;
  }
}
/* --------------------------------------------------------------------------
   8-mono. Tactile Studio Monograph - Inset Architectural Matting
   -------------------------------------------------------------------------- */
.monograph-mat {
  position: relative;
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: clamp(12px, 1.8vh, 18px);
  box-shadow:
    0 2px 4px rgba(31, 29, 26, 0.04),
    0 18px 40px -18px rgba(31, 29, 26, 0.18);
  outline: 1px solid color-mix(in srgb, var(--rule) 50%, transparent);
  outline-offset: -5px;
}

.monograph-mat__frame {
  position: relative;
  border-radius: 2px;
  overflow: hidden;
  border: 1px solid rgba(31, 29, 26, 0.14);
  min-height: clamp(380px, 46vh, 470px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(14px, 2vh, 20px);
  background: var(--paper-3);
}

.monograph-mat__plate {
  position: absolute;
  inset: 0;
}
.monograph-mat__plate .plate__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 54% 52%;
}

.plate__veil--monograph {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(135deg, rgba(242, 237, 225, 0.68) 0%, rgba(242, 237, 225, 0.32) 50%, rgba(242, 237, 225, 0.62) 100%),
    radial-gradient(ellipse at 80% 20%, rgba(255, 253, 249, 0.3) 0%, transparent 70%);
}

.monograph-mat .hero__cycle {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 500px;
  margin: 0 auto;
  box-shadow: 0 16px 36px -16px rgba(31, 29, 26, 0.35);
}

.monograph-mat__caption {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 10px;
  padding: 0 4px;
  font-family: var(--font-label);
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted-ink);
}
.monograph-mat__tag {
  font-weight: 600;
  color: var(--accent);
}
.monograph-mat__desc {
  font-weight: 500;
  opacity: 0.85;
}

/* --------------------------------------------------------------------------
   8a. Hero cycle tabs & panels
   -------------------------------------------------------------------------- */
/* THE LOOP CARD, AND THE TABS ARE INSIDE IT. On the desk the card is the
   only thing on the page sitting on a photograph rather than on paper, so
   it is one object: a header row of three pills on a hairline, and the
   drawing under it. A strip of pills floating naked on a picture beside a
   plate was two objects pretending to be one.

   ⚠ THIS SHADOW IS THE PAGE'S ONE DECLARED ELEVATION. Nothing else on the
   page casts one - the plates inside the panels gave theirs up when the
   card took the frame, the price rows and the case rows are hairlines, and
   the booking card is a border with an accent edge. If something else ever
   needs to lift, this one comes off first. */
.hero__cycle {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 560px;
  margin-left: auto;
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: 4px;
  box-shadow: 0 22px 48px -24px rgba(31, 29, 26, 0.45);
}

/* The tabs: a 44px hit box on every width, drawn as a 28px pill, now the
   card's header row on a hairline. Transition on background-color (the
   pill) and transform (the press) only. */
.cycle-tabs {
  display: flex;
  align-items: center;
  column-gap: 6px;
  row-gap: 0;
  margin: 0;
  padding: 5px 10px;
  border-bottom: 1px solid var(--rule);
  flex-wrap: wrap;
}

.cycle-tab {
  position: relative;
  z-index: 0;
  min-height: 44px;
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--muted-ink);
  background: none;
  border: 0;
  padding: 0 12px;
  cursor: pointer;
  line-height: 1.3;
  user-select: none;
  -webkit-appearance: none;
  appearance: none;
  transition: transform 120ms cubic-bezier(.23, 1, .32, 1);
}
.cycle-tab:active { transform: scale(0.97); }

.cycle-tab::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 8px 0;
  background-color: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: 9999px;
  transition: background-color 180ms cubic-bezier(.23, 1, .32, 1);
}

/* Hover is a mouse thing: a touch has no hover, and a sticky :hover on the
   last-tapped pill would read as a second active tab. One step of paper
   shade on the pill, ink on the label. */
@media (hover: hover) and (pointer: fine) {
  .cycle-tab:not(.is-active):hover { color: var(--ink); }
  .cycle-tab:not(.is-active):hover::before {
    background-color: var(--paper-3);
    border-color: var(--field-line);
  }
}

.cycle-tab.is-active {
  color: var(--on-accent);
  font-weight: 600;
}
.cycle-tab.is-active::before {
  background-color: var(--accent);
  border-color: var(--accent);
}

/* THE FILL IS THE TIMER. A 2px bar along the active pill's bottom edge;
   site.js animates it scaleX 0→1 over the dwell (WAAPI) and advances when it
   lands, so the loop's rhythm is legible and a pause is visibly a pause.
   .cycle-tab__clip is the pill's inner box (inside the 1px border) with the
   pill's radius and overflow hidden, so the bar's ends follow the curve.
   The bar is a lighter step of the pill, not the accent itself: the active
   pill IS the accent. Inactive tabs never show it. */
.cycle-tab__clip {
  position: absolute;
  z-index: -1;
  inset: 9px 1px;
  border-radius: 9999px;
  overflow: hidden;
  pointer-events: none;
}
.cycle-tab__fill {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background-color: color-mix(in srgb, var(--on-accent) 60%, var(--accent));
  transform-origin: left center;
  transform: scaleX(0);
}
.cycle-tab:not(.is-active) .cycle-tab__fill { visibility: hidden; }

.cycle-tab:focus-visible { outline: none; }
.cycle-tab:focus-visible::before {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The card's body. Panels stretch rather than sit at the top, so every
   caption lands on the card's own foot line instead of hanging wherever
   its own drawing happens to end. */
.cycle-stage {
  display: grid;
  grid-template-columns: 1fr;
  align-items: stretch;
  position: relative;
  width: 100%;
  padding: 14px 16px 16px;
}

/* THE CROSSFADE, asymmetric. A panel leaves in 200ms (up 4px, 2px of blur)
   and the next arrives in 320ms (from 6px below, from 2px of blur): the
   exit is quicker than the entrance, and the blur bridges the two states so
   the eye is never asked to read both at once. Base = hidden, parked on the
   entrance's start frame, no transition, so a panel that has finished
   leaving snaps back to its start frame unseen. site.js stamps .is-leaving
   on the outgoing panel for the length of its exit. The blur lives on the
   panel only. */
.cycle-panel {
  grid-column: 1;
  grid-row: 1;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(6px);
  filter: blur(2px);
  width: 100%;
  display: flex;
}

.cycle-panel.is-active {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  position: relative;
  z-index: 1;
  transform: none;
  filter: none;
  transition:
    opacity 320ms cubic-bezier(.23, 1, .32, 1),
    transform 320ms cubic-bezier(.23, 1, .32, 1),
    filter 320ms cubic-bezier(.23, 1, .32, 1),
    visibility 0s;
}

.cycle-panel.is-leaving {
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  filter: blur(2px);
  transition:
    opacity 200ms cubic-bezier(.23, 1, .32, 1),
    transform 200ms cubic-bezier(.23, 1, .32, 1),
    filter 200ms cubic-bezier(.23, 1, .32, 1),
    visibility 0s linear 200ms;
}

/* Reduced motion: a 150ms opacity-only crossfade, no travel, no blur. The
   !important outranks the page-wide transition: none in §2, on purpose: an
   instant cut between two panels is harsher than a short fade. Nothing
   auto-advances and the fill never runs (site.js). */
@media (prefers-reduced-motion: reduce) {
  .cycle-panel { transform: none; filter: none; }
  .cycle-panel.is-active { transition: opacity 150ms linear, visibility 0s !important; }
  .cycle-panel.is-leaving { transition: opacity 150ms linear, visibility 0s linear 150ms !important; }
}


/* --------------------------------------------------------------------------
   9. THE THREE HERO DEVICES. One figure per panel (.vp): a drawing and a
      caption, inside the loop card. Each drawing has its own grammar:
        01 .sel  a SELECTION   five processes, one row picked and ticked
        02 .pr   a PROPORTION  two bars; the whole price, then the 80% share
        03 .ct   a CONTINUITY  one mark that travels; one box that leaves

      ⚠ THE PER-PANEL KICKER IS GONE (round 5, the desk). "01 · The outcome"
      sat 8px under a tab reading "01 Outcome", inside the same card, which
      is a label repeating the control that selected it. The tabs moved into
      the card, so the card's header row IS the kicker: the words survive
      exactly where a reader now reads them. .vp__plate keeps its class - it
      is the drawing's own box - but gives its frame, its ground and its
      shadow to the card, because a plate inside a card is two cards.

      MOTION IS TRANSITIONS, NOT KEYFRAMES. The stylesheet's base state is
      the FINISHED frame, so a reader with no JavaScript, or with reduced
      motion, sees every device complete and nothing ever runs. Under
      html.js + no-preference, a panel that is not .is-playing holds its
      START frame; site.js stamps .is-playing when the panel becomes active
      and each part transitions to the base state on its own delay. Only
      transform, opacity, stroke-dashoffset and background-color move.
   -------------------------------------------------------------------------- */
.vp {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  margin: 0;
  width: 100%;
  --ez: cubic-bezier(.23, 1, .32, 1);
}

.vp__plate { border: 0; background: none; border-radius: 0; padding: 0; box-shadow: none; margin-bottom: 14px; }

/* the caption: body size, the one .em per device, a caption measure. margin
   auto pushes it to the card's foot, so all three captions land on the same
   line however tall their own drawing is. */
.vp__cap {
  font-size: var(--fs-4);
  line-height: 1.5;
  color: var(--ink);
  margin: auto 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--rule);
  max-width: 42ch;
}

/* shared drawing hand: every drawn stroke is one dash unit long */
.sel__brk path, .sel__ring, .sel__check,
.ct__seat, .ct__link, .ct__frame, .ct__leaf {
  fill: none;
  stroke-dasharray: 1;
  stroke-dashoffset: 0;
}

/* --- 01 the selection ------------------------------------------------- */
.sel { list-style: none; margin: 0; padding: 0; }

.sel__row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "name  tick"
    "state tick";
  column-gap: 10px;
  align-items: center;
  padding: 6px 10px;
  border-bottom: 1px solid var(--rule);
  font-size: var(--fs-4);
  line-height: 1.3;
  color: var(--ink);
}
.sel__row:last-child { border-bottom: 0; }
.sel__row:not(.is-pick) { opacity: 0.4; }
.sel__row.is-pick { font-weight: 500; }

.sel__row:nth-child(1) { --d: 0.08s; }
.sel__row:nth-child(2) { --d: 0.14s; }
.sel__row:nth-child(3) { --d: 0.20s; }
.sel__row:nth-child(4) { --d: 0.26s; }
.sel__row:nth-child(5) { --d: 0.32s; }

.sel__name { grid-area: name; display: block; }

.sel__state {
  grid-area: state;
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.3;
  color: var(--accent);
}

.sel__tick { grid-area: tick; display: block; width: 22px; height: 22px; overflow: visible; }
.sel__ring  { stroke: var(--accent); stroke-width: 1.5; }
.sel__check { stroke: var(--accent); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* the bracket: two 8px arms pinned to the row's ends; the arms' height
   follows the row (preserveAspectRatio none) and the stroke stays 1.5px */
.sel__brk { position: absolute; top: 0; bottom: 0; width: 8px; height: 100%; display: block; overflow: visible; pointer-events: none; }
.sel__brk--l { left: 0; }
.sel__brk--r { right: 0; }
.sel__brk path { stroke: var(--accent); stroke-width: 1.5; }

@media (min-width: 481px) {
  .sel__row {
    grid-template-columns: minmax(0, 1fr) auto auto;
    grid-template-areas: "name state tick";
    column-gap: 12px;
  }
}

/* --- 02 the proportion ------------------------------------------------ */
.pr { display: grid; row-gap: 16px; }

.pr__head {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  column-gap: 12px;
  row-gap: 2px;
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.3;
  color: var(--muted-ink);
}
.pr__tag { color: var(--accent); white-space: nowrap; }

.pr__figure {
  margin: 4px 0 0;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: var(--fs-2);
  font-weight: var(--display-weight);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.pr__track {
  position: relative;
  height: 22px;
  margin-top: 6px;
  border: 1px solid var(--field-line);
  background: var(--paper);
  overflow: hidden;
}

.pr__fill { position: absolute; inset: 0; transform-origin: left center; }
.pr__fill--pilot { background: var(--accent); }
.pr__fill--grant { width: 80%; background: color-mix(in srgb, var(--accent) 38%, var(--paper-2)); border-right: 1px solid var(--field-line); }

.pr__legend {
  margin: 4px 0 0;
  display: flex;
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.3;
}
.pr__seg--ncss { flex: 0 0 80%; color: var(--accent); }
.pr__seg--you  { flex: 1 1 auto; padding-left: 6px; color: var(--muted-ink); }

/* --- 03 the continuity ------------------------------------------------ */
.ct { display: grid; row-gap: 8px; }

.ct__stations {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  text-align: center;
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.3;
  color: var(--muted-ink);
}
.ct__station:nth-child(1) { --d: 0.06s; }
.ct__station:nth-child(2) { --d: 0.12s; }
.ct__station:nth-child(3) { --d: 0.18s; }

.ct__row { display: block; width: 100%; height: auto; overflow: visible; }
.ct__seat { stroke: var(--muted); stroke-width: 1.2; stroke-linecap: round; }
.ct__seat:nth-child(1) { --d: 0.06s; }
.ct__seat:nth-child(2) { --d: 0.12s; }
.ct__seat:nth-child(3) { --d: 0.18s; }

/* ONE mark. It rests at PHONE: two nested hops of 100 units each. */
.ct__person { fill: var(--accent); transform-box: fill-box; transform-origin: center; }
.ct__hop1, .ct__hop2 { transform: translateX(100px); }

/* the trail it leaves: as the mark departs a seat, an outlined copy of the
   same mark stays behind at 35%, and a hairline joins seat to seat at
   shoulder height. Finished frame: two ghosts, one solid mark, one line. */
.ct__ghost { fill: none; stroke: var(--accent); stroke-width: 1.2; opacity: 0.35; }
.ct__link { stroke: var(--rule); stroke-width: 1; }

/* the box and the door: the row is the container, so the box's landing is
   its own width and the row's width, whatever the column measures */
.ct__own {
  position: relative;
  container-type: inline-size;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 4px;
}

.ct__box {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 10px;
  border: 1.5px solid var(--accent);
  border-radius: 3px;
  background: var(--paper-2);
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.3;
  color: var(--accent);
  white-space: nowrap;
  transform: translateX(calc(100cqw - 100% - 48px));
}
.ct__lock { width: 11px; height: 13px; display: block; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linejoin: round; }
.ct__tag {
  padding: 1px 5px;
  border-radius: 2px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11px;
  line-height: 1.3;
}

.ct__door { flex: none; display: block; width: 30px; height: 44px; overflow: visible; margin-left: auto; }
.ct__frame { stroke: var(--ink); stroke-width: 1.5; stroke-linejoin: round; }
.ct__leaf  { stroke: var(--muted-ink); stroke-width: 1.2; stroke-linejoin: round; }

/* Plan and price diagram flex layout */
.plan__desk { display: flex; align-items: flex-end; gap: 24px; }
.plan__sheet { flex: 0 0 100px; width: 100px; }
.plan__stamp { display: flex; flex-direction: column; gap: 8px; margin-left: 24px; }
.plan__tag { white-space: nowrap; }
.plan__same { white-space: nowrap; }

.ct__go {
  margin: 0;
  text-align: right;
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.3;
  color: var(--accent);
}

/* --- the draw. Start frames, then the transitions that leave them ------- */
@media (prefers-reduced-motion: no-preference) {
  /* 01: rows rise in on a 60ms stagger; at 0.58 the first row is picked -
     bracket draws, the rest sink to 40%; the tick's ring then check draw;
     the state label lands last. Done at 1.35s. */
  html.js .cycle-panel:not(.is-playing) .sel__name { opacity: 0; transform: translateY(5px); }
  html.js .cycle-panel:not(.is-playing) .sel__row:not(.is-pick) { opacity: 1; }
  html.js .cycle-panel:not(.is-playing) .sel__brk path,
  html.js .cycle-panel:not(.is-playing) .sel__ring,
  html.js .cycle-panel:not(.is-playing) .sel__check { stroke-dashoffset: 1; }
  html.js .cycle-panel:not(.is-playing) .sel__state { opacity: 0; }

  html.js .cycle-panel.is-playing .sel__name { transition: opacity 0.26s var(--ez) var(--d), transform 0.26s var(--ez) var(--d); }
  html.js .cycle-panel.is-playing .sel__row:not(.is-pick) { transition: opacity 0.30s var(--ez) 0.58s; }
  html.js .cycle-panel.is-playing .sel__brk path { transition: stroke-dashoffset 0.30s var(--ez) 0.58s; }
  html.js .cycle-panel.is-playing .sel__ring  { transition: stroke-dashoffset 0.30s var(--ez) 0.82s; }
  html.js .cycle-panel.is-playing .sel__check { transition: stroke-dashoffset 0.20s var(--ez) 1.06s; }
  html.js .cycle-panel.is-playing .sel__state { transition: opacity 0.25s var(--ez) 1.10s; }

  /* 02: bar one's head, then the bar fills and the figure lands. Bar two's
     head, then 80% shades in from the left; the two legends land. Done at
     1.40s. */
  html.js .cycle-panel:not(.is-playing) .pr__head,
  html.js .cycle-panel:not(.is-playing) .pr__seg { opacity: 0; }
  html.js .cycle-panel:not(.is-playing) .pr__figure { opacity: 0; transform: translateY(4px); }
  html.js .cycle-panel:not(.is-playing) .pr__fill { transform: scaleX(0); }

  html.js .cycle-panel.is-playing .pr__bar--pilot .pr__head { transition: opacity 0.25s var(--ez) 0.08s; }
  html.js .cycle-panel.is-playing .pr__fill--pilot { transition: transform 0.45s var(--ez) 0.15s; }
  html.js .cycle-panel.is-playing .pr__figure { transition: opacity 0.25s var(--ez) 0.70s, transform 0.25s var(--ez) 0.70s; }
  html.js .cycle-panel.is-playing .pr__bar--grant .pr__head { transition: opacity 0.25s var(--ez) 0.65s; }
  html.js .cycle-panel.is-playing .pr__fill--grant { transition: transform 0.45s var(--ez) 0.78s; }
  html.js .cycle-panel.is-playing .pr__seg--ncss { transition: opacity 0.25s var(--ez) 1.05s; }
  html.js .cycle-panel.is-playing .pr__seg--you  { transition: opacity 0.25s var(--ez) 1.15s; }

  /* 03: stations and seats on a stagger; the one mark pops in at PLAN,
     hops to BUILD, hops to PHONE, and each time it leaves a seat a ghost
     of it fades in there and a hairline draws to the next seat; the door
     draws; the box slides through it; the label lands. Done at 1.40s. */
  html.js .cycle-panel:not(.is-playing) .ct__station,
  html.js .cycle-panel:not(.is-playing) .ct__ghost,
  html.js .cycle-panel:not(.is-playing) .ct__box,
  html.js .cycle-panel:not(.is-playing) .ct__go { opacity: 0; }
  html.js .cycle-panel:not(.is-playing) .ct__seat,
  html.js .cycle-panel:not(.is-playing) .ct__link,
  html.js .cycle-panel:not(.is-playing) .ct__frame,
  html.js .cycle-panel:not(.is-playing) .ct__leaf { stroke-dashoffset: 1; }
  html.js .cycle-panel:not(.is-playing) .ct__person { opacity: 0; transform: scale(0.6); }
  html.js .cycle-panel:not(.is-playing) .ct__hop1,
  html.js .cycle-panel:not(.is-playing) .ct__hop2 { transform: none; }
  html.js .cycle-panel:not(.is-playing) .ct__box { transform: none; }

  html.js .cycle-panel.is-playing .ct__station { transition: opacity 0.25s var(--ez) var(--d); }
  html.js .cycle-panel.is-playing .ct__seat { transition: stroke-dashoffset 0.30s var(--ez) var(--d); }
  html.js .cycle-panel.is-playing .ct__person { transition: opacity 0.25s var(--ez) 0.18s, transform 0.25s var(--ez) 0.18s; }
  html.js .cycle-panel.is-playing .ct__hop1 { transition: transform 0.32s var(--ez) 0.52s; }
  html.js .cycle-panel.is-playing .ct__hop2 { transition: transform 0.32s var(--ez) 0.88s; }
  html.js .cycle-panel.is-playing .ct__ghost--plan  { transition: opacity 0.18s var(--ez) 0.52s; }
  html.js .cycle-panel.is-playing .ct__ghost--build { transition: opacity 0.18s var(--ez) 0.88s; }
  html.js .cycle-panel.is-playing .ct__link--1 { transition: stroke-dashoffset 0.32s var(--ez) 0.52s; }
  html.js .cycle-panel.is-playing .ct__link--2 { transition: stroke-dashoffset 0.32s var(--ez) 0.88s; }
  html.js .cycle-panel.is-playing .ct__frame { transition: stroke-dashoffset 0.30s var(--ez) 0.30s; }
  html.js .cycle-panel.is-playing .ct__leaf  { transition: stroke-dashoffset 0.25s var(--ez) 0.55s; }
  html.js .cycle-panel.is-playing .ct__box { transition: opacity 0.20s var(--ez) 0.15s, transform 0.38s var(--ez) 0.95s; }
  html.js .cycle-panel.is-playing .ct__go { transition: opacity 0.20s var(--ez) 1.20s; }
}

/* the phone fold: at 375 the primary CTA has to land inside the first
   screen, so every plate trims the same 20px on phones. 01 loses 4px a
   row; 02 loses the same across its gaps; 03 is the shortest and keeps
   its own. Type sizes do not change. */
@media (max-width: 560px) {
  .vp__plate { padding: 12px; }
  .vp__k { margin-bottom: 8px; }
  .vp__cap { margin-top: 10px; }
  .sel__row { padding: 3px 8px; }
  .pr { row-gap: 8px; }
  .pr__figure { margin-top: 2px; }
  .pr__track { margin-top: 4px; }
  .pr__legend { margin-top: 2px; }
}

@media (max-width: 420px) {
  .hero__grid { padding-top: 14px; }
  .cta-row { margin-top: 14px; }
}


/* --------------------------------------------------------------------------
   10. #work, the five jobs, one line each, on hairlines
   -------------------------------------------------------------------------- */

.p-list { margin-top: 24px; border-top: 1px solid var(--rule); }
/* Each row is an <a> into #book. The row keeps the type colours it had as a
   div - a five-row wall of link blue is not a list, it is a warning - and
   says it is a link AT REST, not only on hover: ::after is an accent chevron
   on the row's right edge, in the third track. Five rows that looked like
   prose and behaved like doors is the failure this closes; a pointer still
   gets the lift and the accent name on top of it, and the chevron steps one
   pixel further right. It is a pseudo-element, so the accessible name of
   each row is still its own two sentences and nothing is announced twice. */
.p-row {
  display: grid;
  grid-template-columns: 340px 1fr auto;
  gap: 40px;
  align-items: baseline;
  padding: 13px 0;
  border-bottom: 1px solid var(--rule);
  color: inherit;
  text-decoration: none;
  transition: background-color 0.15s ease;
}
.p-row::after {
  content: "";
  align-self: center;
  justify-self: end;
  width: 9px;
  height: 9px;
  border-top: 1.5px solid var(--accent);
  border-right: 1.5px solid var(--accent);
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}
.p-row:hover::after { transform: rotate(45deg) translate(2px, -2px); }
@media (prefers-reduced-motion: reduce) { .p-row::after { transition: none; } }
.p-row:hover { background: var(--bg-tint); }
.p-row:hover .p-row__name { color: var(--accent); }
.p-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
@media (prefers-reduced-motion: reduce) { .p-row { transition: none; } }
.p-row:last-child { border-bottom: 0; }
.p-row__name {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: var(--display-weight);
  font-style: var(--display-style);
  font-size: var(--fs-3);
  line-height: 1.2;
  letter-spacing: -0.008em;
  margin: 0;
  color: var(--ink);
}
.p-row__v {
  margin: 0;
  font-size: var(--fs-4);
  line-height: 1.45;
  color: var(--ink);
  border-left: 2px solid var(--accent);
  padding-left: 20px;
}

/* --------------------------------------------------------------------------
   11. #pricing, the pilot, the two monthly prices and the NCSS grant, in
       one band, on the page's only inverse tier - and on the desk that band
       is the darkest point of the page and the one place a figure is set at
       display scale. NOTHING IN HERE IS A CARD. Two bordered boxes side by
       side was the stock shape the critique named; the two hosting options
       are two columns under two hairlines, which is what a table of two
       prices actually is.
   -------------------------------------------------------------------------- */

/* THE FIGURE. "Two-month pilot:" at h3 size, "S$2,000." at --fs-1, sharing
   one baseline, with the sub-line beside them rather than under them so the
   band opens as one row instead of three stacked ones. */
.price-lead {
  margin-top: 30px;
  border-top: 2px solid var(--ink);
  padding: 26px 0 30px;
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(260px, 1fr);
  column-gap: 56px;
  row-gap: 16px;
  align-items: baseline;
}
.price-lead__fig {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: var(--display-weight);
  font-style: var(--display-style);
  font-size: var(--fs-3);
  line-height: 1.05;
  letter-spacing: -0.012em;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 14px;
}
.price-lead__num {
  font-size: var(--fs-1);
  letter-spacing: -0.03em;
  color: var(--ink);
}
.price-lead__sub { margin: 0; font-size: var(--fs-4); line-height: 1.5; max-width: 54ch; }

/* the two hosting options: two columns, each opened by its own hairline.
   The recommended one takes the warm lift on its rule - the one mark, in
   the one place a deep green accent would be invisible. */
.packs { display: grid; grid-template-columns: 1fr 1fr; gap: 26px 56px; margin-top: 0; }
.pack { border: 0; border-top: 1px solid var(--rule); background: none; padding: 18px 0 0; display: flex; flex-direction: column; }
.pack--lead { border-top: 2px solid var(--accent); padding-top: 17px; }
.pack__name {
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted-ink);
  margin: 0;
}
.pack__price {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-style: var(--display-style);
  font-size: var(--fs-2);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 10px 0 0;
}
.pack__price span { display: block; font-family: var(--font-body); font-weight: 400; font-style: normal; font-size: var(--fs-5); color: var(--muted-ink); letter-spacing: 0; margin-top: 4px; }
.pack__line { margin: 12px 0 0; font-size: var(--fs-4); line-height: 1.45; }

/* THE GRANT. Every figure in here is NCSS's, not ours, and the claim is
   scoped to NCSS members: it is quoted at exactly the scope round 4 agreed
   and neither round 5 nor the desk touched it. Only the frame moved, and on
   the desk the frame is gone: a bordered box inside a full-bleed colour
   band is a card on a poster. A rule and two columns say the same thing. */
.grantbox {
  margin-top: 30px;
  border: 0;
  border-top: 1px solid var(--rule);
  background: none;
  padding: 22px 0 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 300px);
  gap: 44px;
  align-items: start;
}
.grantbox p { margin: 0; font-size: var(--fs-4); line-height: 1.5; }
.grantbox p.grantbox__note { font-size: var(--fs-5); line-height: 1.5; color: var(--muted-ink); border-left: 1px solid var(--rule); padding-left: 20px; }

/* --------------------------------------------------------------------------
   12. #why-us, the team, the two agencies and the programmes, in one band
   -------------------------------------------------------------------------- */

.why { display: grid; grid-template-columns: 1fr 1fr; gap: 44px; margin-top: 26px; }
.why__col { border-top: 1px solid var(--rule); padding-top: 16px; }
.why__k {
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 10px;
}
.why__col p.body { color: var(--ink); }

/* The two agencies, directly under the plate that names them. Two hairline
   entries, not two bordered boxes: the heading is on a photograph and the
   evidence under it should read as two columns of a caption, which is what
   the copy is. margin-top 0 because .sec__inner--cases supplies the air. */
.cases { display: grid; grid-template-columns: 1fr 1fr; gap: 26px 56px; margin-top: 0; }
.case { border: 0; border-top: 1px solid var(--rule); background: none; padding: 18px 0 0; }
.case__word {
  font-family: var(--font-display);
  font-weight: var(--display-weight-strong);
  font-style: var(--display-style);
  font-size: var(--fs-3);
  letter-spacing: -0.01em;
  color: var(--accent);
  display: block;
  margin-bottom: 8px;
}
.case__line { margin: 0; font-size: var(--fs-4); line-height: 1.45; color: var(--ink); }

/* The credential foot: institutional provenance ledger.
   A 3-column architectural honors strip directly on the paper ground,
   followed by a quiet row of global accelerator cohort marks. */
.creds {
  margin-top: clamp(24px, 3.5vh, 40px);
  border-top: 1px solid var(--rule);
  padding-top: clamp(16px, 2.5vh, 24px);
  display: flex;
  flex-direction: column;
  gap: clamp(14px, 2vh, 20px);
}
.ledger-provenance {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 3vw, 36px);
}
.ledger-item {
  border-left: 2px solid var(--accent, #24504A);
  padding-left: clamp(10px, 1.5vw, 16px);
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.ledger-item:first-child {
  border-left-color: #A36B22; /* Warm gold/bronze accent for 2× winner */
}
.ledger-role {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #8F3E14;
}
.ledger-name {
  font-family: var(--font-display, serif);
  font-size: clamp(15px, 1.3vw, 18px);
  font-weight: 500;
  line-height: 1.25;
  color: var(--ink);
}
.ledger-programs {
  display: flex;
  align-items: center;
  gap: clamp(16px, 2.5vw, 28px);
  padding-top: clamp(10px, 1.6vh, 16px);
  border-top: 1px dashed var(--rule);
  flex-wrap: wrap;
}
.ledger-programs-label {
  font-size: 11.5px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted-ink, #5E594F);
  white-space: nowrap;
}
.ledger-logos {
  display: flex;
  align-items: center;
  gap: clamp(20px, 2.8vw, 36px);
  flex-wrap: wrap;
}
.ledger-logos img {
  height: 20px;
  width: auto;
  opacity: 0.85;
  filter: var(--logo-filter, grayscale(1) contrast(1.15));
  mix-blend-mode: var(--logo-blend, multiply);
  transition: opacity 0.2s ease, filter 0.2s ease;
}
.ledger-logos img:hover {
  opacity: 1;
  filter: none;
}
@media (max-width: 680px) {
  .ledger-provenance {
    grid-template-columns: 1fr;
    gap: 14px;
  }
}
/* --------------------------------------------------------------------------
   13. #book, the booking card and the one-pager, in one band
   -------------------------------------------------------------------------- */

.book { display: grid; grid-template-columns: minmax(0, 1fr); margin-top: 18px; }

/* ⚠ CONTAINMENT, NOT DECORATION. A grid item's automatic minimum is
   min-content, so anything inside .cal that refuses to shrink - a day strip,
   a 7-column month, a long label - sizes the TRACK instead of overflowing
   its own box, and a track wider than the viewport drags the whole document
   sideways. The card's interior belongs to #picker (book-picker.css), which
   is edited on its own schedule; this floor is what keeps a change in there
   from reaching the document. */
.book > * { min-width: 0; }

/* what the call actually is, the two lines that stop an ED hesitating.
   The third line ("you leave with a straight no") is the section heading
   now, and nothing on this page is said twice. */
.nextup { list-style: none; margin: 20px 0 0; padding: 0; border-top: 1px solid var(--rule); max-width: 66ch; }
.nextup li {
  display: grid;
  grid-template-columns: 26px 1fr;
  gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--rule);
  font-size: var(--fs-4);
  line-height: 1.45;
  color: var(--ink);
}
.nextup li::before {
  content: attr(data-n);
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--accent);
  padding-top: 4px;
}

/* THE BOOKING CARD TAKES THE WHOLE BAND. Measured, not preferred: the picker
   lays DATE and TIME out side by side at and above 1040px of card width and
   stacks them below it. Across the band it gets 1152 - 56 = 1096px and lays
   out in two columns. The month grid and the time chips belong to #picker;
   every rule they need is in book-picker.css, which is byte-identical on all
   three sites. Do not re-add chip styling here. */
.cal {
  border: 1px solid var(--accent);
  box-shadow: inset 3px 0 0 var(--accent);
  background: var(--paper-2);
  padding: 20px 28px 22px;
}
.cal__note { margin: 0; font-family: var(--font-label); font-size: var(--fs-5); letter-spacing: 0.01em; color: var(--muted-ink); }

/* THE BAIL STATE IS A COMPOSITION, NOT AN APOLOGY (book.js adds .bk--bailed
   to #picker whenever availability fails, answers nothing bookable, or runs
   past its five seconds).
   Three things this has to be, because the band it lives in is the page's
   one ask. The STAGE HEIGHT IS RESERVED, so a card that cannot show a month
   grid does not render as a 90px sliver under a 42px heading and leave the
   band looking broken. The recovery IS the composition: the mailto is the
   band's primary button at its full size, sitting where the picker would
   have been, not a line of grey fine print. And the bail's own frame comes
   off - a bordered box inside a bordered card is two cards - so what is on
   screen is one card with a sentence and a button in it.
   The band's bottom rhythm collapses to 64px in this state: the picker's
   tail (the month bar, the aside, the confirm row) is not there to be given
   air, and the page must not leave a screen of paper under a short card. */
#picker.bk--bailed {
  display: grid;
  align-content: center;
  min-height: 300px;
}
#picker.bk--bailed .bk__bail {
  padding: 0;
  border: 0;
  background: none;
}
#picker.bk--bailed .bk__bail .cta-row { margin-top: 20px; }
/* and the band gives up its one-screen floor with it: a card that cannot
   show a month grid has no business holding a screen open, so the band ends
   64px under the one-pager instead of on a pillow of paper. */
#book:has(.bk--bailed) { min-height: 0; }
#book:has(.bk--bailed) .sec__inner { padding-bottom: 64px; }
@media (max-width: 560px) {
  #picker.bk--bailed { min-height: 240px; }
}

/* the one-pager, folded into #book: one strip under the card. It is a scroll
   target (the hero's one-pager link points at #form-onepager), so it keeps
   its own sticky-nav offset. */
.onepager {
  scroll-margin-top: var(--navh, 78px);
  margin-top: 22px;
  border: 1px solid var(--rule);
  background: var(--paper-2);
  padding: 18px 24px 20px;
  display: grid;
  grid-template-columns: 1fr 620px;
  gap: 44px;
  align-items: end;
}
.onepager__fields { display: grid; grid-template-columns: 1fr 1fr auto; gap: 16px; align-items: end; }
.onepager__fields .field { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   14. #faq, five, native disclosure, no script, one column at every width
   -------------------------------------------------------------------------- */

.faq { margin-top: 24px; border-top: 1px solid var(--rule); }
.faq__item { border-bottom: 1px solid var(--rule); }
.faq__q {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding: 15px 0;                  /* a 57px row: well past the 44px floor */
  cursor: pointer;
  list-style: none;
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-style: var(--display-style);
  font-size: var(--fs-3);
  line-height: 1.3;
  color: var(--ink);
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { color: var(--accent); }
.faq__q:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A disclosure's closed/open pair is +/−, not +/×. The two strokes are drawn
   as pseudo-elements so [open] can collapse the vertical one into a minus;
   rotating a literal "+" by 45° produced a × , which reads as dismiss. */
.faq__sign {
  position: relative;
  width: 18px;
  height: 18px;
  color: var(--accent);
  flex: none;
}
.faq__sign::before,
.faq__sign::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 2px;
  margin-top: -1px;
  background: currentColor;
}
.faq__sign::after { transform: rotate(90deg); transition: transform 0.18s ease; }
.faq__item[open] .faq__sign::after { transform: rotate(90deg) scaleX(0); }
.faq__a { margin: 0; padding: 0 120px 18px 0; font-size: var(--fs-4); line-height: 1.55; color: var(--muted-ink); max-width: 76ch; }

/* --------------------------------------------------------------------------
   15. Forms
   -------------------------------------------------------------------------- */

.form {
  border: 1px solid var(--rule);
  background: var(--paper-2);
  padding: 22px 26px 24px;
}
.form__title {
  font-family: var(--font-display);
  font-weight: var(--display-weight-strong);
  font-style: var(--display-style);
  font-size: var(--fs-3);
  margin: 0 0 8px;
  color: var(--ink);
}
.form__line { margin: 0 0 18px; font-size: var(--fs-4); line-height: 1.45; color: var(--muted-ink); }
.field { display: block; margin-bottom: 16px; }
.field__label {
  display: block;
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted-ink);
  margin-bottom: 7px;
}
.field input,
.field select,
.field textarea {
  width: 100%;
  min-height: 46px;
  padding: 11px 13px;
  border: 1px solid var(--field-line);
  border-radius: 2px;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-4);
  line-height: 1.4;
}
.field textarea { min-height: 76px; resize: vertical; }
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.field--pair { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.form__note { margin: 14px 0 0; font-size: var(--fs-5); line-height: 1.5; color: var(--muted-ink); }

/* A form's one announced line. Empty until something happens, so it takes no
   room and never pushes the button down on load. */
.form__status { margin: 14px 0 0; font-size: var(--fs-5); line-height: 1.5; color: var(--muted-ink); }
.form__status:empty { display: none; }
.form__status[data-state="ok"] { color: var(--accent); }
.form__status[data-state="error"] { color: var(--accent2-ink); }

/* A field the collector rejected. Colour is never the only signal: the
   handler also moves focus to the control. */
.field input[aria-invalid="true"],
.field select[aria-invalid="true"],
.field textarea[aria-invalid="true"] { border-color: var(--accent2-ink); box-shadow: inset 0 -2px 0 var(--accent2-ink); }

/* Honeypot. Not display:none, some bots skip hidden inputs, and some
   password managers do too. Taken out of flow and out of the way instead. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* --------------------------------------------------------------------------
   16. footer
   -------------------------------------------------------------------------- */

.foot { border-top: 1px solid var(--rule); background: var(--paper-3); }
.foot__inner { padding: 44px 0 40px; }
.foot__mark {
  font-family: var(--font-label);
  font-size: var(--fs-5);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0;
}
.foot__mark span { color: var(--accent); }
.foot__tag { margin: 12px 0 0; font-size: var(--fs-4); line-height: 1.5; color: var(--muted-ink); max-width: 42ch; }

.foot__legal {
  margin-top: 36px;
  padding-top: 20px;
  border-top: 1px solid var(--rule);
  display: flex;
  justify-content: space-between;
  gap: 30px;
  flex-wrap: wrap;
}
/* ONE ITEM PER <li>; THE SEPARATOR IS A ::before ON EVERY ITEM BUT THE
   FIRST, so it belongs to the item it precedes and can never be orphaned
   from it. Below 880 the list stacks one item per line and the separators
   are not drawn at all - a stacked list has nothing to separate, and there
   is then no line at 375 that can begin with a "·", which is what the run
   of bare middots used to produce.
   Every footer link is a 44px hit box: the fine print is the one place on
   the page a thumb was being asked to hit 13px of text. */
.foot__items {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 10px;
}
.foot__items li,
.foot__legal a {
  font-family: var(--font-label);
  font-size: var(--fs-5);
  letter-spacing: 0.02em;
  color: var(--muted-ink);
}
.foot__items li + li::before { content: "\00b7"; margin-right: 10px; color: var(--muted); }
.foot__legal a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
}
.foot__legal a:hover { color: var(--ink); }
.foot__legal a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --------------------------------------------------------------------------
   17. Narrower viewports. The page is designed at 1280; below that it folds
       to one column rather than scaling down.
   -------------------------------------------------------------------------- */

@media (max-width: 1180px) {
  .grantbox { grid-template-columns: 1fr; gap: 22px; }
  .why { grid-template-columns: 1fr; gap: 24px; }
  /* The rung folds; the ladder keeps a wider gap than the rung so the pairing
     of a label with the thing under it survives the stack. */
  .creds { gap: 24px; }
  .creds__line { grid-template-columns: 1fr; gap: 10px; }
  .onepager { grid-template-columns: 1fr; gap: 22px; }
}

@media (max-width: 880px) {
  /* SSA-11. The labels are not dropped on a phone: they move to their own row
     under the wordmark. The first row is unchanged - wordmark left, CTA right
     - because the CTA is a sibling of the label row, not a member of it.

     ⚠ IT WRAPS, IT DOES NOT SCROLL. Round 4 made this row a sideways
     scroller (overflow-x: auto, scrollbar hidden) on the theory that a label
     that does not fit can be swiped to. What it actually shipped in round 5,
     once the labels grew, was a nav whose fourth item was cut to the single
     letter "Q" at the right edge, with no scrollbar to say why: 377px of
     labels in a 311px row. A hidden scroller cannot say it is a scroller, so
     it is a clipping device. Wrapping is honest - it costs a 44px second
     label row, which is exactly why the labels are kept short enough not to
     need one at 375 (see the comment on the <nav> in index.html). Below
     ~345px they do wrap, and the nav grows; site.js re-measures --navh on
     resize, so nothing lands under the bar. */
  .nav__inner { flex-wrap: wrap; height: auto; padding: 8px 0 0; row-gap: 0; }
  .nav__links {
    order: 3;                       /* after the CTA in the DOM's flow, on its own row */
    flex: 1 0 100%;
    flex-wrap: wrap;
    gap: 0 14px;
  }
  .nav__links a:not(.btn) {
    display: inline-flex;
    align-items: center;
    min-height: 44px;               /* the family's hit-box floor, thumb-sized */
  }
  .nav .btn--sm { min-height: 44px; }
  /* the chevron keeps its own track and spans both rows of the stacked row */
  .p-row { grid-template-columns: 1fr auto; gap: 8px 16px; }
  .p-row__name,
  .p-row__v { grid-column: 1; }
  .p-row::after { grid-column: 2; grid-row: 1 / -1; }
  .cases,
  .packs { grid-template-columns: 1fr; }
  .price-lead { grid-template-columns: minmax(0, 1fr); row-gap: 14px; }

  /* THE PLATE ON A PHONE. A veil that is solid to 46% and clear by 70%
     needs a screen wide enough to have a left and a right, and 375 has
     neither. So the photograph becomes the top band of the hero - still
     edge to edge, still alt="" - and dissolves downwards into the paper the
     copy is set on; the copy runs under it in one column. The one-screen
     floor comes off with the split: at 375x667 the who-line, the headline,
     the promise and the ask all land by ~490px, and a floor under that
     would buy nothing but empty paper. The crop drops to the desk itself,
     which is the bottom third of the frame. */
  .hero {
    padding-top: calc(var(--navh, 78px) + clamp(16px, 3vh, 28px));
    padding-bottom: clamp(32px, 4vh, 48px);
  }
  .hero__wrap { display: flex; flex-direction: column; gap: 28px; }
  .hero__grid { display: flex; flex-direction: column; }
  .hero__copy .h1 { margin-right: 0; }
  .hero__visual { margin-top: 20px; width: 100%; }
  .monograph-mat { padding: 10px; outline: none; }
  .monograph-mat__frame { min-height: clamp(340px, 48vh, 420px); padding: 10px; }
  .monograph-mat .hero__cycle { max-width: 100%; }
  .plate--cases { min-height: 0; padding: 34px 0; }
  .onepager__fields { grid-template-columns: 1fr; }
  .faq__a { padding-right: 0; }
  .hero__who { margin-bottom: 12px; }
  .lede { margin-top: 14px; }
  /* the footer's fine print stacks, and the separators go with the stack */
  .foot__items { flex-direction: column; align-items: flex-start; }
  .foot__items li + li::before { content: none; }
  .foot__legal { gap: 0; }
  .awards__mark { height: 56px; }
}

/* The phone tier. Below 560 the page is one narrow column: anything still
   sitting in two tracks, or sized for a desk, goes to one. */
@media (max-width: 560px) {
  /* ⚠ `.nav__links { display: none }` IS GONE, AND IS NOT TO COME BACK.
     Round 5 hid the only in-page navigation below 560, which left a phone
     with exactly one route to the price: scroll, for seven screens. The bar
     keeps the wordmark and the CTA on row one, unchanged; the four labels
     take one row underneath it, spread edge to edge.
     13px, not --fs-5's 12.5px mobile step: this row IS the page's navigation
     on a phone and does not get to be the smallest type on the screen. The
     40px row is the one place the 44px floor is traded down, and it is
     traded against width, not away: each label is a ~70px-wide target. */
  .nav__inner {
    height: auto;
    padding: 4px 0 0;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 12px;
    row-gap: 0;
  }
  .nav__links {
    justify-content: space-between;
    flex-wrap: nowrap;
    gap: 0 8px;
    border-top: 1px solid var(--rule);
  }
  .nav__links a:not(.btn) { font-size: 13px; min-height: 40px; }

  /* THE FIRST SCREEN AT 375x667. The ask used to finish 702px down a 667px
     screen - below the fold, on the page's most common width - because it
     sat under a three-panel device that is evidence, not the ask. On a phone
     the order is who / headline / promise / THE ASK, and the device follows
     it; on a desk the grid's own areas are untouched. The label shortens to
     the site's short form here too, which is what keeps the button one line
     in a 311px column instead of two. */
  .cta-row { gap: 16px; margin-top: 14px; }
  .hero__visual { margin-top: 16px; }
  .cta-row .btn { width: 100%; }
  .btn { min-width: 0; }
  .btn__wide { display: none; }
  .btn__narrow { display: inline; }
  .hero__grid { padding-top: 10px; }
  .hero__who { margin-bottom: 8px; }
  .lede { margin-top: 10px; }
  /* no clock below 560 (site.js does not arm it), so no bar to draw */
  .cycle-tab__fill { display: none; }
  /* the label sat in its own inline column and left the logos wrapping into a
     third of the width; below 560 it becomes a heading over a full-width row */
  .supported { flex-direction: column; align-items: flex-start; gap: 16px; }
  .supported__logos { gap: 20px 24px; }
  .supported__logos img { height: 24px; }
  .supported__logos img.is-tall { height: 30px; }
  .supported__logos img.is-square { height: 32px; }
  .creds__row { gap: 20px 24px; }
  .awards { gap: 16px; }
  .grantbox p.grantbox__note { border-left: 0; border-top: 1px solid var(--rule); padding: 14px 0 0; }
  .field--pair { grid-template-columns: 1fr; }
  .cal { padding: 20px 18px 22px; }
  .onepager { padding: 18px 18px 20px; }
  .onepager__fields .btn { width: 100%; }
  .sec__inner--close { padding: 40px 0; gap: 16px; }
  .sec__inner--close .btn { width: 100%; }
}

/* At 320 the wordmark and the CTA want more than the gutters leave, and
   without this the CTA wraps onto a third row and the sticky nav eats a
   third of a phone. The CTA gives up six pixels of side padding instead.
   The other two are the first-screen rule at the family's narrowest width:
   the lede takes a fourth line at 320, so the hero's own padding and the gap
   above the button give back eight more than the phone tier already does.
   The nav's four labels still fit one row here: at 13px they run ~200px in
   a 256px row. */
@media (max-width: 360px) {
  .hero__grid { padding-top: 6px; }
  .hero__grid .cta-row { margin-top: 10px; }
  .nav__links { gap: 0 4px; }
  .nav .btn--sm { padding-left: 10px; padding-right: 10px; }
}

/* --------------------------------------------------------------------------
   18. THE PLATES. Two photographs, and they are the only two on the page.
       Both bleed edge to edge - a picture in a 1152 column is an
       illustration, a picture that runs off both sides is a place - and
       both carry alt="": the sentence beside each one is the content, and
       a screen reader that is told "a cleared desk with paperwork" learns
       nothing it can act on.

       ⚠ THE CREAM IN THESE GRADIENTS IS A COPY OF --paper (#F2EDE1),
       written out because a gradient cannot interpolate a custom property
       to zero alpha without going through transparent BLACK, which leaves a
       grey bruise down the middle of the ramp. rgba(242,237,225,0) is the
       same cream at zero alpha and fades clean. Change --paper and change
       these six numbers in the same edit.
   -------------------------------------------------------------------------- */

.plate { position: relative; overflow: hidden; background: var(--bg-paper); }
.plate__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.plate__veil { position: absolute; inset: 0; pointer-events: none; }
.plate__wrap { position: relative; z-index: 1; }

/* THE HERO PLATE. Absolute over the whole section - under the copy, under
   the loop card and under the bar - so the first screen IS the photograph.
   The crop leans right and down, which is where the desk is in the frame. */
.plate--hero { position: absolute; inset: 0; }
.plate--hero .plate__img { object-position: 54% 56%; }

/* THE CREAM VEIL, two layers on one element.
   ACROSS: solid paper to 46% of the viewport and gone by 70%, so the copy
   sits on paper and the desk reads as a photograph beside it. The copy
   column is sized to finish inside that solid zone at every desk width
   (§8): 500px from the measure's left edge lands at 44-47% of the viewport
   from 1200 to 1920.
   UP: solid paper for the bottom 96px and gone by 240px, so the picture
   dissolves into the page instead of stopping on a line, the funders' row
   at the hero's foot has paper under it, and its four logos have something
   to multiply against. It is also the join to #work: there is no band edge
   between the photograph and the first cream band, only the dissolve. */
.plate__veil--cream {
  background:
    linear-gradient(to top,
      #F2EDE1 0,
      #F2EDE1 96px,
      rgba(242, 237, 225, 0) 240px),
    linear-gradient(to right,
      #F2EDE1 0,
      #F2EDE1 46%,
      rgba(242, 237, 225, 0) 70%);
}

/* Between 881 and 1199 the screen has less picture to give away and the
   copy column is proportional rather than fixed, so the paper holds
   further across: at 1199 the column ends at 52.5% and at 1024 at 52%. */
@media (min-width: 881px) and (max-width: 1199px) {
  .plate__veil--cream {
    background:
      linear-gradient(to top,
        #F2EDE1 0,
        #F2EDE1 96px,
        rgba(242, 237, 225, 0) 240px),
      linear-gradient(to right,
        #F2EDE1 0,
        #F2EDE1 58%,
        rgba(242, 237, 225, 0) 84%);
  }
}

/* THE SECOND PLATE, in #cases. A band rather than a backdrop, and the only
   place on the page where a heading is set on a picture.

   WHY NOT THE DESK AGAIN: the warm plate is the first screen and the reader
   has just spent a minute with it. A second crop of the same photograph is
   a repeated photograph, which is precisely the failure the four medal
   pictures were rebuilt to escape. The dusk facade is a different subject
   under the same house green, so the page reads as one place photographed
   twice rather than one photograph used twice.

   THE VEIL IS TWO LAYERS AND BOTH ARE MEASURED. rgba(36,80,74,.72) is the
   brief's green over the whole frame: cream on the lit windows at the right
   is 5.3:1. The concrete columns behind the heading are the brightest large
   area in the frame and .72 alone leaves the heading at 4.28:1, under the
   floor - so the second layer deepens the left 64%, where the heading is,
   to 6.6:1. Re-measure both if either number moves. */
#cases.sec--cases {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  box-sizing: border-box;
  padding: clamp(48px, 8vh, 108px) 0;
  overflow: hidden;
  background: none;
}
#cases .plate--cases {
  position: absolute;
  inset: 0;
  z-index: 0;
  height: 100%;
  min-height: 0;
  padding: 0;
  display: block;
}
#cases .plate--cases .plate__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
}
#cases .plate__veil--green {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to right, rgba(14, 32, 29, 0.45), rgba(14, 32, 29, 0.15) 64%),
    rgba(36, 80, 74, 0.78);
  z-index: 1;
}
#cases .wrap {
  position: relative;
  z-index: 2;
  width: 100%;
}
#cases .sec__inner--cases {
  padding: 0;
}
#cases .cases__title {
  color: #F4F0E6;
  max-width: 26ch;
  font-size: clamp(28px, 3.5vw, 44px);
  line-height: 1.15;
  margin-bottom: clamp(24px, 4vh, 44px);
}
#cases .cases {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(24px, 4vw, 56px);
}
#cases .case {
  border-top: 1px solid rgba(244, 240, 230, 0.28);
  padding-top: clamp(14px, 2.5vh, 24px);
}
#cases .case__word {
  color: #F4F0E6;
  font-size: clamp(20px, 2.2vw, 26px);
  font-family: var(--font-display);
  display: block;
  margin-bottom: 8px;
}
#cases .case__line {
  color: rgba(244, 240, 230, 0.84);
  font-size: clamp(15px, 1.4vw, 17px);
  line-height: 1.55;
  margin: 0;
}
@media (max-height: 760px) and (min-width: 601px) {
  #cases.sec--cases { padding: clamp(28px, 4vh, 48px) 0; }
  #cases .cases__title { margin-bottom: 20px; font-size: 32px; }
  #cases .case { padding-top: 12px; }
}
@media (max-width: 600px) {
  #cases.sec--cases { min-height: auto; padding: 64px 0; }
  #cases .cases { grid-template-columns: 1fr; gap: 24px; }
}
/* --------------------------------------------------------------------------
   19. Compact laptop & window fitting (height <= 820px)
   -------------------------------------------------------------------------- */
@media (min-width: 881px) and (max-height: 820px) {
  .hero__grid {
    padding: clamp(12px, 2vh, 22px) 0;
    row-gap: 14px;
    column-gap: 40px;
  }
  .hero__who { margin-bottom: 8px; }
  .lede { margin-top: 8px; }
  .sec__inner {
    padding: clamp(18px, 2.5vh, 32px) 0;
  }
  .sec__head {
    margin-bottom: 12px;
  }
  .cases {
    gap: 16px 40px;
  }
  .cases__title {
    font-size: 26px;
    margin-bottom: 16px;
  }
  .case__word {
    font-size: 18px;
  }
  .sec__inner--cases {
    padding: clamp(24px, 3vh, 40px) 0;
  }
  #pricing .price-lead { margin-top: 14px; }
  #pricing .packs { margin-top: 18px; }
  #pricing .grantbox { margin-top: 16px; }
  .creds {
    margin-top: 16px;
    padding-top: 12px;
    gap: 12px;
  }
  #book {
    padding-top: calc(var(--navh, 64px) + 2px) !important;
    padding-bottom: 4px !important;
  }
  #book .sec__inner { padding: 0 !important; }
  #book .sec__head { margin-bottom: 4px; }
  #book .nextup { margin: 4px 0 8px; }
  #book .nextup li { font-size: 13px; }
  #book .bk__card { padding: 10px 16px; }
  #book #form-review .field textarea { min-height: 40px; height: 40px; }
  #book .onepager { margin-top: 4px; padding: 4px 12px; }
  .book {
    margin-top: 10px;
  }
  .cal {
    padding: 14px 20px 16px;
  }
  .cal .field {
    margin-bottom: 10px;
  }
  .bk__pane {
    gap: 14px;
  }
  .supported {
    padding: 12px 0 14px;
    gap: 24px;
  }
}
