/* ==========================================================================
   Al-Ibrab Memorial School — design system
   --------------------------------------------------------------------------
   Deep forest green carries the brand. Amber is reserved exclusively for
   giving actions, so the donate path is never competing with ordinary links.
   Everything is driven by the tokens in :root — change them there, not here.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Brand — forest */
  --forest-950: #062A16;
  --forest-900: #0A3D1F;
  --forest-800: #0F5129;
  --forest-700: #166534;
  --forest-600: #1F7A41;
  --leaf-500: #3FA35F;
  --leaf-200: #B7E4C7;
  --leaf-100: #DCF2E4;

  /* Giving actions only */
  --amber-600: #C2871E;
  --amber-500: #E0A32E;
  --amber-400: #F2BB52;
  --amber-100: #FBEBCB;

  /* Decorative tints — section bands, photo blobs, sticker doodles, card fills.
     NEVER use these for a button, a link or a focus ring. Amber stays the one
     giving colour and forest the one interactive colour, so however playful the
     page gets, "where do I click to help" is never a guess. */
  --sky-500: #2F7FA6;
  --sky-200: #BBDCEE;
  --sky-100: #E4F1F8;
  --clay-500: #C4703F;
  --clay-200: #F2CDB4;
  --clay-100: #FBEADF;
  --lilac-500: #6B5CA5;
  --lilac-200: #D9D2EE;
  --lilac-100: #EFEBFA;

  /* Neutrals — warm, never clinical */
  --cream-50: #FBF8F2;
  --cream-100: #F4EFE4;
  --cream-200: #E8E0CF;
  --ink-900: #14201A;
  --ink-700: #2C3A33;
  --ink-600: #4A5A51;
  /* Darkened from #7C8A82, which measured 3.47:1 on the cream background —
     under the 4.5:1 AA floor everywhere it was used, including the fee-table
     column headers. This value measures 4.69:1 on cream and 4.98:1 on white. */
  --ink-400: #65736B;
  --white: #FFFFFF;

  /* Semantic */
  --bg: var(--cream-50);
  --bg-raised: var(--white);
  --bg-sunken: var(--cream-100);
  --text: var(--ink-900);
  --text-muted: var(--ink-600);
  --text-faint: var(--ink-400);
  --border: color-mix(in srgb, var(--ink-900) 10%, transparent);
  --border-strong: color-mix(in srgb, var(--ink-900) 18%, transparent);
  --accent: var(--forest-700);

  /* Fluid type — 320px to 1440px viewport */
  /* Floor for the all-caps tracked micro-labels (eyebrows, tags, flags, table
     column heads). They were hard-coded at 0.62/0.68/0.72rem in eleven places,
     which is 9.9-11.5px — too small to read on a phone, and three sizes doing
     one job. One token, no smaller than 12px. */
  --step-micro: 0.75rem;
  --step--1: clamp(0.84rem, 0.81rem + 0.14vw, 0.92rem);
  --step-0: clamp(1rem, 0.96rem + 0.19vw, 1.11rem);
  --step-1: clamp(1.19rem, 1.12rem + 0.33vw, 1.39rem);
  --step-2: clamp(1.41rem, 1.29rem + 0.56vw, 1.74rem);
  --step-3: clamp(1.67rem, 1.48rem + 0.9vw, 2.18rem);
  --step-4: clamp(1.98rem, 1.69rem + 1.4vw, 2.72rem);
  --step-5: clamp(2.35rem, 1.91rem + 2.1vw, 3.4rem);
  --step-6: clamp(2.79rem, 2.14rem + 3.1vw, 4.25rem);
  --step-7: clamp(3.31rem, 2.37rem + 4.5vw, 5.32rem);

  /* Spacing — 8px rhythm */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-9: 6rem;
  --space-10: 8rem;

  /* Shape */
  --radius-sm: 8px;
  --radius: 14px;
  --radius-lg: 22px;
  --radius-xl: 32px;
  --radius-2xl: 44px;
  --radius-pill: 999px;

  /* Organic photo shapes. The arch is the workhorse: it reads playful and, unlike
     a true blob, it never crops a face at an awkward angle. */
  --arch: 48% 48% var(--radius-lg) var(--radius-lg) / 32% 32% var(--radius-lg) var(--radius-lg);
  --arch-flip: var(--radius-lg) var(--radius-lg) 48% 48% / var(--radius-lg) var(--radius-lg) 32% 32%;
  --blob: 56% 44% 47% 53% / 46% 52% 48% 54%;

  /* Green-tinted shadows read warmer than neutral grey */
  --shadow-sm: 0 1px 2px color-mix(in srgb, var(--forest-950) 8%, transparent);
  --shadow: 0 2px 4px color-mix(in srgb, var(--forest-950) 5%, transparent),
            0 8px 20px -6px color-mix(in srgb, var(--forest-950) 12%, transparent);
  --shadow-lg: 0 4px 8px color-mix(in srgb, var(--forest-950) 5%, transparent),
               0 18px 44px -12px color-mix(in srgb, var(--forest-950) 20%, transparent);
  --shadow-amber: 0 6px 20px -6px color-mix(in srgb, var(--amber-600) 48%, transparent);

  /* Motion */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
  --dur-fast: 160ms;
  --dur: 280ms;
  --dur-slow: 560ms;

  /* Layout */
  --container: 1200px;
  --container-narrow: 760px;
  --measure: 68ch;
  --header-h: 76px;

  /* Type families */
  --font-display: 'Fraunces', 'Iowan Old Style', 'Palatino Linotype', Georgia, serif;
  --font-body: 'Plus Jakarta Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}

/* --------------------------------------------------------------------------
   2. Reset
   -------------------------------------------------------------------------- */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + var(--space-5));
  /* The mobile drawer is a fixed panel parked one full width off-screen to the
     right. Even off-screen it extends the document, so phones get a horizontal
     scroll. `clip` rather than `hidden`: hidden on the root makes html a scroll
     container and breaks `position: sticky` on the header. `clip` just trims. */
  overflow-x: clip;
}

