/* ==========================================================================
   Go To Many CIC — "Ink and Oat"
   Design system per Lisa's brand palette, August 2026.
   --------------------------------------------------------------------------
   Her rules, implemented literally:
   · Bark is the ONLY button colour. One primary action per screen.
   · Paper is the text on Bark. Hover darkens to Ink.
   · Taupe is decoration only — never body text.
   · Never Stone on Sand.
   · Sand borders at 1px. No heavy rules or boxes.
   · No colour does the work, so hierarchy comes from size, weight and space.
     Be generous with spacing.
   --------------------------------------------------------------------------
   1. Tokens      5. Buttons     9. Cards
   2. Reset       6. Header     10. Lists & data
   3. Layout      7. Hero       11. Forms
   4. Type        8. Bands      12. Footer
   ========================================================================== */

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

:root {
  /* Palette — exactly as supplied, with one accessibility correction noted. */
  --ink:    #1F2422;   /* headings, body text, footer                       */
  --bark:   #4A3F34;   /* buttons, links, key actions                       */
  --stone:  #686D66;   /* captions, labels, muted text.
                          Supplied as #6B7069, which is 4.35:1 on Oat —
                          just under AA. Darkened to reach 4.55:1.          */
  --taupe:  #A2917C;   /* tags, underlines, small accents. DECORATION ONLY. */
  --sand:   #E2D7C5;   /* borders, dividers, quiet fills                    */
  --oat:    #F4EDE1;   /* page background                                   */
  --paper:  #FDFBF7;   /* cards, forms, panels                             */

  /* Derived, for states only — no new hues. */
  --ink-90:  rgba(31,36,34,.90);
  --ink-70:  rgba(31,36,34,.70);
  --sand-60: rgba(226,215,197,.60);
  --oat-80:  rgba(244,237,225,.80);

  /* Bark carried toward Taupe, for the accent-phrase gradient. Taupe itself
     cannot be used as text: it is 2.62:1 on Oat, which fails even the 3:1
     large-text threshold, and the brief marks it decoration only. This stop
     is 4.55:1 on Oat and 3.64:1 against Sand, the darkest point of the hero
     panel, so the gradient clears AA for large text everywhere it lands.
     Derived the same way Stone was corrected from #6B7069 to #686D66. */
  --bark-warm: #7C6A55;

  /* Soft fills for panels and cards. Tints of the supplied palette only. */
  --tint-warm:  #F7EFE3;
  --tint-sand:  #EFE4D3;
  --tint-cool:  #EDEDE7;
  --tint-paper: #FFFDFA;

  /* Type */
  --display: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  /* Body face. The brief specified Inter, and Inter is a fine text face, but it
     is also the single most-used default in AI-generated sites and reads as one.
     IBM Plex Sans is humanist rather than neo-grotesque: slightly warmer, more
     character in the letterforms, and drawn for long reading, which suits pages
     of dense admissions detail. Headings stay Plus Jakarta Sans.
     To revert, put "Inter" back as the first family and swap the name in the
     Google Fonts link in build.py. Nothing else depends on it. */
  --sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  --t-xs:   .8125rem;
  --t-sm:   .9375rem;
  /* Was 1.0625rem flat. On anything wider than a laptop the body copy sat
     the same size against a much bigger canvas, which is most of why the
     site read as quiet/small on a real monitor rather than a 13" test
     window. Scales gently with viewport now instead of sitting still. */
  --t-base: clamp(1.0625rem, .18vw + 1.02rem, 1.1875rem);
  --t-lg:   clamp(1.25rem, .5vw + 1.1rem, 1.5rem);
  /* h3. Kept a clear step below the h2 so the three levels read as three. */
  --t-xl:   clamp(1.3rem, .9vw + 1.1rem, 1.75rem);
  --t-2xl:  clamp(1.875rem, 2vw + 1.35rem, 2.75rem);
  /* One step below the h1, which the hero caps at 52px. This used to top out
     at 60px, so a section heading outrendered the title of the page it sat on
     and each page had h2s at three different sizes: 38, 44 and 60. */
  --t-3xl:  clamp(1.75rem, 1.9vw + 1.05rem, 2.5rem);
  --t-4xl:  clamp(2.5rem, 5.2vw + 1.2rem, 5rem);

  /* Rhythm. Generous, per the brief. */
  --wrap: 1180px;
  --wrap-narrow: 680px;
  /* The ch unit measures the "0" glyph, which is narrower than average
     lowercase — 66ch was letting through ~80 real characters. 56ch lands
     at roughly 68-72, which is the readable range. */
  --measure: 56ch;
  --gutter: clamp(1.25rem, 4.5vw, 2.5rem);
  --section: clamp(4.5rem, 9vw, 8rem);
  --section-tight: clamp(3rem, 5.5vw, 5rem);

  --r-sm: 10px;
  --r: 14px;
  --r-lg: 22px;
  --r-xl: 30px;      /* big soft panels — the friendly shape */

  --ease: cubic-bezier(.22,.75,.3,1);
  --fast: 170ms;
  --header-h: 4.5rem;
}

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

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

/* The UA sheet gives [hidden] display:none, but any author rule that sets a
   display wins on specificity: .field is display:grid, so the donation form's
   custom amount box stayed on screen with the attribute set. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 5.5rem;
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--oat);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-base);
  line-height: 1.65;
  font-feature-settings: "kern" 1, "liga" 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img, svg, video { display: block; max-width: 100%; }
/* Was img-only. The CPR page's real video inherited a width/height PAIR of
   HTML attributes the same way every <img> on the site already does, but
   without this rule to match, the browser held the video's height at its
   raw 850px attribute while width scaled down to fit — a visibly stretched
   9:16 clip squeezed into a 320px-wide box. .hero__bg video's own explicit
   height:100% (for its full-bleed cover crop) still wins there, same as any
   other more specific img rule already overriding this for photos. */
img, video { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }

:focus-visible {
  outline: 2px solid var(--bark);
  outline-offset: 3px;
  border-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip {
  position: absolute; left: 1rem; top: -3.5rem; z-index: 100;
  padding: .7rem 1.1rem; background: var(--ink); color: var(--paper);
  text-decoration: none; border-radius: var(--r);
  transition: top var(--fast) var(--ease);
}
.skip:focus { top: 1rem; }

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

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }
.wrap--narrow { max-width: var(--wrap); }
.wrap--narrow > * { max-width: var(--wrap-narrow); }

/* On an Ink band a narrow measure left the block ending around 945px against
   a 1268px right edge, so the band's own dark panel ran on past its contents.
   Twelve columns: the heading takes the left six, the supporting copy and the
   action take the right six, and the row closes where every other row does. */
@media (min-width: 900px) {
  .band > .wrap--narrow {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: .35rem 24px;
    align-items: start;
  }
  .band > .wrap--narrow > *          { max-width: none; }
  .band > .wrap--narrow > .eyebrow   { grid-column: 1 / -1; }
  .band > .wrap--narrow > h2         { grid-column: 1 / span 6; }
  .band > .wrap--narrow > .lede,
  .band > .wrap--narrow > p          { grid-column: 7 / span 6; max-width: 46ch; }
  .band > .wrap--narrow > .btn-row   { grid-column: 7 / span 6; margin-top: 1.5rem; }
}

.section { padding-block: var(--section); }
.section--tight { padding-block: var(--section-tight); }

/* Sections are separated by SPACE and by a change of surface — not by a rule
   between every one, which turns a page into a list of identical slices.
   A hairline appears only where two same-coloured sections actually meet. */
.section + .section:not(.on-paper):not(.band):not(.statement) { }
.on-paper + .on-paper { border-top: 1px solid var(--sand); }