body {
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.65;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

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

/* Every <img> carries width/height attributes so the browser can reserve space
   and avoid layout shift. Those attributes are presentational hints, and an
   explicit height beats `aspect-ratio` — without `height: auto` here, the
   gallery and split images render at their intrinsic pixel height instead of
   the ratio the design asks for. Rules that genuinely want a fixed height
   (.page-banner__media, .quote__by, .day__media) set it at higher specificity.
   SVG is left out on purpose: icons size from their own width/height. */
img,
video {
  height: auto;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

button { background: none; border: 0; cursor: pointer; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-variation-settings: 'SOFT' 36, 'WONK' 1;
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

p, li { text-wrap: pretty; }

a { color: var(--accent); text-underline-offset: 0.2em; }

ul[class], ol[class] { list-style: none; padding: 0; }

:focus-visible {
  outline: 3px solid var(--forest-600);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  inset-inline-start: var(--space-4);
  inset-block-start: calc(-1 * var(--space-10));
  z-index: 200;
  padding: var(--space-3) var(--space-5);
  background: var(--forest-900);
  color: var(--white);
  border-radius: var(--radius);
  text-decoration: none;
  transition: inset-block-start var(--dur) var(--ease-out);
}
.skip-link:focus { inset-block-start: var(--space-4); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   3. Layout primitives
   -------------------------------------------------------------------------- */

.container {
  width: min(100% - var(--space-6), var(--container));
  margin-inline: auto;
}
.container--narrow { width: min(100% - var(--space-6), var(--container-narrow)); }

.section { padding-block: clamp(var(--space-8), 7vw, var(--space-10)); }
.section--tight { padding-block: clamp(var(--space-7), 5vw, var(--space-8)); }

.section--sunken { background: var(--bg-sunken); }

/* Tinted bands. The page used to be one unbroken sheet of cream, which is why
   long scrolls read as a pile rather than a sequence — nothing told you a new
   thought had started. Each chapter of the story now sits on its own colour.
   Keep them alternating: two adjacent bands of the same tint undo the effect.
   --bg-band must match the background, because components that paint a frame in
   the band colour (the arch media's inset photo) read it from here. */
.section { --bg-band: var(--bg); }
.section--sky { background: var(--sky-100); --bg-band: var(--sky-100); }
.section--clay { background: var(--clay-100); --bg-band: var(--clay-100); }
.section--leaf { background: var(--leaf-100); --bg-band: var(--leaf-100); }
.section--lilac { background: var(--lilac-100); --bg-band: var(--lilac-100); }
.section--sunken { --bg-band: var(--bg-sunken); }
.section--dark { --bg-band: var(--forest-950); }

/* Softens the seam where a tinted band meets the band below it. */
.section--curve-top {
  border-start-start-radius: clamp(var(--space-6), 5vw, var(--radius-2xl));
  border-start-end-radius: clamp(var(--space-6), 5vw, var(--radius-2xl));
}
.section--curve-bottom {
  border-end-start-radius: clamp(var(--space-6), 5vw, var(--radius-2xl));
  border-end-end-radius: clamp(var(--space-6), 5vw, var(--radius-2xl));
}

.section--dark {
  position: relative;
  background: var(--forest-950);
  color: var(--cream-100);
  isolation: isolate;
}
/* The ink for these bands is declared in section 22, after the components.
   It has to be: several components set their own dark ink at the same
   specificity (`.prose p`, `.allocation__key li`), so this can only win the
   tie on source order. Painting it from here lost those ties silently. */

/* Soft light bloom so the dark bands aren't flat slabs of colour */
.section--dark::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(70ch 50ch at 18% 0%, color-mix(in srgb, var(--leaf-500) 20%, transparent), transparent 62%),
    radial-gradient(60ch 46ch at 92% 100%, color-mix(in srgb, var(--amber-500) 11%, transparent), transparent 60%);
}

.stack > * + * { margin-block-start: var(--flow, var(--space-4)); }

.prose { max-width: var(--measure); }
.prose p + p { margin-block-start: var(--space-4); }
.prose p { color: var(--text-muted); }

.grid { display: grid; gap: var(--space-5); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }

/* ---- utilities ----
   A deliberately short list. These exist because the markup was carrying the
   same four or five spacing and sizing decisions as inline `style` attributes,
   which is how a page ends up with eight slightly different gaps between
   headings and body copy. If you need a sixth utility, consider whether the
   component should own the spacing instead. */

.center { text-align: center; }

.flow-3 { --flow: var(--space-3); }
.flow-4 { --flow: var(--space-4); }
.flow-5 { --flow: var(--space-5); }
.flow-6 { --flow: var(--space-6); }
.flow-7 { --flow: var(--space-7); }

.mt-5 { margin-block-start: var(--space-5); }
.mt-6 { margin-block-start: var(--space-6); }
.mt-7 { margin-block-start: var(--space-7); }

/* "(optional)" beside a field label, and the same treatment anywhere a word
   inside a stronger run of text should step back rather than disappear. */
.soft { font-weight: 400; color: var(--text-faint); }

/* Heading sizes, decoupled from heading level. `.section-head h2` covers the
   common case; these are for the headings that sit outside one — a split
   column, a card — where the level is fixed by document structure but the
   size is a design decision. */
.h-sm { font-size: var(--step-1); }
.h-md { font-size: var(--step-4); }
.h-lg { font-size: var(--step-5); }
.h-xl { font-size: var(--step-6); }

/* An aside the reader should notice but not be stopped by: the safeguarding
   statement, a caveat on a figure. */
.note {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: var(--space-3);
  max-width: 52ch;
  font-size: var(--step--1);
  line-height: 1.55;
  color: var(--text-muted);
}
.note svg { margin-block-start: 3px; color: var(--leaf-500); }

/* --------------------------------------------------------------------------
   4. Section headings
   -------------------------------------------------------------------------- */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--forest-600);
  margin-block-end: var(--space-4);
}
.eyebrow::before {
  content: '';
  width: 28px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
}
.section--dark .eyebrow { color: var(--leaf-200); }

.section-head { max-width: 58ch; margin-block-end: var(--space-7); }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center .eyebrow::after {
  content: '';
  width: 28px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
}
.section-head h2 { font-size: var(--step-5); }
.section-head p {
  margin-block-start: var(--space-4);
  font-size: var(--step-1);
  color: var(--text-muted);
}
.section--dark .section-head p { color: color-mix(in srgb, var(--cream-100) 78%, transparent); }

/* The eyebrow outside a `.section-head--center` — it has no centred parent to
   inherit alignment from, so it needs the trailing rule of its own. */
.eyebrow--center::after {
  content: '';
  width: 28px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
}

/* Lede paragraph for a centred block that isn't a `.section-head` (the closing
   call to action, where the heading is the h2 and there is no eyebrow+rule
   pairing to hang off). */
.lede-center {
  max-width: 48ch;
  margin-inline: auto;
  margin-block-start: var(--space-5);
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--text-muted);
  text-wrap: pretty;
}
.section--dark .lede-center { color: color-mix(in srgb, var(--cream-100) 80%, transparent); }

/* --------------------------------------------------------------------------
   5. Buttons
   -------------------------------------------------------------------------- */

.btn {
  --btn-bg: var(--forest-800);
  --btn-fg: var(--white);
  --btn-border: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: 0.85em 1.6em;
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 700;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  color: var(--btn-fg);
  background: var(--btn-bg);
  border: 2px solid var(--btn-border);
  border-radius: var(--radius-pill);
  transition: transform var(--dur-fast) var(--ease-spring),
              box-shadow var(--dur) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.btn:active { transform: translateY(0); }

/* Amber is the giving colour. Dark ink on amber clears AA comfortably. */
.btn--give {
  --btn-bg: var(--amber-500);
  --btn-fg: var(--forest-950);
  box-shadow: var(--shadow-amber);
}
.btn--give:hover { --btn-bg: var(--amber-400); box-shadow: 0 10px 28px -8px color-mix(in srgb, var(--amber-600) 60%, transparent); }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--forest-800);
  --btn-border: var(--border-strong);
}
.btn--ghost:hover { --btn-bg: var(--white); --btn-border: var(--forest-700); }

/* The hero is a light composition now, so it is no longer in this list — a
   white-on-white ghost button there would have been invisible. */
.section--dark .btn--ghost {
  --btn-fg: var(--white);
  --btn-border: color-mix(in srgb, var(--white) 36%, transparent);
}
.section--dark .btn--ghost:hover {
  --btn-bg: color-mix(in srgb, var(--white) 12%, transparent);
  --btn-border: var(--white);
}

.btn--lg { padding: 1em 2em; font-size: var(--step-1); }
.btn--block { width: 100%; }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  align-items: center;
}
.btn-row--center { justify-content: center; }
.btn-row--tight { gap: var(--space-2); }
/* Pushes two groups to opposite ends — the calculator's frequency and currency
   toggles — but wraps to a stack rather than squeezing them together. */
.btn-row--split { justify-content: space-between; gap: var(--space-4) var(--space-6); }

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 700;
  text-decoration: none;
  color: var(--forest-700);
  /* Some of these links are addresses, and an email address is one unbreakable
     word. The label on contact.html measured 324px of min-content inside a
     277px grid track, so the link sized itself to the word and pushed its
     arrow 18px off a 375px screen. `anywhere` rather than `break-word` because
     only `anywhere` lowers the min-content contribution — `break-word` breaks
     the glyphs but still reports the whole word's width to the track, which
     is what caused the overflow in the first place. */
  overflow-wrap: anywhere;
}
.link-arrow svg { flex: none; transition: transform var(--dur) var(--ease-spring); }
.link-arrow:hover { text-decoration: underline; }
.link-arrow:hover svg { transform: translateX(4px); }

/* --------------------------------------------------------------------------
   6. Header & navigation
   -------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: 100;
  transition: background-color var(--dur) var(--ease-out),
              box-shadow var(--dur) var(--ease-out),
              border-color var(--dur) var(--ease-out);
  border-block-end: 1px solid transparent;
}
.site-header.is-scrolled {
  background: color-mix(in srgb, var(--cream-50) 86%, transparent);
  backdrop-filter: blur(14px) saturate(1.3);
  border-block-end-color: var(--border);
  box-shadow: var(--shadow-sm);
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  min-height: var(--header-h);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  color: var(--forest-900);
  margin-inline-end: auto;
}
.brand__mark { width: 42px; height: 42px; flex: none; }
.brand__name {
  font-family: var(--font-display);
  font-variation-settings: 'SOFT' 40, 'WONK' 1;
  font-size: var(--step-1);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
}
.brand__tag {
  display: block;
  font-family: var(--font-body);
  font-size: var(--step-micro);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-block-start: 3px;
}

.nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}
.nav__link {
  position: relative;
  display: block;
  padding: var(--space-2) var(--space-4);
  font-size: var(--step--1);
  font-weight: 600;
  text-decoration: none;
  color: var(--ink-700);
  border-radius: var(--radius-pill);
  transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.nav__link:hover { color: var(--forest-800); background: color-mix(in srgb, var(--forest-700) 8%, transparent); }
.nav__link[aria-current='page'] { color: var(--forest-800); }
.nav__link[aria-current='page']::after {
  content: '';
  position: absolute;
  inset-block-end: 2px;
  inset-inline: 50% auto;
  translate: -50% 0;
  width: 18px;
  height: 3px;
  background: var(--amber-500);
  border-radius: 2px;
}

.nav-toggle {
  display: none;
  width: 46px;
  height: 46px;
  place-items: center;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--white);
}
.nav-toggle__bars { position: relative; width: 20px; height: 2px; }
.nav-toggle__bars,
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  display: block;
  background: var(--ink-900);
  border-radius: 2px;
  transition: transform var(--dur) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  height: 2px;
}
.nav-toggle__bars::before { inset-block-start: -6px; }
.nav-toggle__bars::after { inset-block-start: 6px; }

.nav-toggle[aria-expanded='true'] .nav-toggle__bars { background: transparent; }
.nav-toggle[aria-expanded='true'] .nav-toggle__bars::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded='true'] .nav-toggle__bars::after { transform: translateY(-6px) rotate(-45deg); }

.nav-scrim {
  position: fixed;
  inset: 0;
  z-index: 98;
  background: color-mix(in srgb, var(--forest-950) 50%, transparent);
  backdrop-filter: blur(2px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur) var(--ease-out), visibility var(--dur);
}
body.nav-open .nav-scrim { opacity: 1; visibility: visible; }

/* Overlay state — header sitting on top of a dark hero or banner.
   Reverts to the normal dark-on-light treatment the moment it raises. */