.grid { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); }
@media (min-width: 700px) {
  .g-2 { grid-template-columns: repeat(2, 1fr); }
  .g-3 { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 560px) and (max-width: 699px) {
  .g-3 { grid-template-columns: repeat(2, 1fr); }
}

/* Asymmetric split — the editorial default, not a 50/50. */
.split { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: start; }

/* The per-column h2 override that lived here is gone. It existed because the
   global h2 was 60px and broke a heading in a 440px column over five lines,
   but the side effect was three different h2 sizes on one page. --t-3xl now
   tops out at 40px, which sits correctly under the h1 and holds its lines in
   a split column, so one size serves everywhere. */
@media (min-width: 900px) {
  .split { grid-template-columns: minmax(0,5fr) minmax(0,7fr); }
  .split--flip { grid-template-columns: minmax(0,7fr) minmax(0,5fr); }
}

.stack > * + * { margin-top: 1rem; }
.stack-lg > * + * { margin-top: 1.75rem; }

/* A handful of one-off headings wanted extra breathing room before a new
   subsection and got it as an inline style="margin-top:…" each time.
   Pulled out 18 Sep 2026 — that was the last inline style anywhere on the
   site, and the one thing stopping a style-src CSP without 'unsafe-inline'. */
.mt-125 { margin-top: 1.25rem; }
.mt-15  { margin-top: 1.5rem; }
.mt-2   { margin-top: 2rem; }
.mt-25  { margin-top: 2.5rem; }
.mb-125 { margin-bottom: 1.25rem; }

/* -------------------------------------------------------------- 4. Type - */

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 700;
  color: var(--ink);
  margin: 0;
  text-wrap: balance;
}
h1 { font-size: var(--t-4xl); line-height: 1.06; letter-spacing: -.033em; font-weight: 800; }
h2 { font-size: var(--t-3xl); line-height: 1.12; letter-spacing: -.026em; font-weight: 800; }
h3 { font-size: var(--t-xl);  line-height: 1.28; letter-spacing: -.016em; }
h4 {
  font-family: var(--sans); font-size: var(--t-sm); font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--stone);
}

p { margin: 0; }
p + p { margin-top: 1rem; }

.lede {
  font-size: var(--t-lg);
  line-height: 1.5;
  color: var(--ink-90);
  max-width: 54ch;
}

/* Nothing reads well past ~66 characters, so cap every run of body copy
   wherever it lives. Short utility lines and anything already constrained
   are excluded. */
main p:not(.give__lead):not(.hero__meta *),
main li,
.card p, .steps p, .facts dd {
  max-width: var(--measure);
}
main .lede { max-width: 54ch; }
main .small, .form-note, figcaption { max-width: 50ch; }
/* No exceptions. max-width only ever narrows, so where the grid is already
   tighter than the measure the cap simply does nothing. */

.muted { color: var(--stone); }
.small { font-size: var(--t-sm); }

/* Eyebrow: a label, with a Taupe rule under it. Taupe as decoration only. */
.eyebrow {
  display: inline-block;
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--bark);
  margin-bottom: 1.25rem;
}

a { color: var(--bark); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--ink); }

.sec-head { max-width: 60ch; margin-bottom: clamp(2rem, 4vw, 3rem); }
.sec-head p { margin-top: 1rem; color: var(--ink-90); font-size: var(--t-lg); }

/* Pull quote — a rule and space, no box. */
.quote {
  border-left: 2px solid var(--taupe);
  padding-left: clamp(1.25rem, 3vw, 2rem);
  max-width: 58ch;
}
.quote p {
  font-family: var(--display);
  font-size: var(--t-xl);
  line-height: 1.4;
  color: var(--ink);
}
.quote footer {
  margin-top: 1.1rem;
  font-size: var(--t-sm);
  color: var(--stone);
}
.quote footer b { color: var(--ink); font-weight: 600; display: block; }

/* On the Ink band this attribution was Ink-on-Ink: the founder's name rendered
   at a 1:1 contrast ratio, i.e. invisible, and the role beneath it sat at
   2.98:1. Both are restored to the band's own text colours. */
.band .quote footer   { color: rgba(244,237,225,.72); }
.band .quote footer b { color: var(--paper); }

/* ----------------------------------------------------------- 5. Buttons - */
/* Bark is the only button colour. One primary per screen.                  */

.btn {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .85rem 1.5rem;
  font-size: var(--t-sm); font-weight: 600;
  border-radius: var(--r);
  border: 1px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease),
              border-color var(--fast) var(--ease), transform var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease);
}
.btn svg { flex: none; }

/* A button that only swapped colour on hover never confirmed it was
   clickable until the cursor was already on it. A small lift does that from
   a distance — same physical language as the card hover below, so the
   whole site's "this responds to you" cue is one idea, not two. Pressing
   settles it back down rather than just undoing the lift, which is what
   made it read as a real button instead of a colour change. */
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); transition-duration: 80ms; }

/* Primary. The only filled button on the page. */
.btn--primary { background: var(--bark); color: var(--paper); }
.btn--primary:hover, .btn--primary:focus-visible { background: var(--ink); color: var(--paper); }

/* --- Glow on the one primary action ------------------------------------- */
/* Adapted from a 21st.dev component. The idea worth keeping is @property:
   registering a custom property gives it a type, which is what lets it be
   animated at all (a plain --var cannot be transitioned). The original cycled
   --hue through all 360 degrees over a near-black card; that is the opposite
   of a palette the brief locks to seven values, so only the mechanism carries
   over. Here a single Taupe bloom breathes under the button. Taupe is
   decoration-only under the brief and this never touches a letterform.
   One primary button per screen, so this fires in exactly one place. */
/* inherits MUST be true. The glow is drawn on ::before, and a pseudo-element
   only sees a custom property if that property inherits: with `false` the
   pseudo falls back to initial-value and the shadow renders 0px at 0 alpha,
   which is to say invisible, while the property itself animates correctly on
   the button. Silent, and easy to mistake for a browser support problem. */
@property --glow-spread {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}
@property --glow-alpha {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

.btn--primary {
  position: relative;
  isolation: isolate;
  --glow-spread: 0px;
  --glow-alpha: 0;
  /* transform has to be repeated here: this rule replaces the shared .btn
     transition wholesale, so without it the lift below snaps instead of
     easing — visible as a jolt rather than a hover. */
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease),
              transform var(--fast) var(--ease),
              --glow-spread .5s var(--ease), --glow-alpha .5s var(--ease);
}
.btn--primary::before {
  content: "";
  position: absolute;
  inset: -2px;
  z-index: -1;
  border-radius: inherit;
  box-shadow: 0 0 var(--glow-spread) rgba(162,145,124,var(--glow-alpha));
  pointer-events: none;
}
.btn--primary:hover,
.btn--primary:focus-visible {
  --glow-spread: 22px;
  --glow-alpha: .85;
}

/* On the Ink band the bloom would vanish against the dark, so it warms up. */
.band .btn--primary:hover,
.band .btn--primary:focus-visible {
  --glow-spread: 26px;
  --glow-alpha: .5;
}

@media (prefers-reduced-motion: reduce) {
  .btn--primary { transition: background var(--fast) var(--ease); }
  .btn--primary:hover, .btn--primary:focus-visible { --glow-spread: 0px; --glow-alpha: 0; }
}

/* Secondary. Outlined, never filled — so the primary always wins. */
.btn--line {
  background: transparent; color: var(--ink); border-color: var(--sand);
}
.btn--line:hover, .btn--line:focus-visible { border-color: var(--ink); color: var(--ink); }

/* On the Ink band. */
.band .btn--primary { background: var(--oat); color: var(--ink); }
.band .btn--primary:hover { background: var(--paper); color: var(--ink); }
.band .btn--line { color: var(--oat); border-color: rgba(244,237,225,.35); }
.band .btn--line:hover { border-color: var(--oat); color: var(--oat); }

.btn-row { display: flex; flex-wrap: wrap; gap: .75rem; align-items: center; }

/* A text link that reads as an action. */
.link-arrow {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: var(--t-sm); font-weight: 600;
  color: var(--bark); text-decoration: none;
  border-bottom: 1px solid var(--sand);
  padding-bottom: .15rem;
  transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.link-arrow svg { flex: none; transition: transform var(--fast) var(--ease); }
.link-arrow:hover { color: var(--ink); border-color: var(--ink); }
.link-arrow:hover svg { transform: translateX(3px); }

/* The text plus its underline is 28px tall, under the 44px minimum target.
   The hit area is extended with a pseudo-element rather than with padding, so
   the underline stays tight against the words. Only real links need this: the
   .link-arrow inside a card is a span, and the card itself is the target. */
a.link-arrow { position: relative; }
a.link-arrow::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 50%;
  height: 44px;
  transform: translateY(-50%);
}

/* ------------------------------------------------------------ 6. Header - */