.site-header--overlay:not(.is-scrolled) .brand { color: var(--white); }
.site-header--overlay:not(.is-scrolled) .brand__tag { color: color-mix(in srgb, var(--white) 70%, transparent); }

.site-header--overlay:not(.is-scrolled) .nav-toggle {
  background: color-mix(in srgb, var(--white) 10%, transparent);
  border-color: color-mix(in srgb, var(--white) 32%, transparent);
}
.site-header--overlay:not(.is-scrolled) .nav-toggle:not([aria-expanded='true']) .nav-toggle__bars,
.site-header--overlay:not(.is-scrolled) .nav-toggle:not([aria-expanded='true']) .nav-toggle__bars::before,
.site-header--overlay:not(.is-scrolled) .nav-toggle:not([aria-expanded='true']) .nav-toggle__bars::after {
  background: var(--white);
}

/* Only above the drawer breakpoint — in the mobile drawer the links sit on
   a cream panel and must stay dark. */
@media (min-width: 881px) {
  .site-header--overlay:not(.is-scrolled) .nav__link { color: color-mix(in srgb, var(--white) 88%, transparent); }
  .site-header--overlay:not(.is-scrolled) .nav__link:hover,
  .site-header--overlay:not(.is-scrolled) .nav__link[aria-current='page'] {
    color: var(--white);
    background: color-mix(in srgb, var(--white) 14%, transparent);
  }
}

/* --------------------------------------------------------------------------
   7. Hero
   -------------------------------------------------------------------------- */

/* The hero used to be a full-bleed photo under a dark scrim. It looked handsome
   and said nothing: one flat rectangle, no entry point, and a headline fighting
   whatever happened to be in the photograph behind it. It is now a composition —
   copy on the left, a two-photo stack on colour blobs on the right, stickers
   scattered between. The page starts with a face instead of a gradient. */
.hero {
  position: relative;
  /* Pulled up under the sticky header so the hero's tint wash runs behind it,
     then padded back down by the same amount. Drop the negative margin and the
     header reserves its own band of flat cream above the composition. */
  margin-block-start: calc(-1 * var(--header-h));
  padding-block: calc(var(--header-h) + var(--space-7)) clamp(var(--space-8), 7vw, var(--space-9));
  background: var(--bg);
  /* Blobs and doodles are positioned past the container edge on purpose. */
  overflow: hidden;
  isolation: isolate;
}

/* Warm wash under the photo column so the composition has a horizon rather than
   floating on flat cream. Inline-end only — the reading side stays clean. */
.hero::before {
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  z-index: -1;
  width: min(58%, 820px);
  background:
    radial-gradient(58ch 46ch at 62% 24%, color-mix(in srgb, var(--sky-200) 50%, transparent), transparent 68%),
    radial-gradient(42ch 38ch at 96% 92%, color-mix(in srgb, var(--clay-200) 44%, transparent), transparent 64%);
}

.hero__grid {
  display: grid;
  gap: clamp(var(--space-7), 5vw, var(--space-9));
  grid-template-columns: minmax(0, 1.04fr) minmax(0, 1fr);
  align-items: center;
}

.hero__copy { max-width: 36rem; }

.hero__kicker {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4) var(--space-2) var(--space-2);
  margin-block-end: var(--space-5);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--ink-700);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-sm);
}
.hero__kicker b {
  padding: 3px var(--space-3);
  font-size: var(--step-micro);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--forest-950);
  background: var(--amber-400);
  border-radius: var(--radius-pill);
}

.hero h1 {
  /* A step down from the display maximum on purpose. The old hero gave the
     headline the full page width; this one gives it half, and --step-7 in a
     576px column sets 85px type that breaks into four lines of three words.
     --step-6 holds the same presence in three. */
  font-size: var(--step-6);
  font-variation-settings: 'SOFT' 48, 'WONK' 1;
  letter-spacing: -0.03em;
  color: var(--forest-950);
}

/* Per-word emphasis, the way the reference headline alternates hues. The marker
   is a pseudo-element sweep rather than text-decoration so it can sit behind the
   descenders like a highlighter pass. */
.hero h1 em {
  position: relative;
  font-style: normal;
  white-space: nowrap;
}
.hero h1 .is-marked::after {
  content: '';
  position: absolute;
  z-index: -1;
  inset-inline: -0.06em;
  inset-block-end: 0.06em;
  height: 0.28em;
  background: var(--amber-400);
  border-radius: var(--radius-pill);
}
.hero h1 .is-tinted { color: var(--clay-500); }

.hero__lede {
  max-width: 44ch;
  margin-block-start: var(--space-5);
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--text-muted);
}

.hero__actions { margin-block-start: var(--space-6); }

/* Stands in for the reference's stacked "client reviews" avatars. Avatars of
   children would be the wrong call here, and three verifiable facts earn more
   trust than a face count anyway. */
.hero__facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  margin-block-start: var(--space-6);
  padding-block-start: var(--space-5);
  border-block-start: 1px solid var(--border);
}
.hero__facts li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-muted);
}
.hero__facts svg { flex: none; color: var(--leaf-500); }

/* ---- the photo composition ---- */

.hero__media {
  position: relative;
  width: min(100%, 460px);
  margin-inline: auto;
  /* Room for the inset photo and chip, which hang outside the main shot. */
  padding-block-end: var(--space-8);
  padding-inline-start: var(--space-6);
}

.hero__blob {
  position: absolute;
  z-index: 0;
  inset-block-start: -4%;
  inset-inline: 10% -8%;
  aspect-ratio: 1;
  background: var(--sky-200);
  border-radius: var(--blob);
  animation: blob-drift 20s var(--ease-out) infinite alternate;
}

.hero__shot {
  position: relative;
  z-index: 1;
}
.hero__shot img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--arch);
  box-shadow: var(--shadow-lg);
}

/* The second, smaller photo tucked at the lower start edge — the two-child
   composition from the reference. It stops the hero reading as one stock
   portrait and gives the eye somewhere to travel. */
.hero__shot--inset {
  position: absolute;
  z-index: 2;
  inset-block-end: 0;
  inset-inline-start: 0;
  width: 54%;
}
.hero__shot--inset img {
  aspect-ratio: 4 / 3;
  border: 6px solid var(--bg);
  border-radius: var(--radius-xl);
}

.hero__chip {
  position: absolute;
  z-index: 3;
  inset-block-start: 8%;
  inset-inline-end: calc(-1 * var(--space-5));
  display: grid;
  gap: 1px;
  padding: var(--space-4) var(--space-5);
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  text-align: center;
}
.hero__chip b {
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1;
  color: var(--forest-800);
}
.hero__chip span {
  font-size: var(--step-micro);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}

@keyframes blob-drift {
  from { border-radius: var(--blob); rotate: -3deg; }
  to { border-radius: 44% 56% 58% 42% / 54% 46% 54% 46%; rotate: 3deg; }
}

/* --------------------------------------------------------------------------
   8. Impact figures
   -------------------------------------------------------------------------- */

.impact { position: relative; }

.impact__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
  gap: var(--space-4);
}

/* Four pastel tiles, each its own colour — the reference's stat row. These were
   four columns inside a single white card separated by hairlines, which at
   desktop width read as a spreadsheet rather than four things worth knowing.
   Set --tint and --tint-ink per card from the HTML. */