.header {
  position: sticky; top: 0; z-index: 40;
  background: var(--oat-80);
  backdrop-filter: saturate(1.1) blur(10px);
  -webkit-backdrop-filter: saturate(1.1) blur(10px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.header.is-stuck { border-bottom-color: var(--sand); background: var(--oat); }

.header__in {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem; min-height: 4.5rem;
}

.brand {
  font-family: var(--display); font-size: 1.3rem; font-weight: 600;
  color: var(--ink); text-decoration: none; letter-spacing: -.01em;
  display: inline-flex; align-items: center; gap: .5rem;
}
.brand__icon { width: 1.65em; height: 1.65em; flex: none; display: block; }
.brand__text { display: flex; flex-direction: column; line-height: 1.2; }
.brand__name { white-space: nowrap; }
/* Requested twice in one voice note, so it goes wherever the wordmark goes
   rather than only on the home page. Small enough to read as a caption under
   the name, not a second headline. */
.brand__tagline {
  font-family: var(--sans); font-size: .38em; font-weight: 500;
  letter-spacing: .01em; color: var(--stone); white-space: nowrap;
}
.footer .brand__tagline { color: rgba(244,237,225,.6); }

/* The one accent colour outside the seven-token palette, scoped to this one
   mark rather than spent anywhere else. Sage reads as a pulse-monitor green,
   which is the whole reason "pulse to path" works as a mark: CPR and
   admissions progress as one line. Two lightnesses, tuned per surface,
   same hue: header sits on Oat, footer sits on Ink. */
.brand__accent { color: #5F7A54; }
.footer .brand__accent { color: #9FC091; }

.pulse-wrap svg { display: block; }
.pulse-wrap .p-line {
  fill: none; stroke: currentColor; stroke-width: 4.5;
  stroke-linecap: round; stroke-linejoin: round;
}
.pulse-wrap .p-dot { fill: #5F7A54; }

/* Standalone size, CPR page hero — Lisa asked for the mark specifically
   "where the life skills are." Bark rather than Ink: it sits directly
   against the eyebrow label, which is already Bark. */
.hero__pulse { display: inline-block; width: 3.25rem; height: 3.25rem; margin-bottom: 1rem; color: var(--bark); }
.footer .pulse-wrap .p-dot { fill: #9FC091; }

/* Draws once on load, then the end dot beats for about a second and a half
   every 4.5s, resting in between — never continuously moving. Hovering
   freezes whatever is mid-animation in place, and resumes it from there on
   mouse-out, rather than restarting it. Off entirely under reduced motion:
   the elements simply render at rest. */
@media (prefers-reduced-motion: no-preference) {
  .pulse-wrap .p-line { stroke-dasharray: 1; stroke-dashoffset: 1; }
  .pulse-wrap .p-dot {
    opacity: 0; transform-origin: center; transform-box: fill-box; transform: scale(.3);
  }
  .pulse-wrap.play .p-line { animation: brandDraw 1s cubic-bezier(.4,.1,.2,1) forwards; }
  .pulse-wrap.play .p-dot {
    animation: brandPop .35s ease-out forwards .78s,
               brandBeat 4.5s ease-in-out infinite 1.3s;
  }
  .pulse-wrap:hover .p-line,
  .pulse-wrap:hover .p-dot {
    animation-play-state: paused;
  }
  @keyframes brandDraw { to { stroke-dashoffset: 0; } }
  @keyframes brandPop { to { opacity: 1; transform: scale(1); } }
  /* Beats in the first ~1.6s of every 4.5s cycle, rests for the other ~2.9s. */
  @keyframes brandBeat {
    0%, 100% { transform: scale(1); }
    9%  { transform: scale(1.35); }
    18% { transform: scale(1); }
    27% { transform: scale(1.18); }
    36% { transform: scale(1); }
  }
}

.nav { display: none; }
/* Was 1000px, which is where this used to switch on — but seven nav items
   plus the wider logo lockup (icon + tagline) don't actually fit until
   ~1250px; below that the links wrapped onto two lines. Measured against
   the real content, not guessed. */
@media (min-width: 1250px) {
  .nav { display: flex; align-items: center; gap: 1.75rem; }
}
.nav a {
  font-size: var(--t-sm); color: var(--ink); text-decoration: none;
  padding-block: .35rem; border-bottom: 1px solid transparent;
  transition: border-color var(--fast) var(--ease);
}
.nav a:hover { border-bottom-color: var(--taupe); }
.nav a[aria-current="page"] { border-bottom-color: var(--ink); font-weight: 600; }

.nav-toggle {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .55rem .9rem; font-size: var(--t-sm); font-weight: 600;
  background: transparent; color: var(--ink);
  border: 1px solid var(--sand); border-radius: var(--r); cursor: pointer;
}
@media (min-width: 1250px) { .nav-toggle { display: none; } }

.nav-panel {
  display: none;
  padding-block: 1rem 1.75rem;
  border-top: 1px solid var(--sand);
}
.nav-panel.is-open { display: block; }
@media (min-width: 1250px) { .nav-panel { display: none !important; } }
.nav-panel a {
  display: block; padding: .7rem 0;
  border-bottom: 1px solid var(--sand);
  color: var(--ink); text-decoration: none; font-size: var(--t-lg);
}
.nav-panel a:last-of-type { border-bottom: 0; }

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

/* Every inner page gets the same soft panel as the home page, so the site
   reads as one product rather than a landing page bolted to a blog. */
.hero { padding-block: clamp(1.5rem, 3vw, 2.5rem) clamp(2.5rem, 5vw, 4rem); }
.hero > .wrap {
  /* No panel behind the heading. A boxed hero on every page was reading as a
     component the copy had been dropped into. The heading now sits on the
     page, and the rail's hairlines carry the structure instead. */
  padding-block: clamp(2.5rem, 5vw, 4.5rem);
  max-width: var(--wrap);
}
.hero > .wrap--narrow { max-width: var(--wrap); }
.hero .eyebrow { color: var(--bark); }

/* ---- The hero grid ----------------------------------------------------- */
/* Twelve columns. Copy takes 1 to 7, the facts rail takes 8 to 12. The old
   rule here was `.hero > .wrap > * { max-width: 46rem }`, which stopped every
   inner-page hero at 904px against a 1268px right edge. */
.hero__grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(2rem, 4vw, 3rem) 24px;
  align-items: start;
}
.hero__lead { grid-column: 1 / -1; }
.hero__rail { grid-column: 1 / -1; margin: 0; }

@media (min-width: 900px) {
  .hero__lead { grid-column: 1 / span 7; }
  .hero__rail { grid-column: 8 / span 5; }
  /* With no rail on the page, the copy still gets a column more than it had,
     and the panel itself closes the right edge. */
  .hero__lead:only-child { grid-column: 1 / span 8; }
}

/* Facts as rows with a hairline between them, not four bordered cards. */
.hero__rail > div {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0 1rem;
  align-items: baseline;
  padding: .95rem 0;
  border-top: 1px solid var(--sand);
}
.hero__rail > div:last-child { border-bottom: 1px solid var(--sand); }
.hero__rail dt { font-size: var(--t-sm); color: var(--stone); }
.hero__rail dd {
  margin: 0;
  font-family: var(--display);
  font-weight: 800;
  font-size: var(--t-lg);
  letter-spacing: -.02em;
  color: var(--ink);
}

/* One headline treatment for every page. Sized against the wrap and capped by
   measure, so a 45-character headline lands on two or three lines whichever
   page it is on — not two on one page and five on another. */
.hero h1,
.hero__title {
  font-size: clamp(2rem, 3.6vw, 3.25rem);
  line-height: 1.1;
  letter-spacing: -.03em;
  max-width: 18ch;
}
.hero .lede { margin-top: 1.75rem; }
.hero .btn-row { margin-top: 2.25rem; }

.hero__meta {
  display: flex; flex-wrap: wrap; gap: .5rem 1.75rem;
  margin: 2.5rem 0 0; padding-top: 1.25rem;
  border-top: 1px solid var(--sand);
  font-size: var(--t-sm); color: var(--stone);
}
.hero__meta b { color: var(--ink); font-weight: 600; }

.hero__media { border-radius: var(--r-lg); overflow: hidden; }
.hero__media img { width: 100%; }

/* ------------------------------------------------------------- 8. Bands - */
/* Ink band: once per page at most, plus the footer.                        */

.band { background: var(--ink); color: var(--oat); }
.band h1, .band h2, .band h3 { color: var(--paper); }
.band .lede, .band p { color: rgba(244,237,225,.86); }
.band .eyebrow { color: rgba(244,237,225,.72); border-bottom-color: var(--taupe); }
.band a:not(.btn) { color: var(--oat); }
.band .muted { color: rgba(244,237,225,.68); }

/* Paper: the quieter surface, for dense or detailed passages. Alternating
   Oat and Paper is what gives the page its rhythm. */
.on-paper { background: var(--paper); }
.on-paper + .section:not(.on-paper):not(.band) { border-top: 0; }

/* ------------------------------------------------------------- 9. Cards - */
/* Paper panels on Oat, Sand borders at 1px. Nothing heavier.               */

.card {
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: var(--r-lg);
  padding: clamp(1.5rem, 3vw, 2.25rem);
}
/* A column, so the call to action can be pinned to the bottom. Cards in a row
   stretch to a shared height, and with the link sitting straight after the
   copy a three-line card and a four-line card put their links on two
   different lines. */
.card { display: flex; flex-direction: column; }
.card h3, .card h2 { margin-bottom: .75rem; }
.card p { color: var(--ink-90); }
.card > * + * { margin-top: 1rem; }
/* auto, not a fixed gap: takes up the slack so every link in a row lines up.
   Both of these are blockified by the flex container, so they need an explicit
   align-self or the pill and the underline stretch the full card width. */
.card .link-arrow { margin-top: auto; padding-top: 1.25rem; align-self: flex-start; }
.card .tag { align-self: flex-start; }

a.card { text-decoration: none; transition: border-color var(--fast) var(--ease); }
a.card:hover { border-color: var(--taupe); }

/* Price tag — Taupe as a tag background, per the rules. */
.tag {
  display: inline-block;
  padding: .3rem .7rem;
  background: var(--sand);
  color: var(--ink);
  border-radius: 100px;
  font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .03em;
}
/* Paper on Taupe is 2.95:1 and fails AA. It never renders that way today —
   .card p's descendant selector is more specific and overrides this to
   Ink-90 (4.49:1) wherever the badge is actually used — but that was an
   accidental save, not a designed one, and the badge would fail contrast
   in any context without a .card p ancestor. Ink itself (5.16:1) is what
   this rule should specify, deliberately, rather than relying on a more
   specific selector elsewhere to rescue it. */
.tag--free { background: var(--taupe); color: var(--ink); }

/* ------------------------------------------------------ 10. Lists & data - */

/* Was a plain 8px Taupe dot — read as a bullet list rather than a list of
   things actually confirmed true. A small check in its own badge says
   "verified", which is what every one of these lines actually claims
   (trained, insured, checked, included). Sage rather than Taupe: this is
   the same "confirmed" language as the brand mark's pulse dot, not a new
   accent spent twice. */
.ticks { list-style: none; margin: 0; padding: 0; }
.ticks li {
  position: relative;
  padding-left: 2.15rem;
  margin-bottom: .9rem;
  min-height: 1.5rem;
  color: var(--ink-90);
}
.ticks li::before {
  content: "";
  position: absolute; left: 0; top: 0;
  width: 1.5rem; height: 1.5rem; border-radius: 50%;
  background: rgba(95,122,84,.14);
}
.ticks li::after {
  content: "";
  position: absolute; left: .5rem; top: .52rem;
  width: .62rem; height: .34rem;
  border-left: 2px solid #5F7A54;
  border-bottom: 2px solid #5F7A54;
  transform: rotate(-45deg);
}
.ticks li b { color: var(--ink); font-weight: 600; }
.band .ticks li, .footer .ticks li { color: rgba(244,237,225,.86); }
.band .ticks li b, .footer .ticks li b { color: var(--paper); }
.band .ticks li::before, .footer .ticks li::before { background: rgba(159,192,145,.2); }
.band .ticks li::after, .footer .ticks li::after { border-color: #9FC091; }

/* Numbered sequence — the structure carries the meaning. */
.steps { list-style: none; margin: 0; padding: 0; counter-reset: s; }
.steps > li {
  counter-increment: s;
  display: grid;
  gap: .35rem 1.5rem;
  padding: clamp(1.5rem, 3vw, 2rem) 0;
  border-top: 1px solid var(--sand);
}
.steps > li:last-child { border-bottom: 1px solid var(--sand); }
@media (min-width: 760px) {
  .steps > li { grid-template-columns: 4rem 1fr; }
}
.steps > li::before {
  content: counter(s, decimal-leading-zero);
  font-family: var(--display); font-size: var(--t-lg);
  /* Was Taupe: 2.95:1 at 22px/400 weight, which needs 4.5:1 and fails it.
     Taupe as visible readable text also contradicts the brief's own rule
     that Taupe is decoration only, never text. Stone is the semantically
     correct token here regardless (captions, labels, muted text) and clears
     AA on both surfaces this list appears on: 5.12:1 on Paper, 4.55:1 on Oat. */
  color: var(--stone); font-variant-numeric: tabular-nums;
}
.steps h3 { margin-bottom: .4rem; }
.steps p { color: var(--ink-90); }

/* Price list — the donation ladder. */
.ladder { list-style: none; margin: 0; padding: 0; }
.ladder li {
  display: grid;
  grid-template-columns: 4.25rem 1fr;
  gap: 1rem;
  align-items: baseline;
  padding: 1rem 0;
  border-top: 1px solid var(--sand);
}
.ladder li:last-child { border-bottom: 1px solid var(--sand); }
.ladder b {
  font-family: var(--display); font-size: var(--t-lg);
  color: var(--ink); font-variant-numeric: tabular-nums;
}
.ladder span { color: var(--ink-90); }

/* Definition rows — facts, plainly. */
.facts { margin: 0; display: grid; gap: 0; }
.facts > div {
  display: grid; gap: .2rem 1.5rem;
  padding: .95rem 0;
  border-top: 1px solid var(--sand);
}
.facts > div:last-child { border-bottom: 1px solid var(--sand); }
@media (min-width: 620px) { .facts > div { grid-template-columns: 12rem 1fr; } }
.facts dt { font-size: var(--t-sm); color: var(--stone); }
.facts dd { margin: 0; color: var(--ink); }

/* An outstanding item Lisa still has to supply. Visible on purpose. */
.todo {
  display: flex; gap: .7rem; align-items: flex-start;
  padding: .9rem 1.1rem;
  background: var(--paper);
  border: 1px dashed var(--taupe);
  border-radius: var(--r);
  font-size: var(--t-sm);
  color: var(--stone);
}
.todo b { color: var(--bark); font-weight: 600; }

/* ------------------------------------------------------------- 11. Forms - */

.form { display: grid; gap: 1.15rem; }
.field { display: grid; gap: .4rem; }
.field label { font-size: var(--t-sm); font-weight: 600; color: var(--ink); }
.field .hint { font-size: var(--t-xs); color: var(--stone); }
.field input, .field select, .field textarea {
  width: 100%;
  padding: .8rem .95rem;
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: var(--r);
  color: var(--ink);
  transition: border-color var(--fast) var(--ease);
}
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--bark);
}
.field textarea { min-height: 8rem; resize: vertical; }
.req { color: var(--bark); }

.form-note {
  font-size: var(--t-sm);
  color: var(--stone);
  padding: .85rem 0 0;
  border-top: 1px solid var(--sand);
}

/* ------------------------------------------------------ Donation form --- */
/*  Amount, frequency and cause, asked once. Bark stays the only fill on the
    page: the chips are outlined until selected, and the selected state is
    Ink, so the single Bark button is still the loudest thing here.        */

.donate__group { border: 0; margin: 0 0 1.6rem; padding: 0; }
.donate__group legend {
  padding: 0;
  margin-bottom: .7rem;
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--stone);
}

.chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.chips--wide .chip { flex: 1 1 9rem; }

.chip { position: relative; display: block; }
/* The input stays in the accessibility tree and keeps keyboard behaviour;
   only its rendering is replaced. Not display:none, which would remove it. */
.chip input {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: pointer;
}
.chip span {
  display: block;
  padding: .7rem 1.15rem;
  text-align: center;
  /* 44px minimum touch target, per the brief. */
  min-height: 2.75rem;
  line-height: 1.4;
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: 100px;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--ink);
  transition: border-color var(--fast) var(--ease),
              background var(--fast) var(--ease),
              color var(--fast) var(--ease);
}
.chip input:hover + span { border-color: var(--taupe); }
.chip input:checked + span {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.chip input:focus-visible + span {
  outline: 2px solid var(--bark);
  outline-offset: 3px;
}

/* Amount field, with the sign inside the box rather than floating beside it. */
.donate__custom { margin-top: .9rem; }
.money { position: relative; display: block; }
.money__sign {
  position: absolute;
  left: .95rem; top: 50%; transform: translateY(-50%);
  font-weight: 600; color: var(--stone);
  pointer-events: none;
}
.money input { padding-left: 2.1rem; }

.donate__what {
  margin-top: .8rem;
  min-height: 1.4em;   /* reserved, so the layout does not jump on change */
}

.donate select { width: 100%; }
.donate .btn-row { margin-top: 1.75rem; }
.donate .form-status { margin-top: .8rem; }
.donate .form-status .link-arrow { margin-left: .15rem; }
.donate button[disabled] { opacity: .6; cursor: progress; }
.form-status { font-size: var(--t-sm); min-height: 1.4em; }
.form-status.is-err { color: #8A2B22; }
.form-status.is-ok  { color: #2C5A3C; }

/* ------------------------------------------------------------- Promo ---- */
/*  A dismissible announcement. Shown once per visitor, on the home page only.
    <dialog> supplies the focus trap, Esc-to-close and inert background.     */

.promo {
  width: min(30rem, calc(100vw - 2.5rem));
  padding: clamp(1.75rem, 4vw, 2.5rem);
  background: var(--paper);
  border: 1px solid var(--sand);
  border-radius: var(--r-xl);
  color: var(--ink);
  /* The one place a shadow is doing real work: it is what lifts the dialog
     off the page behind it. Not decoration. */
  box-shadow: 0 30px 70px -30px rgba(31,36,34,.5);
}
.promo::backdrop {
  background: rgba(31,36,34,.45);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
/* A dialog heading sits below a page heading, not level with it. At --t-2xl
   this rendered at 44px inside a 480px panel, larger than every section
   heading on the page behind it. */
.promo h2 {
  font-size: clamp(1.5rem, 1.2vw + 1.1rem, 2rem);
  line-height: 1.16;
  letter-spacing: -.022em;
  margin-bottom: .9rem;
  max-width: 16ch;
}
.promo p { color: var(--ink-90); max-width: 38ch; }
.promo .eyebrow { margin-bottom: .9rem; }
.promo .btn-row { margin-top: 1.75rem; }

.promo__dismiss { display: flex; justify-content: flex-end; margin-bottom: -.5rem; }
.promo__x {
  display: inline-flex; align-items: center; justify-content: center;
  /* 44px target, per the brief, without a 44px-looking button. */
  width: 2.75rem; height: 2.75rem;
  margin: -.75rem -.75rem 0 0;
  background: transparent; border: 0; border-radius: 100px;
  color: var(--stone); cursor: pointer;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.promo__x:hover { background: var(--oat); color: var(--ink); }

/* Opening move. Skipped entirely for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .promo[open] { animation: promo-in .42s var(--ease) both; }
  .promo[open]::backdrop { animation: promo-fade .42s var(--ease) both; }
}
@keyframes promo-in {
  from { opacity: 0; transform: translateY(14px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
@keyframes promo-fade { from { opacity: 0 } to { opacity: 1 } }

/* ------------------------------------------------------------ 12. Footer - */

.footer { background: var(--ink); color: rgba(244,237,225,.8); padding-block: clamp(3rem,6vw,4.5rem) 2rem; }
.footer a { color: var(--oat); text-decoration: none; }
.footer a:hover { text-decoration: underline; }
.footer__grid { display: grid; gap: 2.5rem; }
@media (min-width: 800px) { .footer__grid { grid-template-columns: 1.4fr 1fr 1fr; } }
.footer h2 {
  font-family: var(--sans); font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase;
  color: rgba(244,237,225,.6); margin-bottom: 1rem;
}
.footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.footer .brand { color: var(--paper); font-size: 1.4rem; }
.footer__legal {
  margin-top: 3rem; padding-top: 1.5rem;
  border-top: 1px solid rgba(244,237,225,.15);
  font-size: var(--t-xs); color: rgba(244,237,225,.6);
  display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; justify-content: space-between;
}

/* --------------------------------------------------------------- Motion - */

/* The .reveal / .is-in pair that lived here was the second of two scroll
   systems animating the same elements. It is gone; .rise below is the one
   system. */

/* ------------------------------------------------ Reserved photo space - */
/*  An image Lisa has approved but not yet sent. Sized to its final aspect
    ratio so nothing shifts when the real photograph replaces it.          */

.photo-slot {
  margin: 0;
  /* A portrait is a supporting element, not a hero. Cap it so it can never
     swallow the column it sits in. */
  max-width: 320px;
}
.photo-slot__box {
  aspect-ratio: var(--ar, 4 / 5);
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: var(--paper);
  border: 1px dashed var(--taupe);
  border-radius: var(--r-lg);
  text-align: center;
}
.photo-slot__box span {
  max-width: 26ch;
  font-size: var(--t-sm);
  color: var(--stone);
}
.photo-slot figcaption {
  margin-top: .7rem;
  font-size: var(--t-xs);
  color: var(--stone);
}

/* ----------------------------------------------------------- Figure ---- */
/*  A photograph sitting in a column beside text. Every one of these used to
    be a bare <div><img></div>: no caption, and whatever height the source
    file happened to have, so no two pages framed an image the same way.
    One ratio, one radius, one caption style, everywhere.                   */

.figure { margin: 0; }
.figure img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--r-lg);
}
.figure figcaption {
  margin-top: .85rem;
  padding-top: .8rem;
  border-top: 1px solid var(--sand);
  font-size: var(--t-xs);
  line-height: 1.5;
  color: var(--stone);
  max-width: 46ch;
}

/* --------------------------------------------------------- Portrait ---- */
.portrait { margin: 0; max-width: 320px; }
.portrait img, .portrait video { width: 100%; display: block; border-radius: var(--r-lg); }
.portrait figcaption {
  margin-top: .7rem;
  font-size: var(--t-xs);
  color: var(--stone);
}

/* ======================================================================== */
/* 13. Presence                                                             */
/* ------------------------------------------------------------------------ */
/*  The site read as flat because every section carried the same weight and  */
/*  no image ever escaped its column. This section fixes that WITHOUT        */
/*  breaking Lisa's rules: no new colours, no heavy boxes, Bark still the    */
/*  only button. The drama comes from scale, bleed, texture and motion.      */
/* ======================================================================== */

/* --- Paper grain. Warm palettes look dead perfectly flat. --------------- */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: .28;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='4'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.22'/%3E%3C/svg%3E");
}
body > * { position: relative; z-index: 1; }

/* --- Hero: the headline gets room to be the loudest thing on the page. -- */
.hero--lead { padding-block: clamp(4.5rem, 11vw, 9rem) clamp(3rem, 6vw, 5rem); }
/* Sized against the WRAP, not the viewport, and capped by measure so it
   always lands on two or three lines with a decent rag. */
.hero__title { margin-bottom: clamp(2.25rem, 4.5vw, 3.5rem); }

.hero__row { display: grid; gap: clamp(2rem, 4vw, 4rem); align-items: start; }
@media (min-width: 900px) {
  .hero__row { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); }
}
.hero__row .lede { margin-top: 0; max-width: 46ch; }
.hero__act .btn-row { margin-top: 0; }
.hero--lead .lede { font-size: clamp(1.0625rem, .5vw + .95rem, 1.25rem); }

/* --- Full-bleed image band. The single biggest change: let a photograph
       run the whole width and breathe. ---------------------------------- */
.bleed {
  position: relative;
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  overflow: hidden;
}
.bleed img {
  width: 100%;
  height: clamp(300px, 52vh, 620px);
  object-fit: cover;
  object-position: center 38%;
}
/* A whisper of Ink at the edges so the band sits in the page rather than
   floating on top of it. */
.bleed::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(31,36,34,.16) 0%,
    rgba(31,36,34,0) 22%,
    rgba(31,36,34,0) 74%,
    rgba(31,36,34,.20) 100%);
}
.bleed figcaption {
  margin-top: .8rem;
  padding-inline: var(--gutter);
  font-size: var(--t-xs);
  color: var(--stone);
  max-width: var(--wrap);
  margin-inline: auto;
}

/* --- A statement. One per page, at most. -------------------------------- */
/* Left-aligned, like every other block on the site. Centring this one section
   was the thing that made the page read as a blog post: it broke the single
   left edge that everything else shares. */
.statement { padding-block: clamp(4rem, 9vw, 8rem); }
.statement p {
  font-family: var(--display);
  font-size: clamp(1.6rem, 3.6vw, 3.4rem);
  line-height: 1.18;
  letter-spacing: -.018em;
  color: var(--ink);
  max-width: 24ch;
  text-wrap: balance;
}
/* A short Taupe rule turns this from a stray caption into an attribution.
   The measure is set on the footer's own text, not inherited from the 24ch
   display line above it, which was breaking a 33-character label over two. */
.statement footer {
  margin-top: 1.75rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--taupe);
  max-width: 36ch;
  font-size: var(--t-sm);
  color: var(--stone);
}