.stat {
  display: grid;
  align-content: start;
  gap: var(--space-3);
  padding: clamp(var(--space-5), 3vw, var(--space-6));
  background: var(--tint, var(--leaf-100));
  border-radius: var(--radius-xl);
  transition: transform var(--dur) var(--ease-spring);
}
.stat:hover { transform: translateY(-4px); }

.stat__value {
  display: flex;
  align-items: baseline;
  gap: 1px;
  font-family: var(--font-display);
  font-variation-settings: 'SOFT' 30, 'WONK' 0;
  font-size: var(--step-5);
  font-weight: 700;
  line-height: 1;
  color: var(--tint-ink, var(--forest-800));
  font-variant-numeric: tabular-nums;
}
.stat__label {
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1.45;
  color: var(--ink-700);
}

/* --------------------------------------------------------------------------
   9. Editorial split (about / story)
   -------------------------------------------------------------------------- */

.split {
  display: grid;
  gap: clamp(var(--space-6), 5vw, var(--space-8));
  align-items: center;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr));
}
.split--reverse > :first-child { order: 2; }

.split__media { position: relative; }
.split__media img {
  width: 100%;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

/* Offset second image — gives the composition depth without a carousel.
   `:last-of-type`, not `:last-child`: the floating date badge is the real last
   child, so a `:last-child` selector here would never match. */
.split__media--stacked { padding-block-end: var(--space-8); padding-inline-end: var(--space-7); }
.split__media--stacked img:first-of-type { aspect-ratio: 4 / 5; }
.split__media--stacked img:last-of-type {
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: 0;
  width: 54%;
  aspect-ratio: 4 / 3;
  border: 6px solid var(--bg);
  border-radius: var(--radius-lg);
}

.badge-float {
  position: absolute;
  inset-block-start: var(--space-5);
  inset-inline-start: calc(-1 * var(--space-4));
  display: grid;
  gap: 2px;
  padding: var(--space-4) var(--space-5);
  background: var(--forest-900);
  color: var(--white);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
}
.badge-float b {
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1;
}
.badge-float span {
  font-size: var(--step-micro);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--leaf-200);
}

.tick-list { display: grid; gap: var(--space-3); margin-block-start: var(--space-5); }
.tick-list li {
  display: grid;
  grid-template-columns: 24px 1fr;
  gap: var(--space-3);
  align-items: start;
  color: var(--text-muted);
}
.tick-list svg { margin-block-start: 5px; color: var(--leaf-500); }

/* --------------------------------------------------------------------------
   10. Cards
   -------------------------------------------------------------------------- */

.card {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-6);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
.card h3 { font-size: var(--step-2); }
.card p { color: var(--text-muted); font-size: var(--step-0); }

.card__icon {
  display: grid;
  place-items: center;
  width: 54px;
  height: 54px;
  color: var(--forest-700);
  background: var(--leaf-100);
  border-radius: var(--radius);
}

/* Numbered process steps */
.steps {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  counter-reset: step;
}
/* Three steps, three columns. Left to `auto-fit` the 1200px container fits a
   fourth 260px track, which stays empty and pulls the three real ones narrow
   and start-aligned. */
@media (min-width: 900px) {
  .steps { grid-template-columns: repeat(3, 1fr); }
}
/* Two steps that are a sequence in time, not a set of options — the term-by-term
   payment schedule. Stacked, they read as "first this, then that". Declared
   after the media query above, which is what lets it win: a media query adds no
   specificity, only source order. */
.steps--stack { grid-template-columns: 1fr; }

.step { position: relative; padding-block-start: var(--space-6); }
.step::before {
  counter-increment: step;
  content: counter(step, decimal-leading-zero);
  position: absolute;
  inset-block-start: 0;
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 700;
  color: var(--amber-500);
}
.step::after {
  content: '';
  position: absolute;
  inset-block-start: 12px;
  inset-inline-start: 3.2rem;
  inset-inline-end: calc(-1 * var(--space-6));
  height: 1px;
  background: linear-gradient(to right, var(--border-strong), transparent);
}
.step:last-child::after { display: none; }
.step h3 { margin-block: var(--space-3) var(--space-2); font-size: var(--step-1); }
.step p { color: var(--text-muted); font-size: var(--step--1); }

.section--dark .step p { color: color-mix(in srgb, var(--cream-100) 72%, transparent); }
.section--dark .step::after { background: linear-gradient(to right, color-mix(in srgb, var(--white) 22%, transparent), transparent); }

/* --------------------------------------------------------------------------
   11. Sponsorship tiers
   -------------------------------------------------------------------------- */

.tiers {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr));
  align-items: stretch;
}

.tier { position: relative; display: flex; }
.tier input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.tier__body {
  display: grid;
  gap: var(--space-3);
  align-content: start;
  width: 100%;
  padding: var(--space-6);
  background: var(--bg-raised);
  border: 2px solid var(--border);
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out),
              transform var(--dur) var(--ease-out),
              box-shadow var(--dur) var(--ease-out);
}
.tier__body:hover { transform: translateY(-3px); box-shadow: var(--shadow); }

.tier input:checked + .tier__body {
  border-color: var(--amber-500);
  box-shadow: var(--shadow-amber);
}
.tier input:focus-visible + .tier__body {
  outline: 3px solid var(--forest-600);
  outline-offset: 3px;
}

.tier__flag {
  position: absolute;
  inset-block-start: calc(-1 * var(--space-3));
  inset-inline-start: var(--space-5);
  padding: var(--space-1) var(--space-4);
  font-size: var(--step-micro);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--forest-950);
  background: var(--amber-400);
  border-radius: var(--radius-pill);
}

.tier__amount {
  font-family: var(--font-display);
  font-size: var(--step-4);
  font-weight: 700;
  line-height: 1;
  color: var(--forest-900);
  font-variant-numeric: tabular-nums;
}
.tier__amount small {
  display: block;
  margin-block-start: var(--space-2);
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-faint);
}
.tier__name { font-family: var(--font-display); font-size: var(--step-1); font-weight: 600; }
.tier__desc { font-size: var(--step--1); color: var(--text-muted); }

.tier__check {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  position: absolute;
  inset-block-start: var(--space-5);
  inset-inline-end: var(--space-5);
  border: 2px solid var(--border-strong);
  border-radius: 50%;
  color: transparent;
  transition: all var(--dur-fast) var(--ease-out);
}
.tier input:checked + .tier__body .tier__check {
  color: var(--forest-950);
  background: var(--amber-500);
  border-color: var(--amber-500);
}

/* Segmented toggles (frequency / currency) */
.segmented {
  display: inline-flex;
  padding: 4px;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
}
.section--dark .segmented {
  background: color-mix(in srgb, var(--white) 8%, transparent);
  border-color: color-mix(in srgb, var(--white) 16%, transparent);
}
.segmented button {
  padding: 0.5em 1.3em;
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--text-muted);
  border-radius: var(--radius-pill);
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.segmented button[aria-pressed='true'] {
  color: var(--forest-950);
  background: var(--white);
  box-shadow: var(--shadow-sm);
}
.section--dark .segmented button { color: color-mix(in srgb, var(--cream-100) 76%, transparent); }
.section--dark .segmented button[aria-pressed='true'] { color: var(--forest-950); background: var(--amber-400); }

/* Live summary panel */
.summary {
  position: sticky;
  inset-block-start: calc(var(--header-h) + var(--space-5));
  display: grid;
  gap: var(--space-5);
  padding: clamp(var(--space-5), 3vw, var(--space-7));
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
}
.summary__cap {
  margin-block-end: var(--space-2);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-faint);
}
.summary__total {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-family: var(--font-display);
  font-size: var(--step-6);
  font-weight: 700;
  line-height: 1;
  color: var(--forest-900);
  font-variant-numeric: tabular-nums;
}
/* Scoped to the unit. As `.summary__total span` this also caught the total
   itself — both figures live in spans — so the headline number rendered at the
   footnote's size and the step-6 on the parent did nothing. */
.summary__total [data-out-unit] { font-size: var(--step-1); color: var(--text-faint); font-family: var(--font-body); font-weight: 600; }

.summary__lines { display: grid; gap: var(--space-3); }
.summary__line {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: var(--space-3);
  align-items: start;
  font-size: var(--step--1);
  color: var(--text-muted);
}
.summary__line svg { margin-block-start: 4px; color: var(--leaf-500); }

.summary__meta {
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--border);
  font-size: var(--step--1);
  color: var(--text-faint);
}
/* The reassurance line, with its icon in a fixed gutter so the text wraps as a
   block rather than tucking back under the icon on the second line. */
.summary__meta--icon {
  display: grid;
  grid-template-columns: 20px 1fr;
  gap: var(--space-3);
  line-height: 1.5;
}
.summary__meta--icon svg { margin-block-start: 3px; color: var(--leaf-500); }