/* Eight columns of display line, four of attribution, so the row closes on
   the same right edge as everything else. On its own the statement stopped
   around 769px against a 1268px edge. */
@media (min-width: 900px) {
  .statement > .wrap {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 0 24px;
    align-items: end;
  }
  .statement > .wrap > p      { grid-column: 1 / span 8; max-width: none; }
  .statement > .wrap > footer { grid-column: 9 / span 4; margin-top: 0; max-width: none; }

  /* Where the second column would otherwise sit mostly empty above a short
     footer label (e.g. "Our mission"), the mark fills it instead of leaving
     dead space. The plain footer case above is untouched. */
  .statement > .wrap > .statement__aside {
    grid-column: 9 / span 4;
    align-self: stretch;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }
  .statement > .wrap > .statement__aside > footer { margin-top: 0; max-width: none; }
}
.statement__mark .brand { font-size: 1.7rem; }

/* --- Big numerals for the journey. Taupe, so decoration only. ----------- */
.index-list { list-style: none; margin: 0; padding: 0; }
.index-list > li {
  display: grid;
  gap: .4rem 2rem;
  padding-block: clamp(1.75rem, 3.5vw, 2.75rem);
  border-top: 1px solid var(--sand);
}
@media (min-width: 800px) {
  .index-list > li { grid-template-columns: 6rem 1fr; }
}
.index-list > li:last-child { border-bottom: 1px solid var(--sand); }
.index-list .n {
  font-family: var(--display);
  font-size: clamp(2.25rem, 4vw, 3.25rem);
  line-height: .9;
  color: var(--taupe);
  font-variant-numeric: tabular-nums;
}
.index-list h3 { margin-bottom: .6rem; }