/* A value that changes should announce itself without being distracting */
.value-flip { animation: flip var(--dur) var(--ease-out); }
@keyframes flip {
  from { opacity: 0.25; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}

/* Calculator: controls beside a sticky summary, stacking on narrow screens */
.calc-layout {
  display: grid;
  gap: var(--space-7);
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  align-items: start;
}
@media (max-width: 1024px) {
  .calc-layout { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   12. Allocation bar
   -------------------------------------------------------------------------- */

.allocation { display: grid; gap: var(--space-5); }
.allocation__bar {
  display: flex;
  height: 44px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--bg-sunken);
}
.allocation__seg {
  display: grid;
  place-items: center;
  font-size: var(--step-micro);
  font-weight: 800;
  color: var(--white);
  transition: flex-grow var(--dur-slow) var(--ease-out);
}
.allocation__seg:nth-child(1) { background: var(--forest-800); }
.allocation__seg:nth-child(2) { background: var(--forest-600); }
.allocation__seg:nth-child(3) { background: var(--leaf-500); color: var(--forest-950); }
.allocation__seg:nth-child(4) { background: var(--amber-500); color: var(--forest-950); }

.allocation__key {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
}
.allocation__key li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--step--1);
  color: var(--text-muted);
}
.allocation__key i {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  background: var(--dot, var(--forest-800));
}

/* ---- fee schedule ----
   A real <table>, because this is tabular data and a parent may well want to
   read it with a screen reader or copy it into a message. The card it sits in
   is sticky at desktop width so the figures stay beside the explanation. */

.card--fees {
  gap: var(--space-5);
  align-content: start;
  background: var(--white);
  border-radius: var(--radius-xl);
}
@media (min-width: 900px) {
  .card--fees {
    position: sticky;
    inset-block-start: calc(var(--header-h) + var(--space-5));
  }
}
/* The card lifts on hover by default; a sticky panel of numbers has no reason to. */
.card--fees:hover { transform: none; box-shadow: none; }

.fee-table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}
.fee-table th,
.fee-table td {
  padding-block: var(--space-4);
  text-align: start;
  vertical-align: baseline;
  border-block-end: 1px solid var(--border);
}
/* The last row's rule has nothing under it but card padding, so it reads as
   an unfinished edge rather than a separator. */
.fee-table tbody tr:last-child :is(th, td) { border-block-end: 0; }
.fee-table thead th {
  padding-block: 0 var(--space-3);
  font-size: var(--step-micro);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.fee-table tbody th {
  font-size: var(--step-0);
  font-weight: 700;
  line-height: 1.4;
}
/* The sub-line under each term name — what that payment actually covers. */
.fee-table tbody th span {
  display: block;
  margin-block-start: 2px;
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--text-muted);
}
.fee-table .num { text-align: end; white-space: nowrap; }
.fee-table td.num {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--forest-800);
}
.fee-table__total th,
.fee-table__total td {
  padding-block-start: var(--space-5);
  border-block-end: 0;
}
.fee-table__total td.num { font-size: var(--step-3); }

/* --------------------------------------------------------------------------
   13. Gallery
   -------------------------------------------------------------------------- */

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
  gap: var(--space-4);
}
.gallery__item {
  position: relative;
  display: block;
  padding: 0;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--bg-sunken);
  cursor: zoom-in;
}
.gallery__item img {
  width: 100%;
  /* `height: 100%` first, `aspect-ratio` as the fallback, and the order matters.
     Wherever the tile has a height of its own the photograph fills it: on the
     gallery page the feature tile spans two rows, which forces both of those
     rows taller than a 4/3 crop, and without this the four tiles beside it each
     showed a 72px band of empty `--bg-sunken` below the image. Where the tile's
     height is indefinite instead — the last row, the single-column layout, the
     homepage rail — `height: 100%` resolves to auto and the aspect ratio shapes
     the tile as before. */
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}
.gallery__item:hover img { transform: scale(1.05); }

.gallery__item figcaption {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  padding: var(--space-6) var(--space-4) var(--space-4);
  font-size: var(--step--1);
  font-weight: 600;
  text-align: start;
  color: var(--white);
  background: linear-gradient(to top, color-mix(in srgb, var(--forest-950) 88%, transparent), transparent);
  opacity: 0;
  translate: 0 8px;
  transition: opacity var(--dur) var(--ease-out), translate var(--dur) var(--ease-out);
}
.gallery__item:hover figcaption,
.gallery__item:focus-visible figcaption { opacity: 1; translate: 0; }

/* First tile spans two columns on wide screens — breaks the grid monotony.
   Scoped to `.gallery`: the homepage now uses the same tiles inside `.rail`,
   where a two-column span would blow the track sizing apart. */
@media (min-width: 900px) {
  .gallery > .gallery__item:first-child { grid-column: span 2; grid-row: span 2; }
  .gallery > .gallery__item:first-child img { aspect-ratio: 1 / 1; }
}

.lightbox {
  width: min(92vw, 1100px);
  padding: 0;
  background: transparent;
  border: 0;
  overflow: visible;
}
.lightbox::backdrop {
  background: color-mix(in srgb, var(--forest-950) 88%, transparent);
  backdrop-filter: blur(6px);
}
.lightbox img {
  width: 100%;
  max-height: 80svh;
  object-fit: contain;
  border-radius: var(--radius);
}
.lightbox__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-block-start: var(--space-4);
  color: var(--white);
}
.lightbox__caption { font-size: var(--step--1); font-weight: 600; }
.lightbox__close {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  color: var(--white);
  background: color-mix(in srgb, var(--white) 14%, transparent);
  border-radius: 50%;
}
.lightbox__close:hover { background: color-mix(in srgb, var(--white) 26%, transparent); }

/* --------------------------------------------------------------------------
   14. Quote / testimonial
   -------------------------------------------------------------------------- */

.quote { display: grid; gap: var(--space-5); }
.quote blockquote {
  font-family: var(--font-display);
  font-variation-settings: 'SOFT' 44, 'WONK' 1;
  font-size: var(--step-3);
  line-height: 1.32;
  letter-spacing: -0.015em;
}
.quote__mark {
  font-family: var(--font-display);
  font-size: var(--step-7);
  line-height: 0.6;
  color: var(--amber-500);
}
.quote__by { display: flex; align-items: center; gap: var(--space-4); }
.quote__by img { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; }
.quote__by b { display: block; }
.quote__by span { font-size: var(--step--1); color: var(--text-faint); }

/* --------------------------------------------------------------------------
   15. Accordion (FAQ) — native <details>
   -------------------------------------------------------------------------- */

.faq { display: grid; gap: var(--space-3); }
.faq details {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  transition: border-color var(--dur-fast) var(--ease-out);
}
.faq details[open] { border-color: var(--forest-600); }
.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5);
  font-weight: 700;
  cursor: pointer;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '';
  flex: none;
  width: 11px;
  height: 11px;
  border-inline-end: 2px solid var(--forest-700);
  border-block-end: 2px solid var(--forest-700);
  rotate: 45deg;
  transition: rotate var(--dur) var(--ease-out);
}
.faq details[open] summary::after { rotate: 225deg; }
.faq__body { padding: 0 var(--space-5) var(--space-5); color: var(--text-muted); }

/* --------------------------------------------------------------------------
   16. Forms
   -------------------------------------------------------------------------- */

/* ---- numbered steps in a form ----
   Each step of the giving flow is a real <fieldset>, so its <legend> becomes
   the group's accessible name and a screen reader announces "Step one, how
   much and how often" before the first radio rather than reading three
   unlabelled sets of amounts in a row. The browser's default border and
   padding go: the step number and the surrounding whitespace already do that
   grouping, and the chrome fights the card it sits beside. */

.step-group {
  border: 0;
  padding: 0;
  /* Inline only. A fieldset's UA margin is `margin-inline: 2px` — it has no
     block margin to clear — and a blanket `margin: 0` here would also cancel
     the `.stack > * + *` rule that spaces the steps apart, because the two
     selectors have equal specificity and this one comes later in the file.
     That collapses all three steps into one another. */
  margin-inline: 0;
  /* The fieldset fix: a fieldset takes an automatic minimum size from its
     content, so one long word or a wide grid inside it stretches the whole
     form past its column instead of wrapping. */
  min-inline-size: 0;
}
.step-group > legend {
  padding: 0;
  margin-block-end: var(--space-5);
}
.step-group__title {
  display: block;
  margin-block-start: var(--space-2);
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 600;
  line-height: 1.2;
  color: var(--forest-950);
}

/* An opt-in with its box beside the words rather than above them. The 4px
   nudge puts the box on the first line's baseline, not its box top. */
.check {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-3);
  align-items: start;
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--text-muted);
}
/* The wrapping label means the whole three-line row takes the tap, so the box
   itself was never the hit area — but 13px of default checkbox is still a small
   thing to aim at, and the default checked state paints Chrome blue in the
   middle of a green and amber page. */
.check input {
  margin-block-start: 3px;
  inline-size: 1.15rem;
  block-size: 1.15rem;
  accent-color: var(--forest-700);
}

.field { display: grid; gap: var(--space-2); }
.field label { font-size: var(--step--1); font-weight: 700; }
.field input,
.field select,
.field textarea {
  /* A control's automatic minimum size is its min-content width — for a
     <select> that is set by its longest <option>. As grid items these could
     therefore push the track wider than the container: the topic select on
     contact.html sized its track to 373px inside a 277px field and ran 47px
     off a 375px screen, chevron and all. It did not show up as a document
     scrollbar because body has overflow-x: clip, so it simply cut off.
     min-width: 0 lets them shrink to the track instead. */
  min-width: 0;
  padding: 0.8em 1em;
  background: var(--bg-raised);
  border: 1.5px solid var(--border-strong);
  border-radius: var(--radius);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--forest-600);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--forest-600) 16%, transparent);
}
.field__hint { font-size: 0.8rem; color: var(--text-faint); }
/* The "this form isn't wired up yet" message. It is focused when shown, so it
   has to read as a real message rather than a hint. */
.field__hint--notice {
  padding: var(--space-3) var(--space-4);
  font-size: var(--step--1);
  color: var(--forest-950);
  background: var(--amber-100);
  border-radius: var(--radius-sm);
}

/* Sits below the preset tiers: a wider input, labelled, so "or type your own"
   never looks like it belongs to the last tier card. Capped — a four-figure
   amount in a field the width of the form reads as a mistake. */
.field--amount {
  max-width: 20rem;
  padding-block-start: var(--space-5);
  border-block-start: 1px solid var(--border);
}
.section--dark .field--amount { border-color: color-mix(in srgb, var(--white) 16%, transparent); }
.section--dark .field--amount label { color: var(--cream-100); }

.field-row {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
}

.input-money { position: relative; }
.input-money span {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 1em;
  display: grid;
  place-items: center;
  font-weight: 700;
  color: var(--text-faint);
}
.input-money input { width: 100%; padding-inline-start: 2.4em; font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   17. Footer
   -------------------------------------------------------------------------- */

.site-footer {
  padding-block: var(--space-9) var(--space-6);
  background: var(--forest-950);
  color: color-mix(in srgb, var(--cream-100) 82%, transparent);
}
.site-footer__grid {
  display: grid;
  gap: var(--space-7);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
}
.site-footer__grid > :first-child { grid-column: span 1; }

.site-footer h3 {
  margin-block-end: var(--space-4);
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* Pale mint, not mid-green — the old footer failed contrast here */
  color: var(--leaf-200);
}
.site-footer a {
  color: color-mix(in srgb, var(--cream-100) 86%, transparent);
  text-decoration: none;
}
.site-footer a:hover { color: var(--white); text-decoration: underline; }

.site-footer .brand { color: var(--white); }
.site-footer .brand__tag { color: var(--leaf-200); }

.site-footer__blurb {
  max-width: 34ch;
  margin-block-start: var(--space-5);
  font-size: var(--step--1);
  line-height: 1.6;
}
/* In the newsletter column the blurb is followed by a form, not more prose. */
.site-footer__blurb--tight { margin-block: 0 var(--space-4); }

.footer-list { display: grid; gap: var(--space-3); font-size: var(--step--1); }

.contact-list { display: grid; gap: var(--space-4); font-size: var(--step--1); }
.contact-list li { display: grid; grid-template-columns: 22px 1fr; gap: var(--space-3); }
.contact-list svg { margin-block-start: 3px; color: var(--leaf-200); }

.site-footer__base {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  justify-content: space-between;
  align-items: center;
  margin-block-start: var(--space-8);
  padding-block-start: var(--space-5);
  border-block-start: 1px solid color-mix(in srgb, var(--white) 14%, transparent);
  font-size: 0.82rem;
  color: color-mix(in srgb, var(--cream-100) 62%, transparent);
}

.social { display: flex; gap: var(--space-3); }
/* 44px rather than 40: these are the only controls on the site that are small
   in both directions, three circles side by side, and they are the last thing
   a thumb reaches for at the bottom of a long page. Everything else under 44
   is a full-width row where the short side is the only small dimension. */
.social a {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid color-mix(in srgb, var(--white) 20%, transparent);
  border-radius: 50%;
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.social a:hover { background: var(--forest-700); border-color: var(--forest-600); }

/* --------------------------------------------------------------------------
   18. Sticky give bar (small screens)
   -------------------------------------------------------------------------- */

.give-bar {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: 90;
  display: none;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  padding-block-end: max(var(--space-3), env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--white) 94%, transparent);
  backdrop-filter: blur(14px);
  border-block-start: 1px solid var(--border);
  box-shadow: 0 -8px 28px -14px color-mix(in srgb, var(--forest-950) 40%, transparent);
  translate: 0 110%;
  transition: translate var(--dur) var(--ease-out);
}
.give-bar.is-visible { translate: 0; }
.give-bar__text { font-size: 0.82rem; line-height: 1.35; color: var(--text-muted); }
.give-bar__text b { display: block; color: var(--text); font-size: var(--step--1); }
.give-bar .btn { margin-inline-start: auto; padding: 0.7em 1.3em; }

/* --------------------------------------------------------------------------
   19. Page banner (inner pages)
   -------------------------------------------------------------------------- */

.page-banner {
  position: relative;
  margin-block-start: calc(-1 * var(--header-h));
  padding-block: calc(var(--header-h) + var(--space-8)) var(--space-8);
  color: var(--white);
  isolation: isolate;
  overflow: hidden;
}
.page-banner__media { position: absolute; inset: 0; z-index: -2; }
.page-banner__media img { width: 100%; height: 100%; object-fit: cover; }
.page-banner::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(95deg,
    color-mix(in srgb, var(--forest-950) 93%, transparent),
    color-mix(in srgb, var(--forest-950) 62%, transparent));
}
.page-banner h1 { font-size: var(--step-6); }
.page-banner p { max-width: 54ch; margin-block-start: var(--space-4); font-size: var(--step-1); color: color-mix(in srgb, var(--white) 84%, transparent); }

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-block-end: var(--space-5);
  font-size: var(--step--1);
  color: color-mix(in srgb, var(--white) 70%, transparent);
}
.breadcrumb a { color: inherit; text-decoration: none; }
.breadcrumb a:hover { color: var(--white); text-decoration: underline; }
.breadcrumb li + li::before { content: '/'; margin-inline-end: var(--space-2); opacity: 0.5; }
.breadcrumb li { display: flex; }

/* --------------------------------------------------------------------------
   20. Narrative components
   --------------------------------------------------------------------------
   The homepage is sequenced as a story a parent reads on the way down: who
   we are, what a day feels like, what it costs, what your gift does. These
   are the pieces that carry each chapter.
   -------------------------------------------------------------------------- */

/* ---- 20a. Doodle stickers ----
   Hand-drawn marks scattered around the photography. They are decoration and
   nothing else: aria-hidden, pointer-events off, and gone below the breakpoint
   where they would start shoving real content around. */

.doodle {
  position: absolute;
  z-index: 2;
  width: var(--d-size, 44px);
  pointer-events: none;
  color: var(--d-color, var(--leaf-500));
  rotate: var(--d-rot, 0deg);
  animation: doodle-float var(--d-dur, 7s) var(--ease-out) var(--d-delay, 0s) infinite alternate;
}
.doodle svg { width: 100%; height: auto; }

@keyframes doodle-float {
  from { translate: 0 0; }
  to { translate: 0 -11px; }
}

/* ---- 20b. Promise strip ----
   The reference's four-up icon row. Ours carries the four facts a parent or a
   sponsor most needs to hear before they will read any further. */

.section--paper { background: var(--bg-raised); --bg-band: var(--bg-raised); }

.promises {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
}
.promise {
  display: grid;
  grid-template-columns: 52px 1fr;
  gap: var(--space-4);
  align-items: start;
}
.promise__icon {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  background: var(--tint, var(--leaf-100));
  border-radius: var(--radius);
  color: var(--tint-ink, var(--forest-700));
}
.promise h3 {
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 800;
  line-height: 1.3;
  letter-spacing: 0;
}
.promise p {
  margin-block-start: var(--space-2);
  font-size: var(--step--1);
  color: var(--text-muted);
}