/* --- Motion. Quiet, and off entirely for anyone who asked. --------------
   Went to 34px + scale(.98) to fix the previous 22px version reading as
   barely-there on a big monitor — but scale on text triggers sub-pixel
   font rasterisation mid-transition, which is what actually read as rough,
   not the distance. Pure translateY at a size between the two extremes,
   no scale on anything with text in it. */
.rise {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
  transition-delay: calc(var(--i, 0) * 90ms);
}
.rise.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .rise { opacity: 1; transform: none; transition: none; }
}

/* ======================================================================== */
/* 14. Video hero and gradient headings                                     */
/* ------------------------------------------------------------------------ */
/*  Both stay inside Lisa's palette: the heading gradient runs Ink -> Bark   */
/*  (13.5:1 down to 8.8:1 on Oat, so it clears AA even at body size), and    */
/*  the video sits under an Oat scrim so text contrast never depends on      */
/*  which frame is showing.                                                  */
/* ======================================================================== */

/* --- Scroll motion, extended. One idea at a time, ease-out, 700ms. ------ */
.rise { will-change: opacity, transform; }
.rise.in { will-change: auto; }

/* Bleed images drift very slightly as they enter — a hint of depth, not
   parallax, which the guidance flags for motion sickness. */
.bleed img {
  transform: scale(1.04);
  transition: transform 1.4s var(--ease);
}
.bleed.in img { transform: scale(1); }