/* Hairline dividers only at the exact four-column layout. Under `auto-fit` the
   columns wrap, and a leading divider on the first item of a new row reads as
   a mistake — so the rule is scoped to the width where no wrapping happens. */
@media (min-width: 1000px) {
  .promises { grid-template-columns: repeat(4, 1fr); }
  .promise:not(:first-child) {
    padding-inline-start: var(--space-6);
    border-inline-start: 1px solid var(--border);
  }
}

/* ---- 20c. A day at the school ----
   Four circular crops on offset colour discs: the reference's portrait row.
   This is the emotional middle of the page — the part that is about a child's
   day rather than about the institution. */

.days {
  display: grid;
  gap: clamp(var(--space-6), 4vw, var(--space-7));
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
}
.day { text-align: center; }

.day__media {
  position: relative;
  width: min(100%, 210px);
  margin-inline: auto;
  aspect-ratio: 1;
}
/* The disc sits offset behind the photo so a crescent of colour shows at one
   edge — flat-behind would just look like a border. */
.day__media::before {
  content: '';
  position: absolute;
  inset: 0;
  translate: -7px 9px;
  background: var(--tint, var(--leaf-200));
  border-radius: 50%;
}
.day__media img {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}

.day__time {
  display: inline-block;
  margin-block-start: var(--space-5);
  padding: 3px var(--space-3);
  font-size: var(--step-micro);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--forest-800);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
}
.day h3 {
  margin-block-start: var(--space-3);
  font-size: var(--step-2);
}
.day p {
  margin-block-start: var(--space-2);
  font-size: var(--step--1);
  color: var(--text-muted);
}

/* ---- 20c-ii. People row ----
   The same circular-portrait treatment as the day row, for staff and trustees.
   Structurally a twin of `.day`, kept separate because the two drift: a person
   card carries a role above the name and can be marked as an unfilled slot,
   and neither of those belongs on a time of day.

   Names are real; faces are not published yet. A card therefore shows initials
   via .person__monogram rather than an empty <img>, and .is-placeholder stays
   available for a card that has neither. See README § "Our people". */

.people {
  display: grid;
  gap: clamp(var(--space-6), 4vw, var(--space-7));
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
}
.person {
  position: relative;
  text-align: center;
}

.person__media {
  position: relative;
  width: min(100%, 190px);
  margin-inline: auto;
  aspect-ratio: 1;
}
.person__media::before {
  content: '';
  position: absolute;
  inset: 0;
  translate: -7px 9px;
  background: var(--tint, var(--leaf-200));
  border-radius: 50%;
}
.person__media img {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}

/* Initials, for a person whose name we have and whose photograph we do not.
   This is not a placeholder: it is a deliberate stand-in that says "this is a
   real person, we just have not published their face". An <img> with no file
   behind it would say the opposite — broken, or worse, a stock stranger. When a
   real photograph arrives, swap the span for an <img> with descriptive alt and
   nothing else has to change. */
.person__monogram {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 50%;
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 1.2rem + 2.4vw, 2.6rem);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--forest-800);
}

.person__role {
  display: inline-block;
  margin-block-start: var(--space-5);
  padding: 3px var(--space-3);
  font-size: var(--step-micro);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--forest-800);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
}
.person h3 {
  margin-block-start: var(--space-3);
  font-size: var(--step-1);
}
.person p {
  margin-block-start: var(--space-2);
  font-size: var(--step--1);
  color: var(--text-muted);
}

/* Same conspicuous treatment as a template testimonial, and for the same
   reason. The outline hugs the disc rather than the whole card so a row of
   four unfilled slots still reads as a row. */
.person.is-placeholder .person__media img {
  outline: 2px dashed var(--amber-500);
  outline-offset: 5px;
}
.person.is-placeholder h3 { color: var(--text-muted); }
/* Matched on the element too, not just the class: `.card p` and `.person p`
   both set muted ink at class+element specificity, so a lone class selector
   loses and the near-black below never applied — the tag was rendering muted
   green on amber at 4.2:1, under the AA floor. */
.person__tag,
p.person__tag {
  display: inline-block;
  margin-block-start: var(--space-3);
  padding: 3px var(--space-3);
  font-size: var(--step-micro);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--forest-950);
  background: var(--amber-400);
  border-radius: var(--radius-pill);
}

/* ---- 20c-ii. Roster ----
   Seven names, no photographs and no biographies. Seven full .person cards
   would have been seven big circles of nothing, which reads as a page waiting
   to be finished. A wrapping row of name chips reads as a list that is
   complete, because it is — these are all of them. */
.roster {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  /* Uncapped, the seven chips packed six onto one line and left "Surajo Ibrahim
     Mohammed" alone on the second, which reads as a mistake rather than a list.
     Narrowing the track to roughly four chips wide splits it 4 + 3. */
  max-width: 48rem;
  margin-inline: auto;
  padding: 0;
  list-style: none;
}
.roster li {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 5px var(--space-5) 5px 5px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--forest-950);
}
.roster__mono {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: none;
  background: var(--tint, var(--leaf-200));
  border-radius: 50%;
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--forest-900);
}
/* On a phone no two chips fit side by side anyway, so centring them produced a
   ragged column of different-width pills. Full width turns the same markup into
   an ordinary list with the initials lined up down the left edge. */
@media (max-width: 30rem) {
  .roster li { width: 100%; }
}

/* ---- 20d. Arch media (editorial split) ----
   Replaces the plain rounded rectangle with the arch-on-blob treatment from the
   reference. The inset photo's border is painted in the band colour, so every
   tinted section must declare --bg-band or the frame will look like a bug. */

.split__media--arch {
  padding-block-end: var(--space-7);
  padding-inline-end: var(--space-6);
}
.split__media--arch::before {
  content: '';
  position: absolute;
  z-index: 0;
  inset-block: -5% 7%;
  inset-inline: 14% -7%;
  background: var(--tint, var(--clay-200));
  border-radius: var(--blob);
  animation: blob-drift 22s var(--ease-out) 1s infinite alternate;
}
.split__media--arch img:first-of-type {
  position: relative;
  z-index: 1;
  aspect-ratio: 4 / 5;
  border-radius: var(--arch);
}
.split__media--arch img:last-of-type {
  position: absolute;
  z-index: 2;
  inset-block-end: 0;
  inset-inline-end: 0;
  width: 52%;
  aspect-ratio: 4 / 3;
  border: 6px solid var(--bg-band, var(--bg));
  border-radius: var(--radius-xl);
}
/* The badge is `position: absolute` with `z-index: auto`, which paints below
   both photos' explicit z-indexes — it would vanish behind the arch. */
.split__media--arch .badge-float { z-index: 3; }

/* ---- 20e. Parents' voices ----
   Heading on the left, quote card on the right, as in the reference. */

.voices {
  display: grid;
  gap: clamp(var(--space-6), 5vw, var(--space-8));
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  align-items: center;
}

.voice {
  position: relative;
  padding: clamp(var(--space-6), 4vw, var(--space-7));
  background: var(--bg-raised);
  border-radius: var(--radius-2xl);
  box-shadow: var(--shadow);
}
.voice + .voice { margin-block-start: var(--space-5); }

.voice__mark {
  position: absolute;
  inset-block-start: calc(-1 * var(--space-5));
  inset-inline-start: clamp(var(--space-5), 4vw, var(--space-7));
  font-family: var(--font-display);
  font-size: 4.5rem;
  line-height: 1;
  color: var(--amber-400);
}
.voice blockquote {
  font-family: var(--font-display);
  font-variation-settings: 'SOFT' 44, 'WONK' 1;
  font-size: var(--step-2);
  line-height: 1.4;
  letter-spacing: -0.01em;
}
.voice__by {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-block-start: var(--space-6);
  padding-block-start: var(--space-5);
  border-block-start: 1px solid var(--border);
}
.voice__by img {
  width: 54px;
  height: 54px;
  flex: none;
  border-radius: 50%;
  object-fit: cover;
}
.voice__by b { display: block; font-size: var(--step-0); }
.voice__by span { font-size: var(--step--1); color: var(--text-faint); }

/* Deliberately conspicuous. The quotes shipped here are templates, not real
   parents — no testimonial on this site is invented. Delete `is-placeholder`
   and the tag once real, consented quotes are in. See README. */
.voice.is-placeholder {
  outline: 2px dashed var(--amber-500);
  outline-offset: 5px;
}
.voice.is-placeholder blockquote { color: var(--text-muted); }
.voice__tag {
  position: absolute;
  inset-block-start: var(--space-4);
  inset-inline-end: var(--space-4);
  padding: 3px var(--space-3);
  font-size: var(--step-micro);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--forest-950);
  background: var(--amber-400);
  border-radius: var(--radius-pill);
}