@media (prefers-reduced-motion: reduce) {
  .bleed img { transform: none; transition: none; }
}

/* ----------------------------------------------------------- Give box - */
.give__lead {
  font-family: var(--display);
  font-size: var(--t-lg);
  line-height: 1.4;
  color: var(--ink);
  max-width: 34ch;
  margin-bottom: 1.75rem;
}
.give__note { margin-top: 1.1rem; max-width: 42ch; }

/* ------------------------------------------------------------- Nav CTA - */
.nav__cta {
  padding: .5rem 1.05rem;
  border: 1px solid var(--ink);
  border-radius: 100px;
  background: var(--ink);
  color: var(--paper) !important;
  font-weight: 600;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.nav__cta:hover { background: var(--bark); border-color: var(--bark); }
.nav a.nav__cta { border-bottom: 1px solid transparent; }

/* ======================================================================== */
/* 15. Soft panel hero + quick stats                                        */
/* ------------------------------------------------------------------------ */
/*  Rounded, warm, and full of content — the brief was "bright, warm,        */
/*  friendly" and "it feels empty". Tints only, no new brand colours.        */
/* ======================================================================== */

.hero-panel {
  padding-block: clamp(1.5rem, 3vw, 2.5rem) clamp(2.5rem, 5vw, 4rem);
  /* Its own stacking context, so the video can sit at z-index -1 behind the
     hero content without falling behind the page background. */
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

/* --- Hero video ---------------------------------------------------------- */
/* Read as texture, not as a second photograph. A heavy Oat scrim sits over it
   so body text keeps its full 13.5:1 against Oat whatever frame is showing:
   contrast can never depend on which moment of the video is on screen, which
   is the usual way a video hero fails accessibility. */
.hero__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--oat) center / cover no-repeat;
}
.hero__bg video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.hero__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Vertical, and fully opaque before the halfway mark. This is not a taste
     call. Stone is 4.55:1 on Oat, barely over the 4.5:1 floor, so ANY video
     showing through behind a Stone label drops it below AA. The motion is
     therefore confined to the top of the hero, where the only text is the
     Bark badge and the Ink headline, both of which stay above 10:1 even
     against a pure black frame. Everything from the lede down sits on solid
     Oat and is unaffected. */
  background: linear-gradient(to bottom,
    rgba(244,237,225,.90) 0%,
    rgba(244,237,225,.94) 26%,
    rgba(244,237,225,1)   46%,
    rgba(244,237,225,1)   100%);
}

/* No video for anyone who asked for less motion. main.js also removes the
   element outright; this covers the moment before the script runs. */
@media (prefers-reduced-motion: reduce) {
  .bg-video { display: none; }
}

.panel {
  display: grid;
  gap: clamp(1.75rem, 3vw, 3rem);
  align-items: center;
  /* Bleed to the wrap edge, then re-pad by the gutter, so the text inside
     starts on the same grid line as every other section on the page. */
  margin-inline: calc(-1 * var(--gutter));
  /* No panel, matching every other hero. The photograph and the fact rows
     give this block its shape; it does not need a box drawn round it. */
  padding: clamp(2rem, 4vw, 3.25rem) var(--gutter);
}
@media (min-width: 900px) {
  .panel { grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); }
}

/* The second column .panel was built for. Capped narrow on mobile so a
   portrait photo doesn't dominate above the fold; free to fill its column
   once the grid actually has two of them, from 900px up. */
.panel__photo { margin: 0; justify-self: center; }
.panel__photo img {
  width: 100%; max-width: 280px; border-radius: var(--r-lg); display: block;
}
.panel__photo figcaption {
  margin-top: .6rem; font-size: var(--t-xs); color: var(--stone); text-align: center;
}
@media (min-width: 900px) {
  .panel__photo img { max-width: 100%; }
}

/* Sized for the PANEL column, not the viewport — at the global h1 size this
   broke to six lines of three words. */
.panel h1 {
  font-size: clamp(2rem, 3.6vw, 3.25rem);   /* same as every other page */
  line-height: 1.1;
  letter-spacing: -.03em;
  margin-bottom: 1.15rem;
  max-width: 16ch;
}
.panel .lede { margin-bottom: 1.9rem; color: var(--ink-90); }

/* The accent word, drawn with a soft highlight rather than a colour change —
   keeps within the palette while giving the headline a lift. */
/* The accent phrase: the gradient is painted INTO the letterforms, the way
   the reference site fills "Break Free". One phrase per heading, never the
   whole heading, so the rest of the line stays flat Ink.

   The palette is far narrower than a purple-to-pink, so the range here is
   Ink -> Bark -> Bark warmed toward Taupe: a deep green-black opening into
   warm brown. Taupe itself is not a legal stop, on contrast and on the
   brief's decoration-only rule. See --bark-warm. */
.hl {
  display: inline;
  /* The gradient runs Ink to Bark to Bark-warm and back. Every stop clears AA
     on Oat (13.5:1, 8.8:1, 5.1:1), so the phrase stays readable at any point
     in the sweep. It travels rather than sitting still: 260% of the text width
     moved slowly, which reads as light passing over the letters. Used to also
     carry a blurred glow behind the phrase (a ::before bloom); dropped since
     it read as background decoration rather than the letters themselves —
     this is text-only motion now. */
  background-image: linear-gradient(105deg,
    var(--ink)       0%,
    var(--bark)      26%,
    var(--bark-warm) 50%,
    var(--bark)      74%,
    var(--ink)       100%);
  background-size: 260% 100%;
  background-position: 0% 0;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: hl-flow 11s var(--ease) infinite;
}

@keyframes hl-flow {
  0%, 100% { background-position:   0% 0; }
  50%      { background-position: 100% 0; }
}

/* Held still for anyone who asked for less motion, not removed. */
@media (prefers-reduced-motion: reduce) {
  .hl { animation: none; background-position: 30% 0; }
}

/* Without background-clip a clipped gradient fails to INVISIBLE text, so fall
   back to solid Ink. */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .hl {
    background-image: none;
    color: var(--ink);
    -webkit-text-fill-color: var(--ink);
  }
}

/* Where background-clip:text is unsupported, transparent text would render
   as nothing at all. Fall back to solid Ink. */
@supports not ((background-clip: text) or (-webkit-background-clip: text)) {
  .hl {
    background-image: none;
    color: var(--ink);
    -webkit-text-fill-color: var(--ink);
  }
}

/* Anyone who has asked for more contrast gets the flat Ink instead. */
@media (prefers-contrast: more) {
  .hl {
    background-image: none;
    color: var(--ink);
    -webkit-text-fill-color: var(--ink);
  }
}

/* --- Supporter strip ----------------------------------------------------- */
/* Businesses giving a share of their sales. Logos arrive at wildly different
   sizes and aspect ratios, so each sits in a fixed-height box and is contained
   rather than cropped. A supporter with no logo yet gets their name set in the
   display face, which keeps the row even instead of leaving a gap. */
.supporters {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}
.supporters li { border-top: 1px solid var(--sand); }
.supporters li:last-child { border-bottom: 1px solid var(--sand); }
@media (min-width: 700px) {
  .supporters li + li { border-left: 1px solid var(--sand); }
  .supporters li:last-child { border-bottom: 0; }
}
.supporters a,
.supporters li > b {
  display: flex;
  flex-direction: column;
  gap: .45rem;
  padding: 1.6rem 1.5rem 1.6rem 0;
  text-decoration: none;
  color: var(--ink);
  min-height: 44px;
}
@media (min-width: 700px) {
  .supporters li + li a,
  .supporters li + li > b { padding-left: 1.5rem; }
}
.supporters img {
  height: 2.5rem;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  object-position: left center;
}
.supporters b {
  font-family: var(--display);
  font-size: var(--t-lg);
  font-weight: 800;
  letter-spacing: -.02em;
}
.supporters span { font-size: var(--t-sm); color: var(--stone); }
.supporters a:hover b { color: var(--bark); }

/* Hero trust line. Quiet by design: it is there to be found by someone already
   wondering whether this is a real organisation, not to shout. Sits under the
   actions, separated by a hairline rather than boxed. */
.trust {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem 1.75rem;
  margin-top: 2rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--sand);
  font-size: var(--t-sm);
  color: var(--stone);
  max-width: none;
}
.trust b { color: var(--ink); font-weight: 600; }

.badge {
  display: inline-block;
  margin-bottom: 1.35rem;
  padding: .4rem .9rem;
  background: var(--tint-paper);
  border-radius: 100px;
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--bark);
}

.panel__media img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--r-lg);
}

/* The stat panel under a hero photo (home, Emma Taylor). See stats() in
   build.py.

   History: this started as four bordered cards, which read as dashboard
   tiles; then went fully flat — text split by hairlines — which fixed that
   but left four small facts reading as small print under a big photo.

   Now ONE panel, not four: a single Paper surface with the cells divided by
   1px of Sand showing through a 1px gap, so it reads as one object rather
   than a row of tiles. Each figure gets an icon badge in the same sage as
   the tick lists, so the site has one "confirmed fact" language, not two. */
.quickstats {
  list-style: none;
  margin: clamp(2rem, 4vw, 3rem) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  background: var(--sand);           /* shows through the gaps as hairlines */
  border: 1px solid var(--sand);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(31,36,34,.04), 0 22px 44px -30px rgba(31,36,34,.38);
}
/* Four across only once there's room for it. At tablet widths a quarter-
   width cell wrapped Emma's "leading Oxbridge applications" to three lines
   and knocked that cell out of line with its neighbours; 2x2 until then. */
@media (min-width: 1000px) { .quickstats { grid-template-columns: repeat(4, 1fr); } }

.quickstats li {
  max-width: none;
  background: var(--paper);
  padding: clamp(1.1rem, 2vw, 1.5rem);
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas: "icon value" "icon label";
  column-gap: .95rem;
  align-items: center;
  transition: background var(--fast) var(--ease);
}
/* Pointer devices only: on touch screens :hover sticks after a tap and
   leaves one cell looking permanently selected. */
@media (hover: hover) {
  .quickstats li:hover { background: #FBF7EF; }
  .quickstats li:hover .qs__icon { background: #5F7A54; color: var(--paper); }
}

/* Stacked on narrow screens: a side-by-side icon left too little width for
   "Subsidised" in a half-width cell. */
@media (max-width: 759px) {
  .quickstats li {
    grid-template-columns: 1fr;
    grid-template-areas: "icon" "value" "label";
    row-gap: .15rem;
  }
  .quickstats .qs__icon { margin-bottom: .6rem; }
}

.qs__icon {
  grid-area: icon;
  width: 2.75rem; height: 2.75rem;
  border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(95,122,84,.12);
  color: #5F7A54;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.qs__icon svg { width: 1.3rem; height: 1.3rem; }

/* Weight comes from the typography, not the number — the org is small and
   new, so no figure here will ever impress on its own. Tabular figures so
   the row reads as a set. Sized to fit "Subsidised" beside a badge in a
   quarter-width cell without wrapping. */
.qs__value {
  grid-area: value;
  align-self: end;
  font-family: var(--display);
  font-size: clamp(1.4rem, 1.9vw, 1.95rem);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.qs__label {
  grid-area: label;
  align-self: start;
  margin-top: .3rem;
  font-size: var(--t-sm);
  line-height: 1.35;
  color: var(--stone);
}

/* Cards get the friendlier shape and a soft fill. */
.card {
  border-radius: var(--r-lg);
  background: var(--tint-paper);
}
.btn { border-radius: 100px; padding: .95rem 1.6rem; }
.tag, .nav__cta { border-radius: 100px; }
/* .figure is not in this list: it is now a figure element wrapping an image
   plus a caption, and the radius belongs on the image. See Figure above. */
.bleed img, .portrait img, .hero__media { border-radius: var(--r-lg); }

/* ======================================================================== */
/* 16. Page spine                                                           */
/* ------------------------------------------------------------------------ */
/*  hero -> facts -> content -> next step. The same shape on every page.     */
/* ======================================================================== */

/* The closing block. Two columns on desktop so the action sits beside the
   invitation rather than under it. */
.next { display: grid; gap: clamp(1.5rem, 3vw, 3rem); align-items: center; }
@media (min-width: 860px) {
  .next { grid-template-columns: minmax(0, 1.3fr) auto; }
}
.next h2 { margin-bottom: .85rem; }
.next .lede { margin: 0; color: rgba(253,251,247,.86); }
.next .btn-row { flex-wrap: nowrap; }
@media (max-width: 520px) { .next .btn-row { flex-wrap: wrap; } }

/* Buttons on the Ink band. */
.band .btn--primary { background: var(--paper); color: var(--ink); }
.band .btn--primary:hover { background: var(--oat); }
.btn--onDark {
  background: transparent;
  color: var(--paper);
  border: 1px solid rgba(253,251,247,.4);
}
.btn--onDark:hover { border-color: var(--paper); background: rgba(253,251,247,.08); }

/* The band gets the friendly shape too, inset from the page edge. */
.section.band {
  border-radius: var(--r-xl);
  padding-block: clamp(2.75rem, 5vw, 4.25rem);
  max-width: var(--wrap);
  margin-inline: auto;
}
/* The closing band sat flush against a footer of the identical Ink, so its
   rounded bottom corners cut two small Oat notches into the seam and the
   inset band appeared to bleed into a full-width block. Give it air, and the
   band reads as a card with the footer as a separate surface beneath it. */
main > .band:last-child { margin-bottom: clamp(2.5rem, 5vw, 4.5rem); }

/* Cards carry a little more presence now they do more work. */
.card {
  border: 1px solid var(--sand);
  transition: transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
a.card:hover, .card:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 30px -18px rgba(31,36,34,.45);
}
@media (prefers-reduced-motion: reduce) {
  a.card:hover, .card:hover { transform: none; }
}

/* ------------------------------------------------------------- Notice --- */
/* Genuinely-not-yet-known information, said plainly rather than hidden. */
.notice {
  display: grid;
  gap: .5rem;
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  background: var(--tint-paper);
  border: 1px solid var(--sand);
  border-radius: var(--r-lg);
  max-width: 40rem;
}
.notice b { font-family: var(--display); font-size: var(--t-lg); color: var(--ink); }
.notice span { font-size: var(--t-sm); color: var(--ink-90); max-width: 52ch; }
.notice .link-arrow { margin-top: .35rem; justify-self: start; }

/* --------------------------------------------------- Heading rhythm ----- */
/* Headings were inheriting section padding inconsistently, so the space
   above a heading varied from block to block. One rule, everywhere. */
h1 + *, h2 + * { margin-top: 1.15rem; }
h3 + * { margin-top: .65rem; }
h1 + .btn-row, h2 + .btn-row { margin-top: 1.75rem; }
.sec-head { margin-bottom: clamp(1.75rem, 3.5vw, 2.75rem); }
.sec-head h2 { margin-bottom: 0; }
.eyebrow + h1, .eyebrow + h2 { margin-top: 0; }
.badge + h1 { margin-top: 0; }
/* A heading never sits tight against the block above it. */
.section > .wrap > h2, .section > .wrap > .sec-head { margin-top: 0; }