/* ---- 20f. Scroll rail ----
   The reference's arrow-driven photo strip. Built on scroll-snap so it swipes,
   scrolls and keyboards natively — the arrow buttons are an addition, not the
   mechanism, so the rail still works if the JS never loads. */

.rail-head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  align-items: end;
  justify-content: space-between;
  margin-block-end: var(--space-6);
}
/* The heading already has its own bottom margin from `.section-head`; inside a
   flex row aligned to `end` that margin pushes the arrows out of line. */
.rail-head__text { margin-block-end: 0; }

.rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: clamp(230px, 31%, 360px);
  gap: var(--space-4);
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scroll-snap-type: x proximity;
  /* Room for the focus ring, which would otherwise be clipped by the scroller. */
  padding: 4px;
  margin: -4px;
  scrollbar-width: thin;
  scrollbar-color: var(--cream-200) transparent;
}
.rail > * { scroll-snap-align: start; }

.rail-nav { display: flex; gap: var(--space-2); }
.rail-nav button {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  color: var(--forest-800);
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  transition: background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              opacity var(--dur) var(--ease-out);
}
.rail-nav button:hover:not(:disabled) { background: var(--forest-800); color: var(--white); }
.rail-nav button:disabled { opacity: 0.35; cursor: default; }

/* On a rail the captions must be legible without hovering — half these tiles
   are reached by swiping on a touch screen, where hover never happens. */
.rail .gallery__item figcaption {
  opacity: 1;
  translate: 0;
  padding-block-start: var(--space-8);
}


/* --------------------------------------------------------------------------
   21. Scroll reveal
   -------------------------------------------------------------------------- */

[data-reveal] {
  opacity: 0;
  translate: 0 18px;
  transition: opacity var(--dur-slow) var(--ease-out), translate var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
[data-reveal].is-visible { opacity: 1; translate: 0; }

/* Nothing should be invisible if JS fails to run */
.no-js [data-reveal] { opacity: 1; translate: 0; }

/* --------------------------------------------------------------------------
   22. Dark-band ink

   This has to sit after every component rule, which is why it is down here
   rather than beside `.section--dark` in section 3.

   A component that hard-codes dark ink does it with class+element
   specificity — `.prose p`, `.allocation__key li`. That ties with
   `.section--dark p`, and a tie is settled by source order, so declaring
   the band's ink early meant the components quietly won: the scholarship
   copy on admission.html and the allocation key on sponsor.html were
   rendering dark-green on dark-green at 2.1:1.

   The mirror of that bug is a light surface inside a dark band — a .card or
   a .tier__body — which inherits the band's light ink onto its own white
   background. "What a scholarship covers" was white-on-white at 1:1, and
   the sponsor tier names were cream-on-white at 1.15:1.

   So: set the band ink once, then hand the light surfaces back their own.
   -------------------------------------------------------------------------- */

.section--dark :is(h2, h3, h4) { color: var(--white); }
.section--dark :is(p, li, dd, blockquote, figcaption) {
  color: color-mix(in srgb, var(--cream-100) 82%, transparent);
}

/* A child with its own light background needs the dark ink put back. Scoping
   by the surface rather than by each text element inside it means a new
   component dropped into a dark band only has to name itself here. */
.section--dark :is(.card, .tier__body, .faq details) { color: var(--text); }
.section--dark :is(.card, .tier__body, .faq details) :is(h2, h3, h4) { color: var(--forest-900); }
.section--dark :is(.card, .tier__body, .faq details) :is(p, li, dd) { color: var(--text-muted); }

/* --------------------------------------------------------------------------
   23. Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 1024px) {
  .summary { position: static; }
  .voices { grid-template-columns: 1fr; }
}

/* The hero composition needs two real columns. Below this the photo column is
   narrower than the inset photo it has to contain, so it stacks instead. */
@media (max-width: 980px) {
  .hero__grid { grid-template-columns: 1fr; }
  .hero__copy { max-width: 42rem; }
  .hero::before { width: 100%; opacity: 0.6; }
}

/* Stickers crowd a narrow screen and start shoving real content around. */
@media (max-width: 900px) {
  .doodle { display: none; }
}

@media (max-width: 880px) {
  :root { --header-h: 68px; }

  .nav-toggle { display: grid; }

  .site-nav {
    position: fixed;
    inset-block: 0;
    inset-inline-end: 0;
    z-index: 99;
    width: min(84vw, 340px);
    padding: calc(var(--header-h) + var(--space-5)) var(--space-5) var(--space-6);
    background: var(--bg);
    border-inline-start: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
    translate: 100% 0;
    /* Parked off-screen is not the same as gone. Without this the drawer's six
       links and its CTA stay in the tab order and in the accessibility tree,
       so a keyboard or screen-reader user walks through a menu nobody can see
       before reaching the page. `visibility` is the one property that removes
       them without unloading the panel, and it transitions cleanly — a closing
       drawer stays visible for the whole slide, then goes. Same pattern as
       .nav-scrim above. */
    visibility: hidden;
    transition: translate var(--dur) var(--ease-out), visibility var(--dur);
    overflow-y: auto;
  }
  body.nav-open .site-nav { translate: 0; visibility: visible; }

  .nav__list { flex-direction: column; align-items: stretch; gap: var(--space-1); }
  .nav__link {
    padding: var(--space-4);
    font-size: var(--step-1);
    border-radius: var(--radius);
  }
  .nav__link[aria-current='page']::after { inset-inline: auto var(--space-4); inset-block: 50% auto; translate: 0 -50%; width: 6px; height: 6px; border-radius: 50%; }

  .site-nav .btn { margin-block-start: var(--space-5); width: 100%; }

  /* The toggle is a JS-driven button, so without JS the drawer can never open
     and hiding it would leave the header with no navigation at all. Fall back
     to a plain list wrapped under the brand — not pretty, but reachable. The
     footer carries the same links on every page, so this is a second net
     rather than the only one. */
  .no-js .site-header__inner { flex-wrap: wrap; }
  .no-js .nav-toggle { display: none; }
  .no-js .site-nav {
    position: static;
    visibility: visible;
    translate: none;
    width: 100%;
    padding: 0 0 var(--space-5);
    background: none;
    border-inline-start: 0;
    box-shadow: none;
  }
  .no-js .site-nav .btn { width: auto; }

  .split--reverse > :first-child { order: 0; }
  .badge-float { inset-inline-start: var(--space-4); }

  .step::after { display: none; }

  .give-bar { display: flex; }
  body { padding-block-end: 0; }

  /* Footer links measured 17px tall on a phone — the text box and nothing
     more — and the email link 22px. These are full-width rows, so the whole
     row should take the tap. The list gap goes to zero because the padding
     now provides the rhythm; without that the footer grows by a third. */
  .footer-list { gap: 0; }
  .footer-list a {
    display: flex;
    align-items: center;
    min-height: 44px;
  }
  .contact-list a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* The last of the small targets at 375px: the breadcrumb link (22px tall),
     the inline arrow links (27px) and the segmented toggles (36px). Height
     only — none of them move or change size on the page, and `min-height` on
     an inline-flex control grows the hit area without shifting the baseline
     of the text beside it. All four sit at 44 so there is one number to
     remember rather than two.

     Not in this list: the opt-in checkbox label, which measures 348x40. Its
     short side is the only one under 44 and a 348px-wide band is already an
     easy thing to hit; raising it would only add dead space under a one-line
     label. */
  .breadcrumb a { display: inline-flex; align-items: center; min-height: 44px; }
  .link-arrow { min-height: 44px; }
  .segmented button { min-height: 44px; }
}

@media (max-width: 520px) {
  .hero h1 { font-size: var(--step-6); }
  .btn-row .btn { width: 100%; }
  .hero__media { padding-inline-start: var(--space-4); padding-block-end: var(--space-7); }
  .hero__chip { inset-inline-end: 0; }
  .split__media--stacked { padding-block-end: var(--space-7); padding-inline-end: var(--space-5); }
  .split__media--arch { padding-block-end: var(--space-7); padding-inline-end: var(--space-5); }
  .give-bar__text { display: none; }
  .give-bar .btn { margin-inline: 0; flex: 1; }
}

/* --------------------------------------------------------------------------
   23. Reduced motion
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal] { opacity: 1; translate: none; }
  .btn:hover,
  .card:hover,
  .stat:hover,
  .tier__body:hover { transform: none; }
  .gallery__item:hover img { transform: none; }
  /* The blobs animate their own border-radius, which the blanket rule above
     only freezes rather than resets — pin them to the static shape. */
  .hero__blob,
  .split__media--arch::before { animation: none; border-radius: var(--blob); }
}
