/* ===========================================================================
   S.N.A.P. Academy — public design system
   Warm, calm and high-contrast. Built for parents and grandparents on phones:
   large type, generous targets, no motion unless it explains something.
   =========================================================================== */

:root {
  /* Brand — taken from the S.N.A.P. Academy logo */
  --navy-900: #081A3D;
  --navy-800: #0C2350;
  --navy: #10305F;
  --navy-600: #1B4487;
  --blue: #1D6FD0;
  --blue-dark: #14559F;
  --blue-soft: #E8F1FC;
  --red: #D23A2C;
  --red-soft: #FDECEA;
  --gold: #F0A31A;
  --gold-soft: #FDF3DF;
  --green: #2E8F55;
  --green-soft: #E7F4EC;

  /* Neutrals */
  --ink: #16203A;
  --ink-soft: #38415A;
  --muted: #5D667C;
  --line: #E3DDD1;
  --line-soft: #EFEAE1;
  --cream: #FBF7F0;
  --cream-deep: #F5EFE4;
  --white: #FFFFFF;

  --radius-sm: 8px;
  --radius: 14px;
  --radius-lg: 22px;
  --radius-pill: 999px;

  --shadow-sm: 0 1px 2px rgba(16, 48, 95, .06), 0 2px 6px rgba(16, 48, 95, .05);
  --shadow: 0 2px 4px rgba(16, 48, 95, .05), 0 10px 26px rgba(16, 48, 95, .08);
  --shadow-lg: 0 4px 10px rgba(16, 48, 95, .06), 0 24px 56px rgba(16, 48, 95, .12);

  --font-display: 'Fraunces', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
  --font-body: 'Public Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  --wrap: 1160px;
  --wrap-narrow: 760px;
  --gap: clamp(1rem, 2.5vw, 1.75rem);
  --section-y: clamp(3.25rem, 7vw, 6rem);

  /* One spacing scale, so every gap in the site is a multiple of the same unit
     instead of a value someone picked by eye. */
  --s-1: .25rem;  --s-2: .5rem;   --s-3: .75rem;  --s-4: 1rem;
  --s-5: 1.5rem;  --s-6: 2rem;    --s-7: 3rem;    --s-8: 4rem;

  /* One shape for every photograph on the site: cards, the picture beside a
     block of text, and the banner at the top of a page. Sharing a single ratio
     is what makes rows of cards line up — and because the pictures themselves
     are supplied at exactly this shape, nothing is ever cropped to achieve it. */
  --ratio-card: 16 / 9;
  --ratio-wide: 16 / 9;
  --ratio-portrait: 4 / 5;

  --ease: cubic-bezier(.22, .61, .36, 1);
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--ink);
  background: var(--cream);
  -webkit-font-smoothing: antialiased;
}

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

/* Headings are set in Title Case — the first letter of each word capitalised,
   the rest lower case.
   `text-transform` does the work rather than the words being retyped, so the
   admin panel keeps ordinary sentences, screen readers announce ordinary words,
   and an owner who types "programs running now" still gets a proper heading.
   Mixed case needs slightly negative tracking, the opposite of capitals: the
   letterforms were spaced for this and open up if they are pushed apart. */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.1;
  color: var(--navy-900);
  margin: 0 0 .5em;
  text-transform: capitalize;
  letter-spacing: -.014em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.2rem, 5.6vw, 3.9rem); line-height: 1.03; letter-spacing: -.022em; }
h2 { font-size: clamp(1.75rem, 3.9vw, 2.6rem); letter-spacing: -.018em; }
h3 { font-size: clamp(1.2rem, 2.1vw, 1.45rem); letter-spacing: -.01em; line-height: 1.25; }
h4 { font-size: 1.05rem; letter-spacing: -.005em; line-height: 1.3; }

/* Body copy stays in sentence case: capitals are measurably slower to read and
   are a known barrier for dyslexic readers, and this site asks parents to read
   fees, dates and policies carefully. */
p, li, dd, dt, .lede, blockquote, figcaption, label, input, textarea, select {
  text-transform: none;
}
p { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

a { color: var(--blue-dark); text-underline-offset: .18em; text-decoration-thickness: .07em; }
a:hover { color: var(--navy); }

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

hr { border: 0; border-top: 1px solid var(--line); margin: 2rem 0; }

/* ------------------------------------------------------------- utilities -- */
.wrap { width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; }
.wrap-narrow { width: min(100% - 2.5rem, var(--wrap-narrow)); margin-inline: auto; }
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 1rem; top: -100%; z-index: 200;
  background: var(--navy); color: #fff; padding: .8rem 1.25rem;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm); font-weight: 600;
}
.skip-link:focus { top: 0; color: #fff; }
.eyebrow {
  font-size: .8rem; letter-spacing: .14em; text-transform: uppercase;
  font-weight: 700; color: var(--blue-dark); margin: 0 0 .65rem;
}
.lede { font-size: clamp(1.1rem, 2vw, 1.28rem); color: var(--ink-soft); line-height: 1.6; }
.text-center { text-align: center; }
.muted { color: var(--muted); }
.small { font-size: .92rem; }
.stack > * + * { margin-top: 1rem; }

/* --------------------------------------------------------------- buttons -- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  font-family: var(--font-body); font-weight: 700; font-size: .95rem;
  text-transform: uppercase; letter-spacing: .07em;
  padding: .9rem 1.6rem; min-height: 52px;
  border-radius: var(--radius-pill); border: 2px solid transparent;
  text-decoration: none; cursor: pointer; text-align: center;
  transition: background-color .18s var(--ease), border-color .18s var(--ease),
              color .18s var(--ease), transform .14s var(--ease), box-shadow .18s var(--ease);
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(1px); transition-duration: .05s; }
.btn-primary:hover { box-shadow: 0 6px 18px rgba(29, 111, 208, .32); }
.btn-navy:hover { box-shadow: 0 6px 18px rgba(8, 26, 61, .3); }
.btn-gold:hover { box-shadow: 0 6px 18px rgba(240, 163, 26, .38); }
.btn-primary { background: var(--blue); color: #fff; border-color: var(--blue); }
.btn-primary:hover { background: var(--blue-dark); border-color: var(--blue-dark); color: #fff; }
.btn-navy { background: var(--navy); color: #fff; border-color: var(--navy); }
.btn-navy:hover { background: var(--navy-900); color: #fff; }
.btn-gold { background: var(--gold); color: var(--navy-900); border-color: var(--gold); }
.btn-gold:hover { background: #dc9310; color: var(--navy-900); }
.btn-outline { background: transparent; color: var(--navy); border-color: rgba(16,48,95,.28); }
.btn-outline:hover { border-color: var(--navy); background: rgba(16,48,95,.05); color: var(--navy); }
.btn-on-dark { background: rgba(255,255,255,.12); color: #fff; border-color: rgba(255,255,255,.5); }
.btn-on-dark:hover { background: #fff; color: var(--navy-900); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--blue-dark); padding-inline: .5rem; min-height: 44px; }
.btn-ghost:hover { text-decoration: underline; }
.btn-sm { min-height: 42px; padding: .5rem 1rem; font-size: .95rem; }
.btn-block { width: 100%; }
.btn-row { display: flex; flex-wrap: wrap; gap: .8rem; }
.btn[aria-disabled="true"], .btn:disabled { opacity: .55; pointer-events: none; }

/* ---------------------------------------------------------------- header -- */
.site-header {
  background: var(--white);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 60;
}
.site-header .header-inner {
  display: flex; align-items: center; gap: 1rem; padding: .7rem 0; min-width: 0;
  /* Wider than the text container. A line of body copy should stay readable at
     about 1160px, but a menu bar has no such limit and needs the room. */
  width: min(100% - 2.5rem, 1320px); margin-inline: auto;
}
/* `min-width: 0` on both flexible children is what stops a long school name or a
   wide button from pushing the row past the edge of a phone screen — flex items
   refuse to shrink below their content width without it. */
.brand { display: flex; align-items: center; gap: .7rem; text-decoration: none; min-width: 0; flex: 0 1 auto; }
/* The whole logo, never a crop. It is a tall lockup, so it is given a little
   more height than a wordmark would need in order to stay legible. */
.brand img { height: 60px; width: auto; flex: 0 0 auto; }
.brand-text { display: flex; flex-direction: column; line-height: 1.1; min-width: 0; }
.brand-name {
  font-family: var(--font-display); font-weight: 700; font-size: 1.22rem;
  color: var(--navy-900); letter-spacing: -.01em;
}
.brand-tag { font-size: .72rem; color: var(--muted); letter-spacing: .02em; }

.nav-toggle {
  margin-left: auto; display: inline-flex; align-items: center; gap: .5rem;
  background: var(--cream); border: 1px solid var(--line); border-radius: var(--radius-pill);
  padding: .6rem 1rem; font: inherit; font-weight: 650; min-height: 48px; cursor: pointer; color: var(--navy);
}
.nav-toggle .bars { display: block; width: 20px; height: 2px; background: currentColor; position: relative; }
.nav-toggle .bars::before, .nav-toggle .bars::after { content: ''; position: absolute; left: 0; width: 20px; height: 2px; background: currentColor; }
.nav-toggle .bars::before { top: -6px; } .nav-toggle .bars::after { top: 6px; }

.primary-nav ul { list-style: none; margin: 0; padding: 0; }
.primary-nav > ul { display: flex; gap: .15rem; align-items: center; }
.primary-nav a {
  display: block; padding: .65rem .7rem; border-radius: var(--radius-sm);
  text-decoration: none; color: var(--ink); font-weight: 700; font-size: .92rem;
  text-transform: capitalize; letter-spacing: .002em; white-space: nowrap;
  transition: background-color .16s var(--ease), color .16s var(--ease);
}
.primary-nav a:hover { background: var(--cream-deep); color: var(--navy); }
.primary-nav a[aria-current="page"], .primary-nav .is-active > a { color: var(--blue-dark); background: var(--blue-soft); }
.has-children { position: relative; }
.has-children > button {
  display: inline-flex; align-items: center; gap: .35rem; background: none; border: 0; font: inherit;
  font-weight: 700; font-size: .92rem; text-transform: capitalize; letter-spacing: .002em;
  color: var(--ink); padding: .65rem .7rem; border-radius: var(--radius-sm); cursor: pointer;
  white-space: nowrap;
  transition: background-color .16s var(--ease), color .16s var(--ease);
}
.has-children > button:hover { background: var(--cream-deep); color: var(--navy); }
.has-children > button .chev { width: .55rem; height: .55rem; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg); margin-top: -3px; }
.submenu {
  position: absolute; top: calc(100% + .4rem); left: 0; min-width: 268px;
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: .4rem; display: none; z-index: 70;
}
.has-children.open .submenu { display: block; }
.submenu a { font-weight: 500; padding: .6rem .75rem; text-transform: none; letter-spacing: 0; font-size: .98rem; }

.header-actions { display: flex; gap: .6rem; align-items: center; margin-left: auto; }

/* The menu bar collapses to the Menu button below 1280px.
   It used to be 1200px, from when there were seven items in the menu. There are
   eight now, and measuring the header in a browser at every common desktop
   width showed the last item ("Contact") sitting ON TOP of the Parent Portal
   button at EVERY width from 1200 up — by 84px at 1200 and by 119px at 1920.
   Nothing overflowed the page, so nothing looked broken from the outside; the
   menu simply overlapped the buttons. 1280 is where the measured gap is a
   comfortable 26px. */
@media (max-width: 1279px) {
  .primary-nav {
    display: none; position: absolute; left: 0; right: 0; top: 100%;
    background: #fff; border-bottom: 1px solid var(--line); box-shadow: var(--shadow);
    padding: .75rem 0 1.25rem; max-height: calc(100vh - 74px); overflow-y: auto;
  }
  .site-header.nav-open .primary-nav { display: block; }
  .primary-nav > ul { flex-direction: column; align-items: stretch; gap: 0; width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; }
  .primary-nav a, .has-children > button { padding: .9rem .75rem; width: 100%; text-align: left; font-size: 1.08rem; }
  .submenu { position: static; display: block; border: 0; box-shadow: none; padding: 0 0 .5rem .9rem; min-width: 0; }
  .submenu a { font-size: 1rem; color: var(--ink-soft); }
  .header-actions { margin-left: 0; flex: 0 0 auto; }
  .header-actions .btn { white-space: nowrap; min-height: 46px; padding-inline: 1rem; font-size: .82rem; }
  .header-actions .btn-portal { display: none; }
  .site-header.nav-open .primary-nav .portal-mobile { display: block; }
}

/* Narrow phones. The row is brand + menu button + Get Started, and all three
   have to fit inside 320px without the last one sliding off the screen. */
@media (max-width: 600px) {
  .header-inner { gap: .6rem; }
  .brand img { height: 48px; }
  .brand-name { font-size: 1rem; white-space: nowrap; }
  .brand-tag { display: none; }
  .nav-toggle { padding: .55rem .75rem; gap: .4rem; }
  .header-actions .btn { padding-inline: .85rem; font-size: .78rem; letter-spacing: .05em; }
}
@media (max-width: 420px) {
  /* Below this the name itself is the thing that will not fit. The logo carries
     the identity on its own; the accessible name stays in the link's alt text. */
  .brand-text { display: none; }
}
@media (min-width: 1200px) {
  .nav-toggle { display: none; }
  /* The nav takes the space left over and never forces the row wider than the
     window; the brand gives way first, because the logo carries it. */
  .primary-nav { margin-left: 1rem; flex: 1 1 auto; min-width: 0; }
  .primary-nav > ul { flex-wrap: nowrap; }
  .portal-mobile { display: none; }
  /* One line, always. The strapline is decorative and repeats in the footer, so
     it gives up its space to the menu rather than pushing it onto a second row. */
  .brand-name { white-space: nowrap; font-size: 1.06rem; }
  .brand-tag { display: none; }
  .brand img { height: 60px; }
  /* Parent Portal is for families who already have an account, so it steps back
     to a quiet text link and leaves GET STARTED as the only button in the bar.
     That is roughly 130px returned to the menu, which is what stopped the last
     menu item colliding with it. */
  .header-actions .btn-portal {
    background: none; border-color: transparent; color: var(--navy);
    padding-inline: .6rem; min-height: 44px; font-size: .78rem;
  }
  .header-actions .btn-portal:hover {
    background: var(--cream-deep); color: var(--navy-900); box-shadow: none; transform: none;
  }
  .header-actions { gap: .35rem; }
}
/* The desktop menu bar. Eight labels, a logo and three buttons, inside a header
   that is capped at 1320px however wide the screen is — so a 1920px screen has
   exactly as much room for them as a 1366px one does, and the compact treatment
   below is right for all of them rather than for one narrow band. Every number
   here was set by measuring the gap between the last menu item and the first
   button in a browser, not by eye. */
@media (min-width: 1280px) {
  .brand img { height: 50px; }
  .brand-name { font-size: .98rem; white-space: nowrap; }
  .brand-tag { display: none; }
  .primary-nav { margin-left: .4rem; min-width: 0; flex: 1 1 auto; }
  .primary-nav a, .has-children > button {
    font-size: .72rem; padding-inline: .34rem; letter-spacing: .01em;
  }
  /* The buttons are the fixed part: the menu gives way, never these. */
  .header-actions { gap: .35rem; flex: 0 0 auto; }
  .header-actions .btn { font-size: .72rem; padding-inline: .8rem; }
  /* Parent Portal is for families who already have an account, so on the menu
     bar it steps back to a quiet text link. It also must not wrap: two short
     lines of "Parent / Portal" made the whole header 11px taller and read as a
     mistake. */
  .header-actions .btn-portal {
    background: none; border-color: transparent; color: var(--navy);
    padding-inline: .45rem; min-height: 44px; font-size: .74rem; white-space: nowrap;
  }
  .header-actions .btn-portal:hover {
    background: var(--cream-deep); color: var(--navy-900); box-shadow: none; transform: none;
  }
}

/* A screen with genuine room to spare. The header is allowed to be wider than
   the reading column, and everything in it can breathe again. */
@media (min-width: 1600px) {
  .site-header .header-inner { width: min(100% - 2.5rem, 1460px); }
  .brand img { height: 58px; }
  .brand-name { font-size: 1.14rem; }
  .primary-nav a, .has-children > button { font-size: .84rem; padding-inline: .5rem; }
  .header-actions .btn { font-size: .78rem; padding-inline: 1rem; }
  .header-actions .btn-portal { font-size: .8rem; padding-inline: .6rem; }
}

/* --------------------------------------------------------- announcements -- */
.announcement {
  background: var(--navy); color: #fff; font-size: .98rem;
}
.announcement.level-WARNING { background: var(--gold); color: var(--navy-900); }
.announcement.level-CLOSURE { background: var(--red); color: #fff; }
.announcement.level-SUCCESS { background: var(--green); color: #fff; }
.announcement .wrap { display: flex; flex-wrap: wrap; gap: .5rem 1rem; align-items: center; padding: .7rem 0; }
.announcement strong { font-weight: 700; }
.announcement a { color: inherit; font-weight: 700; }

/* ----------------------------------------------------------------- flash -- */
.flash { border-radius: var(--radius); padding: 1rem 1.15rem; margin: 1.25rem 0; border: 1px solid; font-weight: 500; }
.flash-success { background: var(--green-soft); border-color: #bfe0cb; color: #17512F; }
.flash-error { background: var(--red-soft); border-color: #f3c4bf; color: #8A1F14; }
.flash-info { background: var(--blue-soft); border-color: #c3ddf7; color: #10406F; }

/* --------------------------------------------------------------- section -- */
.section { padding: var(--section-y) 0; }
.section-tight { padding: clamp(2rem, 4vw, 3rem) 0; }
.section-cream { background: var(--cream-deep); }
.section-white { background: var(--white); }
.section-navy { background: var(--navy); color: #EAF0FA; }
.section-navy h1, .section-navy h2, .section-navy h3 { color: #fff; }
.section-navy a { color: #BBD6F5; }
.section-navy p, .section-navy .lede, .section-navy li { color: #C9D8EE; }
/* Buttons keep their own colours inside dark sections — the link colour above
   must not bleed into them or the labels lose contrast. */
.section-navy a.btn-gold, .hero.theme-navy a.btn-gold { color: var(--navy-900); }
.section-navy a.btn-primary, .hero.theme-navy a.btn-primary { color: #fff; }
.section-navy a.btn-on-dark, .hero.theme-navy a.btn-on-dark { color: #fff; }
.section-navy a.btn-on-dark:hover, .hero.theme-navy a.btn-on-dark:hover { color: var(--navy-900); }
.section-navy .eyebrow { color: var(--gold); }
.section-gold .eyebrow { color: #8A5B04; }
.section-gold { background: var(--gold-soft); }
.section-head { max-width: 62ch; margin-bottom: clamp(1.75rem, 3.2vw, 2.75rem); }
.section-head.center { margin-inline: auto; text-align: center; }
/* A section that runs to several paragraphs: centred as a block so it is not an
   orphan column against an empty half-page, but the body text stays
   left-aligned and on a readable measure. */
.section-head.prose { margin-inline: auto; text-align: center; max-width: 68ch; }
.section-head.prose .lede { text-align: left; }
.section-head.prose .lede p + p { margin-top: 1rem; }
.section-head h2 { margin-bottom: .45rem; }
.section-head .lede { margin-bottom: 0; }

/* Two plain sections in a row read as one long undifferentiated scroll. A hair
   line between them restores the boundary without adding another colour band. */
.section + .section:not([class*="section-"]) { border-top: 1px solid var(--line-soft); }

/* ------------------------------------------------------------------ hero -- */
.hero { padding: clamp(2.5rem, 6vw, 5rem) 0; background: var(--white); }
.hero.theme-cream { background: var(--cream-deep); }
.hero.theme-navy { background: linear-gradient(160deg, var(--navy) 0%, var(--navy-900) 100%); color: #E7EEF9; }
.hero.theme-navy h1 { color: #fff; }
.hero.theme-navy .lede { color: #C9D8EE; }
.hero.theme-gold { background: var(--gold-soft); }
.hero-grid { display: grid; gap: clamp(1.5rem, 4vw, 3.25rem); align-items: center; }
.hero-grid > * { min-width: 0; }
@media (min-width: 900px) { .hero-grid.has-image { grid-template-columns: 1.05fr .95fr; } }
.hero h1 { margin-bottom: .65rem; font-size: clamp(1.9rem, 3.9vw, 3rem); }
.hero .eyebrow { margin-bottom: .9rem; }
.hero .lede { margin-bottom: 1.75rem; max-width: 54ch; }
/* The hero photo is given a fixed shape too, so the two columns end level
   instead of the image dictating the height of the whole band. */
.hero-media { position: relative; }
.hero-media img {
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  width: 100%; aspect-ratio: var(--ratio-wide); object-fit: cover; height: auto;
}
/* Any picture sitting beside a block of text gets the same treatment. */
.media-frame {
  border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow);
  background: var(--cream-deep);
}
.media-frame img { width: 100%; aspect-ratio: var(--ratio-card); object-fit: cover; height: auto; }
.hero.align-center { text-align: center; }
.hero.align-center .lede { margin-inline: auto; }
.hero.align-center .btn-row { justify-content: center; }

/* ----------------------------------------------------------------- cards -- */
.grid { display: grid; gap: var(--gap); align-items: stretch; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); }
/* Every direct child of a grid fills its row. Without this a card with two lines
   of text sits short next to one with five, and the row looks broken even though
   nothing is wrong with either card. */
.grid > * { min-width: 0; height: 100%; }
/* Cards stretch to the height of their own row, which is what makes a row line
   up. They are deliberately NOT equalised across rows: forcing every card in the
   grid to match the tallest one anywhere leaves short cards with a lake of empty
   space under the text, which looks worse than rows of differing heights. */

.card {
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: clamp(1.25rem, 2.4vw, 1.75rem); box-shadow: var(--shadow-sm);
  display: flex; flex-direction: column; gap: .65rem;
  height: 100%; overflow: hidden;
  transition: transform .22s var(--ease), box-shadow .22s var(--ease), border-color .22s var(--ease);
}
.card h3 { margin: 0; }
.card p { margin: 0; color: var(--ink-soft); }
/* `margin-top: auto` pins the link to the bottom of the card, so the call to
   action lines up across a whole row however long the descriptions are. */
.card .card-link {
  margin-top: auto; padding-top: .75rem; font-weight: 700; font-size: .84rem;
  text-transform: uppercase; letter-spacing: .06em;
  text-decoration: none; display: inline-flex; gap: .45rem;
  /* A long label wraps onto two or three lines. Aligning to the end keeps the
     arrow beside the last line instead of floating beside the middle of the
     block. */
  align-items: flex-end;
}
.card .card-link::after { content: '→'; transition: transform .2s var(--ease); }
.card:hover .card-link::after, .card .card-link:hover::after { transform: translateX(4px); }

/* A card that contains a link lifts on hover and shows a ring when anything
   inside it takes keyboard focus, so mouse and keyboard get the same feedback. */
.card:has(.card-link):hover, .card:has(a):hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow);
  border-color: rgba(29, 111, 208, .35);
}
.card:focus-within { border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-soft), var(--shadow); }

.card-media { margin: calc(-1 * clamp(1.25rem, 2.4vw, 1.75rem)); margin-bottom: .25rem; overflow: hidden; }
/* One shape for every card photo. The images arrive in whatever the camera gave
   us — 16:9, 4:3, 3:2 — and without a fixed ratio each card is a different
   height and the row never lines up. */
.card-media img {
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  aspect-ratio: var(--ratio-card); object-fit: cover; object-position: center;
  width: 100%; height: auto;
  transition: transform .5s var(--ease);
}
.card:hover .card-media img { transform: scale(1.035); }
.card-icon {
  width: 52px; height: 52px; border-radius: 15px; display: grid; place-items: center;
  background: var(--blue-soft); color: var(--blue-dark); flex: 0 0 auto;
}
.card-icon svg { width: 30px; height: 30px; }

/* ---------------------------------------------------------- program card -- */
.program-card { position: relative; }
.program-card .status-badge { align-self: flex-start; }
.program-meta { list-style: none; margin: .35rem 0 0; padding: 0; display: grid; gap: .3rem; font-size: .95rem; color: var(--ink-soft); }
.program-meta li { display: flex; gap: .5rem; }
.program-meta dt, .program-meta .k { font-weight: 650; color: var(--navy); min-width: 5.5rem; }

.status-badge {
  display: inline-flex; align-items: center; gap: .4rem; font-size: .78rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; padding: .32rem .7rem; border-radius: var(--radius-pill);
}
.status-ACTIVE { background: var(--green-soft); color: #1D6B41; }
/* Item 17: the badge says what a visitor can do, not what the record says. */
.status-ENROLLING { background: var(--green-soft); color: #1D6B41; }
.status-REQUEST { background: var(--blue-soft, #E3EEFC); color: #14498F; }
.status-COMING_SOON { background: var(--gold-soft); color: #8A5B04; }
.status-FULL { background: var(--red-soft); color: #96271A; }
.status-REGISTRATION_CLOSED { background: var(--line-soft); color: var(--muted); }
.status-HIDDEN { background: var(--line-soft); color: var(--muted); }

/* -------------------------------------------------------- S.N.A.P method -- */
/* Four steps that happen in order. Each carries one of the four brand colours,
   set once as --accent, and everything in the card reads it — the rail, the
   number, the letter, the watermark and the thread that runs to the next step.
   The result is a family of four rather than four unrelated boxes. */
.snap-section { position: relative; overflow: hidden; }
.snap-eyebrow { color: var(--gold); }
.snap-lede { color: #D3E0F2; }
.snap-footnote { margin-top: 2rem; color: #D3E0F2; max-width: 70ch; }
.snap-head-block { max-width: 68ch; }

/* A soft wash of the four brand colours behind the band, so the panel has
   depth without anything on it having to shout. */
.snap-aura {
  position: absolute; inset: -30% -10% auto -10%; height: 150%; pointer-events: none;
  background:
    radial-gradient(38% 42% at 12% 30%, rgba(210, 58, 44, .30), transparent 70%),
    radial-gradient(34% 40% at 38% 72%, rgba(29, 111, 208, .34), transparent 70%),
    radial-gradient(32% 38% at 66% 26%, rgba(240, 163, 26, .26), transparent 70%),
    radial-gradient(36% 40% at 90% 70%, rgba(46, 143, 85, .26), transparent 70%);
  filter: blur(20px); opacity: .85;
}
.snap-section > .wrap { position: relative; z-index: 1; }

/* The four letters as blocks, the way they are on the logo and on the poster in
   the room. Decorative — the readable version is in the cards below. */
.snap-blocks { display: flex; gap: .4rem; margin: .1rem 0 1.1rem; }
.snap-blocks b {
  width: 2.1rem; height: 2.1rem; border-radius: 7px;
  display: grid; place-items: center;
  font-family: var(--font-body); font-weight: 800; font-size: .95rem; line-height: 1;
  color: #fff; background: var(--c, var(--blue));
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .18);
}
.snap-blocks b:nth-child(1) { --c: #C8392D; }
.snap-blocks b:nth-child(2) { --c: #1D6FD0; }
.snap-blocks b:nth-child(3) { --c: #D98C0A; color: #26170A; }
.snap-blocks b:nth-child(4) { --c: #257A48; }

.snap-grid {
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
  list-style: none; margin: 0; padding: 0;
}
.snap-grid > * { height: 100%; }
.snap-step {
  --accent: var(--blue);
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 1.5rem 1.35rem; position: relative;
  display: flex; flex-direction: column;
}
/* Two strengths of each colour: the full one for the rail, the ring and the
   watermark, and a lighter one for anything that has to be read on navy. */
.snap-step:nth-child(1) { --accent: #E8564A; --accent-soft: #FFB0A6; }
.snap-step:nth-child(2) { --accent: #5CA8F5; --accent-soft: #A8D4FF; }
.snap-step:nth-child(3) { --accent: var(--gold); --accent-soft: #FFC65C; }
.snap-step:nth-child(4) { --accent: #46C480; --accent-soft: #86E3AE; }

/* The letter again, oversized and barely there, so the card has a texture of
   its own. Its own clipped layer, so the letter can run off the corner without
   the card having to hide everything else that leaves its bounds. */
.snap-glyph {
  position: absolute; inset: 0; z-index: 0; overflow: hidden;
  border-radius: inherit; pointer-events: none; user-select: none;
}
.snap-glyph b {
  position: absolute; right: -.35em; bottom: -.42em;
  font-family: var(--font-display); font-weight: 700; font-size: 8.5rem; line-height: 1;
  color: var(--accent); opacity: .10;
}

/* The coloured edge along the top. It grows across the card when the step is
   pointed at, focused or tapped. */
.snap-rail {
  position: absolute; inset: 0 auto auto 0; height: 4px; width: 34%;
  background: var(--accent); border-radius: 0 4px 4px 0; z-index: 2;
}

.snap-top {
  position: relative; z-index: 1; display: flex; align-items: center; gap: .6rem;
  margin-bottom: .9rem; min-height: 1.9rem;
}
.snap-num {
  display: grid; place-items: center; width: 1.9rem; height: 1.9rem; border-radius: 50%;
  font-size: .8rem; font-weight: 700; line-height: 1; flex: 0 0 auto;
  color: var(--accent-soft, var(--accent)); border: 1.5px solid var(--accent); background: transparent;
}
/* "Step 2 of 4" — the position in the sequence, spelled out when you are on
   that step. The number beside it already says the same thing, so it is hidden
   from screen readers rather than read out twice. */
.snap-position {
  font-size: .7rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent-soft, var(--accent)); white-space: nowrap;
}
.snap-letter {
  position: relative; z-index: 1;
  font-family: var(--font-display); font-size: 2.6rem; line-height: 1; font-weight: 700;
  color: var(--navy-900); display: block; margin-bottom: .2rem;
}
.snap-word {
  position: relative; z-index: 1;
  font-weight: 700; letter-spacing: .1em; text-transform: uppercase; font-size: .82rem;
  color: var(--blue-dark); display: block; margin-bottom: .55rem;
}
.snap-step p { position: relative; z-index: 1; margin: 0; }

/* The extra detail, and the button that opens it. Both only exist when the
   owner has written something for that step. */
.snap-more {
  position: relative; z-index: 1; align-self: flex-start; margin-top: 1rem;
  display: inline-flex; align-items: center; gap: .5rem;
  background: transparent; border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  color: var(--accent-soft, var(--accent));
  font-family: var(--font-body); font-weight: 700; font-size: .76rem;
  letter-spacing: .08em; text-transform: uppercase;
  padding: .5rem .85rem; border-radius: var(--radius-pill); cursor: pointer;
  min-height: 38px;
}
.snap-more-close { display: none; }
.snap-more[aria-expanded="true"] .snap-more-open { display: none; }
.snap-more[aria-expanded="true"] .snap-more-close { display: inline; }
.snap-more-chevron {
  width: .5rem; height: .5rem; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
}
.snap-detail { position: relative; z-index: 1; margin-top: .85rem; }
.snap-detail p { font-size: .95rem; }

.section-navy .snap-step { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.16); }
/* S, N, A and P each in their own colour — the four letters are the framework,
   so they carry the brand rather than four identical white capitals. */
.section-navy .snap-letter { color: var(--accent-soft, #fff); }
.section-navy .snap-word { color: var(--accent-soft, var(--gold)); opacity: .92; }
.section-navy .snap-step p { color: #D3E0F2; }

/* -------------------------------------------------------------- timeline -- */
.timeline { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; counter-reset: step; }
.timeline li {
  position: relative; padding: 0 0 1.75rem 3.3rem; counter-increment: step;
}
.timeline li::before {
  content: counter(step); position: absolute; left: 0; top: 0;
  width: 2.3rem; height: 2.3rem; border-radius: 50%;
  background: var(--blue); color: #fff; display: grid; place-items: center;
  font-weight: 700; font-size: .95rem;
}
.timeline li::after {
  content: ''; position: absolute; left: 1.13rem; top: 2.5rem; bottom: .2rem; width: 2px; background: var(--line);
}
.timeline li:last-child { padding-bottom: 0; }
.timeline li:last-child::after { display: none; }
.timeline h3 { margin: .15rem 0 .3rem; font-size: 1.15rem; }
.timeline p { margin: 0; color: var(--ink-soft); }
.timeline .step-time { font-size: .88rem; color: var(--muted); font-weight: 600; }

/* ------------------------------------------------------------------- FAQ -- */
.faq-list { border-top: 1px solid var(--line); }
.faq-item { border-bottom: 1px solid var(--line); }
.faq-item summary {
  cursor: pointer; padding: 1.1rem .5rem 1.1rem 0; font-weight: 650; font-size: 1.08rem;
  list-style: none; display: flex; gap: 1rem; align-items: flex-start; color: var(--navy-900);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: '+'; margin-left: auto; font-size: 1.5rem; line-height: 1; color: var(--blue-dark); flex: 0 0 auto;
}
.faq-item[open] summary::after { content: '–'; }
.faq-answer { padding: 0 2.5rem 1.35rem 0; color: var(--ink-soft); }

/* --------------------------------------------------------------- details -- */
.detail-grid { display: grid; gap: var(--gap); }
@media (min-width: 940px) { .detail-grid { grid-template-columns: minmax(0, 1fr) 340px; align-items: start; } }
.detail-side { position: sticky; top: 92px; display: grid; gap: 1rem; }
.fact-panel {
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 1.4rem; box-shadow: var(--shadow);
}
.fact-panel h2 { font-size: 1.15rem; }
.fact-list { margin: 0; display: grid; gap: .7rem; }
.fact-list div { display: grid; gap: .1rem; }
.fact-list dt { font-size: .8rem; letter-spacing: .07em; text-transform: uppercase; font-weight: 700; color: var(--muted); }
.fact-list dd { margin: 0; font-weight: 600; color: var(--navy-900); }
.price-tag { font-family: var(--font-display); font-size: 2.1rem; font-weight: 700; color: var(--navy-900); line-height: 1; }
.included-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.included-list li { display: flex; gap: .6rem; align-items: flex-start; }
.included-list li::before { content: '✓'; color: var(--green); font-weight: 800; flex: 0 0 auto; }

.date-groups { display: grid; gap: .55rem; list-style: none; margin: 0; padding: 0; }
.date-groups li { display: flex; gap: .75rem; padding: .6rem .85rem; background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-sm); }
.date-groups .month { font-weight: 700; color: var(--navy); min-width: 5.5rem; }

/* ----------------------------------------------------------------- forms -- */
.form-card {
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: clamp(1.35rem, 3vw, 2.25rem); box-shadow: var(--shadow-sm);
}
.field { margin-bottom: 1.3rem; }
.field > label, .field-legend {
  display: block; font-weight: 650; color: var(--navy-900); margin-bottom: .35rem; font-size: 1.02rem;
}
.field .help { display: block; color: var(--muted); font-size: .93rem; margin-bottom: .45rem; }
.field .required-mark { color: var(--red); font-weight: 700; }
.field .optional-mark { color: var(--muted); font-weight: 500; font-size: .88rem; }

input[type="text"], input[type="email"], input[type="tel"], input[type="password"],
input[type="date"], input[type="number"], input[type="url"], input[type="search"],
select, textarea {
  width: 100%; font: inherit; font-size: 1.02rem; color: var(--ink);
  padding: .8rem .95rem; min-height: 52px;
  background: var(--white); border: 2px solid #CFC8BB; border-radius: var(--radius-sm);
  transition: border-color .12s ease, box-shadow .12s ease;
}
textarea { min-height: 120px; resize: vertical; line-height: 1.55; }
select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath fill='none' stroke='%2316203A' stroke-width='2' d='M1 1l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 1rem center; padding-right: 2.6rem;
}
input:focus, select:focus, textarea:focus { border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-soft); outline: none; }
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] { border-color: var(--red); }

.field-error {
  display: flex; gap: .4rem; align-items: flex-start;
  color: #96271A; font-weight: 600; font-size: .95rem; margin-top: .4rem;
}
.field-error::before { content: '!'; display: grid; place-items: center; width: 1.2rem; height: 1.2rem; border-radius: 50%; background: var(--red); color: #fff; font-size: .78rem; flex: 0 0 auto; margin-top: .1rem; }

fieldset { border: 0; margin: 0 0 1.3rem; padding: 0; }
fieldset legend { padding: 0; }
.choice-list { display: grid; gap: .6rem; margin-top: .5rem; }
.choice {
  display: flex; gap: .75rem; align-items: flex-start; padding: .9rem 1rem;
  border: 2px solid var(--line); border-radius: var(--radius); background: var(--white); cursor: pointer;
}
.choice:hover { border-color: #BFD6F0; }
.choice input { margin: .28rem 0 0; width: 1.15rem; height: 1.15rem; accent-color: var(--blue); flex: 0 0 auto; }
.choice input:checked ~ .choice-body .choice-title { color: var(--blue-dark); }
.choice:has(input:checked) { border-color: var(--blue); background: var(--blue-soft); }
.choice-title { font-weight: 650; display: block; }
.choice-note { color: var(--muted); font-size: .93rem; display: block; }

.field-row { display: grid; gap: 1rem; }
@media (min-width: 620px) {
  .field-row.two { grid-template-columns: 1fr 1fr; }
  .field-row.three { grid-template-columns: repeat(3, 1fr); }
}
.form-actions { display: flex; flex-wrap: wrap; gap: .8rem; align-items: center; margin-top: 1.5rem; }
.form-note { font-size: .93rem; color: var(--muted); }
/* The "this is not a booking" line on a service request. Same size as the note
   beside it, but not grey — it is the one sentence on the form a reader must
   not skim past. */
.form-note-strong { color: var(--navy-900); font-weight: 600; }
.honeypot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ------------------------------------------------------------ error list -- */
.error-summary {
  background: var(--red-soft); border: 2px solid #eeb5ad; border-radius: var(--radius);
  padding: 1.1rem 1.25rem; margin-bottom: 1.5rem;
}
.error-summary h2 { font-size: 1.1rem; color: #8A1F14; margin-bottom: .4rem; }
.error-summary ul { margin: 0; padding-left: 1.1rem; }
.error-summary a { color: #8A1F14; font-weight: 600; }

/* ------------------------------------------------------------- wizard ---- */
.wizard-header { background: var(--white); border-bottom: 1px solid var(--line); padding: .9rem 0; }
.wizard-header .wrap { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.steps { list-style: none; display: flex; flex-wrap: wrap; gap: .35rem; margin: 0; padding: 0; counter-reset: wstep; }
.steps li {
  counter-increment: wstep; font-size: .84rem; font-weight: 650; color: var(--muted);
  display: flex; align-items: center; gap: .4rem; padding: .3rem .65rem; border-radius: var(--radius-pill);
  background: var(--cream-deep);
}
.steps li::before {
  content: counter(wstep); width: 1.4rem; height: 1.4rem; border-radius: 50%;
  background: #CFC8BB; color: #fff; display: grid; place-items: center; font-size: .76rem;
}
.steps li.done { color: var(--green); background: var(--green-soft); }
.steps li.done::before { content: '✓'; background: var(--green); }
.steps li.current { color: #fff; background: var(--blue); }
.steps li.current::before { background: rgba(255,255,255,.3); }
.progress-bar { height: 6px; background: var(--line-soft); border-radius: 999px; overflow: hidden; }
.progress-bar > span { display: block; height: 100%; background: var(--blue); border-radius: 999px; }
.autosave-note { font-size: .88rem; color: var(--muted); margin-left: auto; }

.wizard-main { padding: clamp(1.5rem, 4vw, 3rem) 0 5rem; }
.wizard-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: clamp(1.35rem, 3.5vw, 2.5rem); box-shadow: var(--shadow); }
.wizard-summary { background: var(--cream-deep); border-radius: var(--radius); padding: 1rem 1.15rem; margin-bottom: 1.5rem; font-size: .97rem; }

.sticky-cta {
  position: sticky; bottom: 0; background: var(--white); border-top: 1px solid var(--line);
  padding: .85rem 0; z-index: 40; box-shadow: 0 -6px 20px rgba(16,48,95,.07);
}
.sticky-cta .wrap { display: flex; gap: .8rem; align-items: center; justify-content: space-between; flex-wrap: wrap; }

/* --------------------------------------------------------------- tables -- */
.table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); background: #fff; }
table.data { width: 100%; border-collapse: collapse; font-size: .97rem; }
table.data th, table.data td { text-align: left; padding: .8rem 1rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
table.data th { background: var(--cream-deep); font-weight: 700; color: var(--navy-900); white-space: nowrap; }
table.data tr:last-child td { border-bottom: 0; }

/* ---------------------------------------------------------------- panels -- */
.panel { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: clamp(1.2rem, 2.5vw, 1.75rem); }
.panel + .panel { margin-top: 1.25rem; }
.callout {
  border-left: 5px solid var(--blue); background: var(--blue-soft); padding: 1.1rem 1.25rem;
  border-radius: 0 var(--radius) var(--radius) 0;
}
.callout.warn { border-color: var(--gold); background: var(--gold-soft); }
.callout.alert { border-color: var(--red); background: var(--red-soft); }
.callout.good { border-color: var(--green); background: var(--green-soft); }
.callout h2, .callout h3 { font-size: 1.08rem; margin-bottom: .3rem; }
.callout p:last-child { margin-bottom: 0; }

.stat-row { display: grid; gap: var(--gap); grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr)); }
.stat { text-align: center; }
.stat .value { font-family: var(--font-display); font-size: clamp(2.2rem, 5vw, 3rem); font-weight: 700; color: var(--navy-900); line-height: 1; display: block; }
.stat .label { color: var(--muted); font-size: .97rem; }

/* --------------------------------------------------------------- founder -- */
.founder { display: grid; gap: var(--gap); align-items: start; }
@media (min-width: 820px) { .founder { grid-template-columns: 300px 1fr; } }
.founder img { border-radius: var(--radius-lg); box-shadow: var(--shadow); width: 100%; aspect-ratio: 4/5; object-fit: cover; }
.credentials { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .45rem; }
.credentials li { padding-left: 1.5rem; position: relative; color: var(--ink-soft); }
.credentials li::before { content: '◆'; position: absolute; left: 0; color: var(--gold); font-size: .8rem; top: .3rem; }

/* The trademark sign. Most system fonts draw U+2122 at close to full size on
   the baseline, which in a headline reads as two stray capitals stuck to the
   end of the word. Set small and raised, it reads as the mark it is. The
   `line-height: 0` keeps it from pushing the line it sits on any taller. */
.tm {
  font-size: .55em;
  font-weight: inherit;
  vertical-align: .62em;
  line-height: 0;
  letter-spacing: .02em;
}
/* Headings are set much larger than prose, and a mark scaled from them at the
   same ratio becomes a third word. Smaller, and lifted further, so it sits
   against the cap height rather than beside it. */
h1 .tm, h2 .tm, h3 .tm, .hero-cover-title .tm {
  font-size: .4em;
  vertical-align: 1.05em;
}

/* -------------------------------------------------------------- gallery --- */
/* Photographs in justified rows — the arrangement Flickr and Google Photos
   use, and the reason their galleries read as galleries rather than as a pile
   of pictures.

   HOW IT WORKS. Every row is the same height. Each picture is a flex item
   whose natural width at that height is its shape times that height, and it is
   allowed to grow in the same proportion, so a row stretches from margin to
   margin with every picture still very nearly its own shape. The shape arrives
   from the template as `--r`, because the width and height of every photograph
   are already recorded in the media library and CSS has no way to ask.

   WHY NOT A GRID. A grid has to choose one shape and crop every photograph to
   it, which on a picture taken with a phone held upright takes the top of a
   head off. Columns keep the shapes but leave the foot of the gallery ragged.
   Rows keep the shapes AND square the edges. */
.gallery {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  /* The height of one row. Shorter on a phone, where a tall row means one
     photograph fills the screen and the gallery stops looking like a set. */
  --gallery-row: 11rem;
}
@media (min-width: 700px) { .gallery { --gallery-row: 12rem; } }
@media (min-width: 1100px) { .gallery { --gallery-row: 13rem; } }
/* Why 13rem and not something taller. A taller row holds fewer photographs,
   and fewer per row means the last row is left emptier — at 14rem the giving
   page's three pictures stopped fitting on one line and the third dropped
   underneath on its own. At 13rem a wide screen takes three or four across,
   the giving page's three sit together, and the student gallery's last row is
   nearly full. Measured in a browser, not guessed. */

/* The last row has fewer pictures than it has room for. Without this they are
   stretched across the whole width and the row below the rest of the gallery
   is suddenly enormous; this soaks up the spare space instead, so the final
   row simply ends where it ends. */
.gallery::after { content: ''; flex-grow: 1000000; }

.gallery-item {
  /* grow, shrink, and a starting width of shape x row height. */
  flex: var(--r, 1.5) 1 calc(var(--r, 1.5) * var(--gallery-row));
  margin: 0;
  min-width: 0;
}
.gallery-link {
  position: relative;
  display: block;
  height: var(--gallery-row);
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: #E9E3D8;
  box-shadow: var(--shadow-sm);
}
.gallery-link img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: 0;
  box-shadow: none;
  transition: transform .6s cubic-bezier(.2, .6, .2, 1);
}
/* A quiet gradient along the bottom edge. It is what stops a row of bright
   photographs from fizzing against the cream page, and it gives a caption
   something to sit on. */
.gallery-sheen {
  position: absolute;
  inset: auto 0 0 0;
  height: 42%;
  background: linear-gradient(to top, rgba(11, 29, 58, .34), rgba(11, 29, 58, 0));
  opacity: .75;
  transition: opacity .35s ease;
  pointer-events: none;
}
/* A tile carrying words needs more than a hint of a gradient behind them: white
   text on a bright classroom photograph is unreadable without it. */
.gallery-item:has(figcaption) .gallery-sheen {
  height: 62%;
  background: linear-gradient(to top, rgba(11, 29, 58, .82), rgba(11, 29, 58, .25) 55%, rgba(11, 29, 58, 0));
  opacity: 1;
}
.gallery-link:hover img,
.gallery-link:focus-visible img { transform: scale(1.045); }
.gallery-link:hover .gallery-sheen,
.gallery-link:focus-visible .gallery-sheen { opacity: 1; }
.gallery-link:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }

/* A caption, when a photograph has one, printed on the picture rather than
   under it — under it would make that one tile taller than its row. */
/* The caption on a tile is the short title only — a sentence here would cover
   half the photograph, so it waits for the lightbox. */
.gallery-item figcaption {
  position: absolute;
  left: .85rem; right: .85rem; bottom: .7rem;
  margin: 0;
  color: #fff;
  font-size: .88rem;
  font-weight: 650;
  line-height: 1.3;
  letter-spacing: .005em;
  text-shadow: 0 1px 4px rgba(11, 29, 58, .8);
  pointer-events: none;
}
.gallery-item { position: relative; }

/* On a phone a justified row holds one photograph, and "justified" then means
   that one photograph is stretched across the whole width at the row height —
   which crops an upright picture to a letterbox and takes the heads off it.
   So below tablet width the rows are abandoned and each photograph is shown
   full width at its own shape, which is also simply the nicest way to look at
   a photograph on a phone. */
@media (max-width: 699px) {
  .gallery { display: grid; grid-template-columns: 1fr; gap: .7rem; }
  .gallery::after { display: none; }
  .gallery-item { flex: none; }
  .gallery-link { height: auto; aspect-ratio: var(--r, 1.5); }
}

@media (prefers-reduced-motion: reduce) {
  .gallery-link img { transition: none; }
  .gallery-link:hover img, .gallery-link:focus-visible img { transform: none; }
}

/* ------------------------------------------------------------- lightbox --- */
/* The full-size photograph, opened from the gallery. Built on <dialog>, so the
   browser itself handles the things a hand-rolled modal gets wrong: focus
   stays inside it, Escape closes it, and the rest of the page is inert. */
.lightbox {
  border: 0;
  padding: 0;
  background: transparent;
  max-width: 100vw;
  max-height: 100vh;
  width: 100%;
  height: 100%;
  overflow: hidden;
  color: #fff;
}
.lightbox::backdrop { background: rgba(8, 20, 40, .92); }
.lightbox-stage {
  /* Flex rather than grid, and the figure is a flex item with a definite
     height: that is what gives `max-height: 100%` on the photograph something
     real to measure against. Centring the items of a grid makes each one take
     its content's size instead, and the picture then runs off the top and
     bottom of the screen — which is exactly what it did. */
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .9rem;
  width: 100%;
  height: 100%;
  padding: clamp(.8rem, 3vw, 2.2rem);
  box-sizing: border-box;
}
.lightbox-figure {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox-figure img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: var(--radius-sm);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .55);
}
.lightbox-caption {
  max-width: 60ch;
  text-align: center;
  font-size: .95rem;
  line-height: 1.5;
  color: #DCE5F3;
  margin: 0;
}
.lightbox-title { display: block; font-size: 1.05rem; color: #fff; margin-bottom: .2rem; }
.lightbox-title[hidden] { display: none; }
.lightbox-count { display: block; font-size: .8rem; color: #9FB2CE; margin-top: .35rem; }
.lightbox-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 3rem; height: 3rem;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .35);
  background: rgba(8, 20, 40, .55);
  color: #fff;
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.lightbox-btn:hover { background: rgba(8, 20, 40, .85); }
.lightbox-btn:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.lightbox-prev { left: clamp(.5rem, 2vw, 1.6rem); }
.lightbox-next { right: clamp(.5rem, 2vw, 1.6rem); }
.lightbox-close {
  position: absolute;
  top: clamp(.6rem, 2vw, 1.4rem);
  right: clamp(.6rem, 2vw, 1.4rem);
  transform: none;
}
figure { margin: 0; }
figcaption { font-size: .88rem; color: var(--muted); margin-top: .4rem; }

/* ----------------------------------------------------------- breadcrumbs -- */
.breadcrumbs { padding: .9rem 0 0; font-size: .92rem; }
.breadcrumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: .4rem; margin: 0; padding: 0; color: var(--muted); }
.breadcrumbs li + li::before { content: '/'; margin-right: .4rem; color: #B9B0A0; }

/* ---------------------------------------------------------------- footer -- */
.site-footer { background: var(--navy-900); color: #C6D2E6; padding: clamp(2.5rem, 5vw, 4rem) 0 1.5rem; margin-top: auto; }
.site-footer h2 { color: #fff; font-size: 1.05rem; font-family: var(--font-body); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; }
.footer-grid { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr)); }
.site-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.site-footer a { color: #C6D2E6; text-decoration: none; }
.site-footer a:hover { color: #fff; text-decoration: underline; }
/* The logo keeps its own proportions: the height is set, the width follows.
   Without `width: auto` the browser uses the width attribute from the markup
   and the logo is stretched sideways. */
/* The footer carries the full lockup and is the one place with room to read
   it. At 86px the wordmark under the bulb was a smudge; at 132px the SNAP
   blocks and ACADEMY read properly. */
.footer-brand img { height: 132px; width: auto; margin-bottom: 1rem; display: block; }
@media (max-width: 600px) { .footer-brand img { height: 110px; } }
.footer-bottom {
  margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px solid rgba(255,255,255,.14);
  display: flex; flex-wrap: wrap; gap: .75rem 1.5rem; justify-content: space-between; font-size: .9rem; color: #9AA9C2;
}
.social-links { display: flex; gap: .6rem; }
.social-links a {
  width: 42px; height: 42px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(255,255,255,.1); color: #fff;
}
.social-links a:hover { background: var(--blue); }
.social-links svg { width: 20px; height: 20px; }

/* -------------------------------------------------------------- get-started */
.choice-cards { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
.choice-card {
  display: flex; gap: 1rem; align-items: flex-start; text-decoration: none;
  background: var(--white); border: 2px solid var(--line); border-radius: var(--radius-lg);
  padding: 1.25rem 1.35rem; transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.choice-card:hover { border-color: var(--blue); box-shadow: var(--shadow); transform: translateY(-2px); }
.choice-card h3, .choice-card .choice-card-title {
  font-size: 1.14rem; margin: 0 0 .25rem; font-family: var(--font-display); font-weight: 600; line-height: 1.2;
}
.choice-card p { margin: 0; color: var(--ink-soft); font-size: .98rem; }

/* ------------------------------------------------------------- utilities -- */
.pill-row { display: flex; flex-wrap: wrap; gap: .5rem; }
.pill {
  display: inline-block; padding: .35rem .8rem; border-radius: var(--radius-pill);
  background: var(--cream-deep); border: 1px solid var(--line); font-size: .9rem; font-weight: 600; color: var(--navy);
  text-decoration: none;
}
.pill[aria-current="true"], .pill.active { background: var(--navy); color: #fff; border-color: var(--navy); }

.page-body { display: flex; flex-direction: column; min-height: 100vh; }
main { flex: 1 0 auto; }

.prose { max-width: 72ch; }
.prose h2 { margin-top: 2.2rem; }
.prose h3 { margin-top: 1.6rem; }
.prose ul, .prose ol { padding-left: 1.4rem; }
.prose li { margin-bottom: .4rem; }
.prose table { width: 100%; border-collapse: collapse; margin: 1.25rem 0; }
.prose th, .prose td { border: 1px solid var(--line); padding: .6rem .8rem; text-align: left; }

.print-only { display: none; }
@media print {
  .site-header, .site-footer, .btn, .announcement, .breadcrumbs, .sticky-cta { display: none !important; }
  body { background: #fff; font-size: 11pt; }
  .print-only { display: block; }
}

/* ------------------------------------------------- text beside a picture -- */
/* Replaces a `direction: rtl` trick that reversed the columns. Order is set
   explicitly instead, so the reading order for a screen reader stays text-first
   whichever side the photograph is on. */
.media-split { display: grid; gap: clamp(1.5rem, 4vw, 3.25rem); align-items: center; }
.media-split > * { min-width: 0; }
.media-split-cta { margin-top: 1.5rem; margin-bottom: 0; }
@media (min-width: 900px) {
  .media-split.has-image { grid-template-columns: 1fr 1fr; }
  .media-split.has-image.image-left .media-split-text { order: 2; }
  .media-split.has-image.image-left .media-split-media { order: 1; }
}

/* ==========================================================================
   Interaction and motion
   Movement here is small and it always means something: this is a link, this
   is where your keyboard is, this part of the page has just arrived. Anyone
   who has asked their device to reduce motion gets the finished state with no
   animation at all — that switch is honoured at the top of this file, and
   again explicitly at the bottom of this section.
   ========================================================================== */

/* ---- content arriving as you scroll ------------------------------------ */
/* The class is added by JavaScript. If JavaScript never runs, nothing moves and
   nothing is hidden — the page is fully readable without it.
   Text is moved but never faded. Fading it would mean words sitting at zero
   opacity until an observer happens to fire: unreadable if anything goes wrong,
   and a genuine contrast failure while it lasts. Only photographs fade, because
   there are no words in them to lose. */
.js-reveal [data-reveal] {
  transform: translateY(14px);
  transition: transform .55s var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: transform;
}
.js-reveal [data-reveal].is-in { transform: none; will-change: auto; }

.js-reveal [data-reveal] :is(.card-media, .media-frame) img,
.js-reveal [data-reveal]:is(.hero-media, .media-frame) img {
  opacity: 0;
  transition: opacity .6s var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
}
.js-reveal [data-reveal].is-in :is(.card-media, .media-frame) img,
.js-reveal [data-reveal].is-in:is(.hero-media, .media-frame) img { opacity: 1; }

/* ---- links ------------------------------------------------------------- */
a { transition: color .16s var(--ease); }
main a:not(.btn):not(.card-link) {
  text-decoration-color: rgba(29, 111, 208, .4);
  transition: color .16s var(--ease), text-decoration-color .16s var(--ease);
}
main a:not(.btn):not(.card-link):hover { text-decoration-color: currentColor; }

/* ---- keyboard focus ---------------------------------------------------- */
/* A single, unmistakable focus ring everywhere, including on dark bands where
   the default blue would disappear into the background. */
:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; border-radius: 4px; }
.section-navy :focus-visible, .hero.theme-navy :focus-visible, .site-footer :focus-visible {
  outline-color: var(--gold);
}

/* ---- the S.N.A.P. method steps ----------------------------------------- */
/* Reaching a step brings its colour forward: the card fills with a tint of that
   step's colour, the rail runs the full width, the number fills in, the letter
   grows, the watermark warms up and "Step 2 of 4" appears. The thread running
   between the cards fills up to that step, so you can see how far through the
   method you are.

   Three ways in, all doing the same thing: :hover for a mouse, :focus-within
   for a keyboard, and .is-active which the page adds when a card is tapped, so
   a phone gets the same behaviour as a desktop. */
.snap-step {
  transition: transform .3s var(--ease), background-color .3s var(--ease),
    border-color .3s var(--ease), box-shadow .3s var(--ease), opacity .3s var(--ease);
}
.snap-rail { transition: width .38s var(--ease), box-shadow .38s var(--ease); }
.snap-glyph b { transition: opacity .38s var(--ease), transform .45s var(--ease), color .3s var(--ease); }
.snap-letter { transition: transform .35s var(--ease), color .3s var(--ease); transform-origin: left bottom; }
.snap-num {
  transition: background-color .28s var(--ease), color .28s var(--ease),
    transform .28s var(--ease), border-color .28s var(--ease);
}
.snap-position { opacity: 0; transform: translateX(-6px); transition: opacity .3s var(--ease), transform .3s var(--ease); }
.snap-more { transition: background-color .2s var(--ease), color .2s var(--ease), border-color .2s var(--ease); }
.snap-more:hover, .snap-more:focus-visible {
  background: var(--accent); color: var(--navy-900); border-color: var(--accent);
}
.snap-more[aria-expanded="true"] .snap-more-chevron { transform: rotate(-135deg) translate(-2px, -2px); }
.snap-more-chevron { transition: transform .25s var(--ease); }

.snap-step:hover, .snap-step:focus-within, .snap-step.is-active { transform: translateY(-5px); }
.snap-step:hover .snap-rail,
.snap-step:focus-within .snap-rail,
.snap-step.is-active .snap-rail { width: 100%; box-shadow: 0 0 18px -2px var(--accent); }
.snap-step:hover .snap-glyph b,
.snap-step:focus-within .snap-glyph b,
.snap-step.is-active .snap-glyph b { opacity: .24; transform: translate(-.06em, -.06em) rotate(-3deg); }
.snap-step:hover .snap-num,
.snap-step:focus-within .snap-num,
.snap-step.is-active .snap-num { background: var(--accent); color: var(--navy-900); transform: scale(1.08); }
.snap-step:hover .snap-letter,
.snap-step:focus-within .snap-letter,
.snap-step.is-active .snap-letter { transform: scale(1.1); }
.snap-step:hover .snap-position,
.snap-step:focus-within .snap-position,
.snap-step.is-active .snap-position { opacity: 1; transform: none; }

/* The colour takeover. A tint of the step's own colour washes over the card and
   its edge lights up, so the step you are on is unmistakable. The tint is kept
   light enough that the text on it still clears WCAG AA against the panel. */
.section-navy .snap-step:hover,
.section-navy .snap-step:focus-within,
.section-navy .snap-step.is-active {
  background:
    linear-gradient(165deg,
      color-mix(in srgb, var(--accent) 24%, transparent),
      color-mix(in srgb, var(--accent) 8%, transparent)),
    rgba(255, 255, 255, .06);
  border-color: color-mix(in srgb, var(--accent) 70%, transparent);
  box-shadow: 0 20px 44px -24px rgba(0, 0, 0, .85),
              0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
}

/* When one step is active the others step back, so the four read as a sequence
   with a current position rather than four things all shouting at once. Only
   on a real pointer, and only where there is room for the row. */
/* The step you are on is carried by its colour, its glow, its filled number and
   the thread running up to it — not by making the others hard to read. The
   others step back only as far as they can without any of their wording
   dropping below the WCAG AA contrast floor (measured: 5.0:1 at this value),
   and their rails dim instead. */
@media (hover: hover) and (min-width: 1000px) {
  .snap-grid:has(.snap-step:hover) .snap-step:not(:hover),
  .snap-grid:has(.snap-step.is-active) .snap-step:not(.is-active) { opacity: .85; }
  .snap-grid:has(.snap-step:hover) .snap-step:not(:hover) .snap-rail,
  .snap-grid:has(.snap-step.is-active) .snap-step:not(.is-active) .snap-rail { opacity: .5; }
  .snap-grid:has(.snap-step:hover) .snap-step:not(:hover) .snap-glyph b,
  .snap-grid:has(.snap-step.is-active) .snap-step:not(.is-active) .snap-glyph b { opacity: .05; }
}
.snap-rail { opacity: 1; transition: width .38s var(--ease), box-shadow .38s var(--ease), opacity .3s var(--ease); }

/* The thread between the cards. Each card owns the length of thread to its
   right; it fills when that card, or any card after it, is the one you are on —
   so the line grows from the first step up to wherever you are. */
.snap-seg { display: none; }
@media (min-width: 1000px) {
  .snap-seg {
    display: block; position: absolute; z-index: 3;
    top: 2.45rem; right: calc(var(--gap) * -1); width: var(--gap); height: 2px;
    background: rgba(255, 255, 255, .16); border-radius: 2px; overflow: hidden;
  }
  .snap-seg i {
    display: block; height: 100%; width: 100%;
    background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 40%, #ffffff));
    transform: scaleX(0); transform-origin: left center;
    transition: transform .45s var(--ease);
  }
  .snap-step:hover .snap-seg i,
  .snap-step:focus-within .snap-seg i,
  .snap-step.is-active .snap-seg i,
  .snap-step:has(~ .snap-step:hover) .snap-seg i,
  .snap-step:has(~ .snap-step:focus-within) .snap-seg i,
  .snap-step:has(~ .snap-step.is-active) .snap-seg i { transform: scaleX(1); }
}

/* The steps arrive one after another, left to right, so the band reads as a
   sequence rather than four things appearing at once. */
.js-reveal .snap-step[data-reveal] { transition-delay: calc(var(--i, 0) * 90ms); }

/* The letter blocks in the heading tap into place one after the other. */
.js-reveal .snap-head-block[data-reveal] .snap-blocks b {
  opacity: 0; transform: translateY(-8px) rotate(-6deg);
  transition: opacity .3s var(--ease), transform .4s var(--ease);
  transition-delay: calc(240ms + var(--i) * 90ms);
}
.js-reveal .snap-head-block[data-reveal].is-in .snap-blocks b { opacity: 1; transform: none; }

/* ---- images ------------------------------------------------------------ */
/* A soft tint behind every photo, so a slow connection shows a shape in the
   right place rather than a white hole that shifts when the image lands. */
.card-media, .media-frame, .hero-media { background: var(--cream-deep); }

/* ---- forms ------------------------------------------------------------- */
input, select, textarea { transition: border-color .16s var(--ease), box-shadow .16s var(--ease); }
input:focus, select:focus, textarea:focus { box-shadow: 0 0 0 3px var(--blue-soft); }

/* ---- reduced motion ---------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .js-reveal [data-reveal], .js-reveal [data-reveal] img { opacity: 1 !important; transform: none !important; }
  .card:hover, .btn:hover,
  .snap-step:hover, .snap-step:focus-within, .snap-step.is-active { transform: none !important; }
  .snap-step .snap-glyph b, .snap-step .snap-num, .snap-step .snap-letter { transform: none !important; }
  .snap-position { transform: none !important; }
  .js-reveal .snap-step[data-reveal] { transition-delay: 0ms !important; }
  .js-reveal .snap-head-block[data-reveal] .snap-blocks b {
    opacity: 1 !important; transform: none !important; transition-delay: 0ms !important;
  }
  .snap-seg i { transition: none !important; }
  .hero-brand-chips i, .hero-brand-aura { animation: none !important; }
  .section-flow::before { animation: none !important; }
  .card:hover .card-media img { transform: none !important; }
}

/* The header is transparent-edged at the top of the page and grows a shadow
   once content starts passing underneath it. */
.site-header { transition: box-shadow .22s var(--ease); }
.site-header.is-stuck { box-shadow: 0 2px 12px rgba(16, 48, 95, .1); }


/* ------------------------------------------------- other card-like grids -- */
/* The chooser tiles and the footer columns are laid out with their own grids,
   so they need the same stretch rule as `.grid` to line up. */
.choice-cards > *, .footer-grid > * { height: 100%; }


/* ---------------------------------------------------------------- print -- */
@media print {
  /* Nothing may stay invisible on paper, whatever the reveal script did. */
  [data-reveal], [data-reveal] img { opacity: 1 !important; transform: none !important; }
}

/* --------------------------------------------------- awaiting a photograph -- */
/* Shown where the owner has not uploaded an image yet. It is meant to look
   deliberate rather than broken, and to say plainly what to do about it. */
.photo-placeholder {
  aspect-ratio: var(--ratio-portrait);
  background:
    linear-gradient(135deg, rgba(29,111,208,.05), rgba(240,163,26,.06)),
    var(--cream-deep);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  display: grid; place-items: center; align-content: center; gap: .75rem;
  color: var(--muted); text-align: center; padding: 1.5rem;
}
.photo-placeholder svg { width: 34px; height: 34px; opacity: .5; }
/* Standing in for a photograph inside a slot that has already been sized —
   a card, a hero, a banner — so the shape of the page does not move. */
.photo-placeholder-fill { aspect-ratio: auto; width: 100%; height: 100%; min-height: 160px; }

/* -------------------------------------------------- "what can we help with" -- */
/* The chooser is the site's front door, so the eight options are the page. The
   heading, the sentence under it and the tiles are one block with one rhythm —
   previously they sat in two sections whose paddings stacked into a gap the
   height of a phone screen. */
.chooser-head { margin-bottom: clamp(2rem, 4vw, 3rem); }
.chooser-footnote { margin-top: 2.5rem; text-align: center; }
.choice-card-title { letter-spacing: .025em; }
.choice-card:focus-within, .choice-card:focus-visible { border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-soft); }
.choice-card .card-icon { transition: background-color .2s var(--ease), color .2s var(--ease), transform .2s var(--ease); }
.choice-card:hover .card-icon { background: var(--blue); color: #fff; transform: scale(1.06); }

/* ==========================================================================
   Full-bleed hero
   The photograph runs the whole width of the screen and the words sit on it.
   Everything here exists to keep that readable: a photograph is not a
   background colour, and the next one the owner uploads will be lighter or
   busier than this one.
   ========================================================================== */
.hero-full {
  position: relative;
  isolation: isolate;
  display: grid;
  align-items: end;
  /* Tall enough to feel like a front door, never taller than the screen, and
     never so tall on a phone that the words are pushed out of sight. */
  min-height: clamp(30rem, 76vh, 46rem);
  padding: 0;
  overflow: hidden;
  background: var(--navy-900);
}
.hero-full-media { position: absolute; inset: 0; z-index: -2; }
.hero-full-media .hero-full-img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center 38%;
}

/* The scrim. Two gradients rather than one flat wash: a strong one rising from
   the bottom where the words are, and a gentle one from the left so the copy
   column holds together even where the picture is bright. The photograph stays
   visible; the text stops depending on it. */
.hero-scrim {
  /* Above the photograph, not behind it. A negative z-index here would place it
     underneath its own sibling image and do nothing at all. */
  position: absolute; inset: 0; z-index: 1;
  background:
    /* A deep, wide wash under the copy column. It has to be strong enough for
       white text over the BRIGHTEST part of any photograph an owner uploads,
       not just this one — a sunlit wall behind white letters is the case that
       fails, and it fails invisibly until someone cannot read it. */
    linear-gradient(to right, rgba(6, 18, 42, .93) 0%, rgba(6, 18, 42, .88) 26%,
                              rgba(6, 18, 42, .62) 48%, rgba(6, 18, 42, .22) 72%,
                              rgba(6, 18, 42, .04) 100%),
    linear-gradient(to top, rgba(6, 18, 42, .74) 0%, rgba(6, 18, 42, .42) 34%,
                            rgba(6, 18, 42, .14) 70%, rgba(6, 18, 42, .06) 100%);
}

.hero-full-inner { position: relative; width: min(100% - 2.5rem, var(--wrap)); }
.hero-full-copy {
  max-width: 40rem;
  padding: clamp(3rem, 9vw, 5.5rem) 0 clamp(2.75rem, 7vw, 4.5rem);
  color: #fff;
}
.hero-full h1 {
  color: #fff;
  font-size: clamp(2.15rem, 4.6vw, 3.5rem);
  margin-bottom: .7rem;
  /* A soft shadow, not a hard one: it lifts the letters off a busy photograph
     without looking like a drop shadow. */
  text-shadow: 0 2px 24px rgba(6, 18, 42, .55);
}
.hero-full .eyebrow { color: var(--gold); margin-bottom: .8rem; }
.hero-full .lede {
  color: #EAF1FB; max-width: 42ch; margin-bottom: 1.9rem;
  text-shadow: 0 1px 16px rgba(6, 18, 42, .5);
}
.hero-full .btn-row { gap: .9rem; }
.hero-full :focus-visible { outline-color: var(--gold); }

.hero-full.align-center { text-align: center; justify-items: center; align-items: center; }
.hero-full.align-center .hero-full-copy { margin-inline: auto; }
.hero-full.align-center .lede { margin-inline: auto; }
.hero-full.align-center .btn-row { justify-content: center; }
.hero-full.align-center .hero-scrim {
  background: linear-gradient(to bottom, rgba(6, 18, 42, .62) 0%, rgba(6, 18, 42, .5) 50%, rgba(6, 18, 42, .78) 100%);
}

/* On a phone the picture is cropped hard by the narrow screen, so the subject
   is pulled toward the top and the words get a heavier ground underneath. */
@media (max-width: 700px) {
  .hero-full { min-height: clamp(26rem, 68vh, 34rem); }
  .hero-full-media .hero-full-img { object-position: center 30%; }
  .hero-scrim {
    background: linear-gradient(to top, rgba(6, 18, 42, .95) 0%, rgba(6, 18, 42, .82) 34%,
                                        rgba(6, 18, 42, .42) 66%, rgba(6, 18, 42, .2) 100%);
  }
  .hero-full .btn-row .btn { flex: 1 1 100%; }
}

/* The reveal animation must never touch the hero: it is the first thing on the
   page and it has to be there the instant the page paints. */
.js-reveal .hero-full [data-reveal],
.js-reveal .hero-full-media[data-reveal] { transform: none !important; }
.js-reveal .hero-full-media[data-reveal] img { opacity: 1 !important; }

/* --------------------------------------------------------- page banner --- */
/* A shorter cousin of the full-bleed hero, for pages built from a template
   rather than from content blocks. Same idea: photograph edge to edge, words on
   a gradient over it. */
.page-banner {
  position: relative; isolation: isolate; overflow: hidden;
  display: grid; align-items: end;
  min-height: clamp(18rem, 38vh, 26rem);
  background: var(--navy-900);
}
.page-banner-media { position: absolute; inset: 0; z-index: -2; }
.page-banner-media img { width: 100%; height: 100%; object-fit: cover; object-position: center 40%; }
.page-banner-media::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(to right, rgba(6,18,42,.92) 0%, rgba(6,18,42,.85) 30%, rgba(6,18,42,.55) 55%,
                              rgba(6,18,42,.2) 78%, rgba(6,18,42,.05) 100%),
    linear-gradient(to top, rgba(6,18,42,.7) 0%, rgba(6,18,42,.4) 40%, rgba(6,18,42,.12) 100%);
}
.page-banner-inner { position: relative; padding: clamp(2.5rem,6vw,4rem) 0 clamp(2rem,4.5vw,3rem); }
.page-banner h1 { color: #fff; margin-bottom: .5rem; text-shadow: 0 2px 22px rgba(6,18,42,.55); }
.page-banner .lede { color: #E4ECF8; max-width: 58ch; margin-bottom: 0; text-shadow: 0 1px 14px rgba(6,18,42,.5); }

/* The line above a banner headline sits on a solid block of the brand navy
   rather than straight on the photograph.

   Gold printed on a picture is only ever as readable as whatever happens to be
   behind it, and behind it is a different part of the room on every page. On a
   block it is readable every time, on every picture, at every width — and a
   block is the academy's own shape. */
.page-banner .eyebrow,
.hero-cover-eyebrow {
  display: inline-block;
  background: #081A3D;
  color: #FFC65C;
  padding: .38rem .7rem;
  border-radius: 5px;
  margin-bottom: .9rem;
  box-shadow: 0 2px 18px rgba(4, 14, 34, .38);
}
/* On a phone the banner words run the full width of the screen, so a gradient
   that fades to almost nothing on the right leaves the supporting line sitting
   on bright photograph — on the contact page's picture that measured 4.24:1
   against the brightest pixel behind it, under the 4.5:1 the standard asks for.
   At this width the shade is flat and dark across the whole frame instead of
   raked in from the left. */
@media (max-width: 720px) {
  .page-banner-media::after {
    background:
      linear-gradient(to top,
        rgba(6, 18, 42, .90) 0%,
        rgba(6, 18, 42, .78) 45%,
        rgba(6, 18, 42, .58) 100%);
  }
}

.page-banner :focus-visible { outline-color: var(--gold); }
.page-banner + .section, .page-banner + .section-tight { padding-top: clamp(2.5rem, 5vw, 4rem); }

/* ==========================================================================
   Depth and warmth
   The page was correct but flat: every band the same cream, every card the same
   white rectangle, nothing to catch the eye between one section and the next.
   None of this adds decoration for its own sake — each piece gives the eye a
   reason to move down the page.
   ========================================================================== */

/* ---- section bands ------------------------------------------------------ */
/* A very soft warm wash rather than one flat colour, so a long page does not
   read as a single sheet of paper. */
.section-cream {
  background:
    radial-gradient(1200px 400px at 12% 0%, rgba(240, 163, 26, .07), transparent 60%),
    radial-gradient(900px 380px at 92% 100%, rgba(29, 111, 208, .06), transparent 62%),
    var(--cream-deep);
}
.section-navy {
  background:
    radial-gradient(900px 420px at 8% 0%, rgba(29, 111, 208, .38), transparent 64%),
    radial-gradient(760px 380px at 96% 96%, rgba(240, 163, 26, .16), transparent 60%),
    var(--navy);
}

/* A thin brand rule under every section heading — small, but it gives each
   block a beginning instead of text simply starting. */
.section-head > h2::after {
  content: '';
  display: block;
  width: 3.25rem; height: 4px;
  margin-top: .85rem;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--gold), var(--red) 45%, var(--blue));
}
.section-head.center > h2::after,
.section-head.prose > h2::after { margin-inline: auto; }
.section-navy .section-head > h2::after { background: linear-gradient(90deg, var(--gold), #F5C46A); }

/* ---- cards -------------------------------------------------------------- */
/* A coloured edge that only appears on hover: at rest the row stays calm, and
   the card the visitor is pointing at clearly belongs to the brand. */
.card { position: relative; }
.card::after {
  content: '';
  position: absolute; left: 0; right: 0; top: 0; height: 4px;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: linear-gradient(90deg, var(--blue), var(--gold));
  opacity: 0; transition: opacity .22s var(--ease);
  pointer-events: none;
}
.card:hover::after { opacity: 1; }
.card-media + * { position: relative; }

/* The little icon tiles were pale to the point of invisibility, and then still
   too quiet once the drawings were redone. The tile now carries a proper tint
   and a gold hairline under it, so a row of them reads as a row of objects
   rather than nine faint smudges, and the drawing inside sits in the brand
   navy rather than a mid blue. */
.card-icon {
  background: linear-gradient(145deg, #DCEAFA, #BCD8F4);
  color: var(--navy-800);
  box-shadow:
    inset 0 0 0 1px rgba(16, 48, 95, .18),
    0 2px 0 0 var(--gold),
    0 3px 10px rgba(16, 48, 95, .12);
}
.card:hover .card-icon {
  background: linear-gradient(145deg, var(--blue), var(--navy-600));
  color: #fff;
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, .2),
    0 2px 0 0 var(--gold),
    0 6px 16px rgba(16, 48, 95, .22);
}
.card-icon, .card:hover .card-icon {
  transition: background .24s var(--ease), color .24s var(--ease), box-shadow .24s var(--ease);
}

/* ---- the programme cards ------------------------------------------------ */
/* The When / Where / Cost lines are the most-read part of the card for a
   parent, so they are set apart instead of running on as more grey text. */
.program-meta {
  background: var(--cream);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: .85rem 1rem;
  margin-top: .6rem;
  gap: .45rem;
}
.section-cream .program-meta { background: var(--white); }

/* ---- the S.N.A.P. method ------------------------------------------------ */
/* The four letters were the same size as body text sitting on a dark panel.
   They are the framework's whole idea, so they are given the room to say so. */
.snap-step { padding: 1.8rem 1.5rem; }
.snap-rail { height: 5px; }
.snap-letter {
  font-size: 3.1rem;
  line-height: .95;
  letter-spacing: -.03em;
  margin-bottom: .35rem;
}
.snap-glyph b { font-size: 10rem; }
.section-navy .snap-step {
  background: linear-gradient(160deg, rgba(255,255,255,.09), rgba(255,255,255,.045));
  border-color: rgba(255,255,255,.18);
  backdrop-filter: blur(2px);
}

/* Four across at this width: the steps happen in order and there is room to
   show the thread running between them. */
@media (min-width: 1000px) {
  .snap-grid { grid-template-columns: repeat(4, 1fr); }
}

/* ---- calls to action ---------------------------------------------------- */
.section-navy .btn-gold { box-shadow: 0 8px 26px rgba(240, 163, 26, .3); }

/* ---- quiet details ------------------------------------------------------ */
.status-badge { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .05); }
.site-footer { border-top: 4px solid transparent; border-image: linear-gradient(90deg, var(--red), var(--gold) 34%, var(--green) 67%, var(--blue)) 1; }

/* Links sitting on a photograph.
   Body links are a deep blue that works on cream and is unreadable on a dark
   banner. The FAQs intro has one inside it ("if your question is not here, just
   ask"), which is exactly the kind of thing that slips through until a contrast
   check catches it. */
.page-banner a:not(.btn),
.hero-full a:not(.btn) {
  color: #CFE1F8;
  text-decoration-color: rgba(207, 225, 248, .55);
}
.page-banner a:not(.btn):hover,
.hero-full a:not(.btn):hover { color: #fff; text-decoration-color: #fff; }

/* ------------------------------------------------------- closing prompt -- */
/* The call to action that ends a page. Centred, roomy, and the only thing in
   its band — a prompt competing with three other elements is not a prompt. */
.page-cta { padding-block: clamp(3rem, 6vw, 4.5rem); }
.page-cta h2 { margin-bottom: .6rem; }
.page-cta .section-head > h2::after, .page-cta h2::after { margin-inline: auto; }
.page-cta .lede { max-width: 46ch; margin-inline: auto; margin-bottom: 1.75rem; }

/* ===========================================================================
   The full-screen banner
   The photograph is the whole panel. Instead of a flat sheet laid over all of
   it, a band of navy fades across from the left: the words sit on something
   solid, the room stays bright on the right, and the top of the site is no
   longer uniformly dark. Every text colour over it is measured against the
   actual pixels behind it, not guessed.
   =========================================================================== */
.hero-cover {
  position: relative; isolation: isolate;
  /* The header is sticky and about 68px tall, so the banner fills what is left
     of the screen. svh rather than vh: on a phone, vh includes the space the
     browser's own toolbar is sitting in, which pushes the buttons off-screen. */
  min-height: min(calc(100svh - 68px), 900px);
  display: grid; align-items: center;
  padding-block: clamp(3rem, 8vh, 6rem);
  background: var(--navy-900);
  overflow: hidden;
}
.hero-cover-media { position: absolute; inset: 0; z-index: 0; }
.hero-cover-media img,
.hero-cover-img {
  width: 100%; height: 100%; object-fit: cover; object-position: center 38%;
  display: block;
}
/* The wash. Deep navy on the left where the words are, clearing by the middle
   so the photograph is a photograph again, with a soft floor so the buttons and
   the scroll cue keep their footing over a bright patch. */
/* The stops are fixed lengths, not percentages. A percentage moves with the
   width of the screen, which is how the words ended up over a bright part of
   the picture at 1024px while looking fine at 1440px. Measured in rem, the
   dark band always covers the column the words are in; a wide screen simply
   gets more bright photograph to the right of it. */
.hero-cover-wash {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(100deg,
      rgba(6, 20, 47, .95) 0,
      rgba(6, 20, 47, .93) 30rem,
      rgba(7, 23, 52, .74) 46rem,
      rgba(8, 26, 61, .34) 62rem,
      rgba(8, 26, 61, .10) 80rem,
      rgba(8, 26, 61, .06) 100%),
    linear-gradient(180deg, rgba(6, 20, 47, .38) 0%, rgba(6, 20, 47, 0) 26%),
    linear-gradient(0deg, rgba(6, 20, 47, .46) 0%, rgba(6, 20, 47, 0) 30%);
}

/* ---------------------------------------------------------------------------
   The home banner: the whole photograph, with the words straight on it.

   There was a version of this with the copy on a solid card. It read well and
   it was bulletproof, but it sat on the picture like a sticker and the owner
   did not want it. The words are back on the photograph itself.

   What holds them up is a pool of shade rather than a panel: a single soft
   ellipse of navy, anchored over the column the words occupy and fading out
   long before the middle of the frame, so it reads as light falling off across
   the room instead of a dark slab laid over a third of it.

   The ellipse is measured in rem, not per cent. A percentage moves with the
   width of the screen, which is how an earlier version ended up with the
   headline over a bright patch at 1024px while looking fine at 1440px. In rem
   the pool always covers the same column of text; a wider screen simply gets
   more bright photograph to the right of it.
   --------------------------------------------------------------------------- */
/* The pool of navy the words sit in.

   It used to reach 64rem across, which was fine while the banner photograph
   had an empty left third. The photograph it carries now has people standing
   edge to edge, and at that width four of the children spent their life in the
   dark — the exact fault the old flat sheet was removed for.

   So the pool is tighter and falls away faster: it still covers the headline
   and the lede, and it is off the right-hand two thirds of the picture
   altogether. What it gives up in contrast the words take back in their own
   shadow, below. */
.hero-cover:not(.hero-cover-page) .hero-cover-wash {
  background:
    radial-gradient(42rem 36rem at 11rem 54%,
      rgba(5, 16, 38, .90) 0,
      rgba(5, 16, 38, .84) 15rem,
      rgba(5, 16, 38, .60) 25rem,
      rgba(5, 16, 38, .24) 34rem,
      rgba(5, 16, 38, 0) 44rem),
    linear-gradient(180deg, rgba(6, 20, 47, .28) 0%, rgba(6, 20, 47, 0) 16%),
    linear-gradient(0deg, rgba(6, 20, 47, .34) 0%, rgba(6, 20, 47, 0) 22%);
}
.hero-cover:not(.hero-cover-page) .hero-cover-copy { max-width: min(44rem, 100%); }
/* Each line carries its own soft shadow as well, so a bright pixel that slips
   through the pool still has something behind the letterform. */
.hero-cover:not(.hero-cover-page) .hero-cover-lede { text-shadow: 0 1px 16px rgba(4, 14, 34, .7); }
.hero-cover:not(.hero-cover-page) .hero-cover-title { text-shadow: 0 2px 22px rgba(4, 14, 34, .55); }
.hero-cover:not(.hero-cover-page) .hero-cover-eyebrow { text-shadow: 0 1px 12px rgba(4, 14, 34, .6); }

.hero-cover-inner { position: relative; z-index: 1; }
/* Wide enough for the two buttons to sit side by side and for the four
   framework words to stay on one line; the reading measure is held on the
   supporting line underneath instead, where it actually matters. */
.hero-cover-copy { max-width: min(47rem, 100%); }


.hero-cover-title {
  /* Headings across the site capitalise every word, which turns "and" into
     "And" in the middle of a sentence. The banner headline is a sentence the
     owner wrote, so it is printed exactly as they wrote it. */
  text-transform: none;
  color: #fff; margin-bottom: .75rem;
  text-wrap: balance;
  text-shadow: 0 2px 20px rgba(4, 14, 34, .5);
}
/* The four framework words, each initial in its colour from the logo. The
   initials are lighter shades of the brand colours, chosen so each one clears
   WCAG AA against the navy wash behind it. */
.hero-cover-title.is-snap { display: flex; flex-wrap: wrap; gap: .1em .45em; }
.hero-cover-title .hcw { white-space: nowrap; }
.hero-cover-title .hcw b { font-weight: inherit; color: var(--c, #fff); }
.hero-cover-title .hcw:nth-child(1) b { --c: #FF9A8E; }
.hero-cover-title .hcw:nth-child(2) b { --c: #8FC4FF; }
.hero-cover-title .hcw:nth-child(3) b { --c: #FFC65C; }
.hero-cover-title .hcw:nth-child(4) b { --c: #7BDDA6; }

/* Headline words that carry a brand colour. Same four shades as the framework
   letters, each measured against the navy wash behind them. */
.hero-cover-title.is-accented .hcx { color: var(--c, #fff); }
.hero-cover-title.is-accented .hcx:nth-of-type(4n+1) { --c: #FF8B7B; }
.hero-cover-title.is-accented .hcx:nth-of-type(4n+2) { --c: #7FBBFF; }
.hero-cover-title.is-accented .hcx:nth-of-type(4n+3) { --c: #FFC65C; }
.hero-cover-title.is-accented .hcx:nth-of-type(4n+4) { --c: #7BDDA6; }

.hero-cover-lede { color: #E3ECF8; max-width: 46ch; margin-bottom: 1.9rem; }

/* The logo blocks, shown only when the headline is not already the four words. */
.hero-cover-blocks { display: flex; gap: .4rem; margin-top: 2rem; }
.hero-cover-blocks b {
  width: 2.1rem; height: 2.1rem; border-radius: 7px; display: grid; place-items: center;
  font-family: var(--font-body); font-weight: 800; font-size: .95rem; line-height: 1;
  color: #fff; background: var(--c, var(--blue));
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .18);
}
.hero-cover-blocks b:nth-child(1) { --c: #C8392D; }
.hero-cover-blocks b:nth-child(2) { --c: #1D6FD0; }
.hero-cover-blocks b:nth-child(3) { --c: #D98C0A; color: #26170A; }
.hero-cover-blocks b:nth-child(4) { --c: #257A48; }

@keyframes hero-scroll {
  0%, 100% { transform: translateY(0); opacity: .9; }
  50%      { transform: translateY(11px); opacity: .25; }
}

/* ---- the same banner, at about half the height ------------------------- */
/* Used on the pages inside the site. The full-screen treatment belongs to the
   home page: somebody who has clicked through to Donate or to an interest list
   should be able to see the heading and the start of the content at once,
   rather than scrolling past a whole screen of photograph first. */
.hero-cover-page {
  min-height: min(52svh, 520px);
  padding-block: clamp(2.25rem, 5vh, 3.5rem);
}
.hero-cover-page .hero-cover-title { font-size: clamp(2rem, 4.4vw, 3.1rem); }
.hero-cover-page .hero-cover-lede { margin-bottom: 0; }
.hero-cover-page .hero-cover-copy { max-width: min(44rem, 100%); }
@media (max-width: 899px) {
  .hero-cover-page { min-height: min(46svh, 420px); }
}

/* Links that are not buttons sitting on the banner need the lighter blue that
   the rest of the site uses on dark bands. */
.hero-cover a:not(.btn) { color: #CFE1F8; text-decoration-color: rgba(207, 225, 248, .55); }
.hero-cover a:not(.btn):hover { color: #fff; text-decoration-color: #fff; }

@media (max-width: 899px) {
  /* On a phone the wash runs top-to-bottom instead of left-to-right: there is
     no "left half" to put the words in, so the picture is darkened from the
     bottom up and the words sit in that. */
  .hero-cover { min-height: min(calc(100svh - 62px), 760px); align-items: end; }
  /* On a phone the words sit in the bottom half, so the picture is darkened
     from roughly the middle down and the words sit in that. */
  .hero-cover-wash {
    background:
      linear-gradient(180deg,
        rgba(6, 20, 47, .52) 0%,
        rgba(6, 20, 47, .26) 20%,
        rgba(6, 20, 47, .70) 42%,
        rgba(6, 20, 47, .90) 58%,
        rgba(6, 20, 47, .95) 100%);
  }
  /* A page banner is less than half the height of the home one, so its words sit
     where that gradient is still nearly clear. On a light photograph — the
     contact page's, for one — that left the supporting line at 4.24:1 against
     the brightest pixel behind it, just under the 4.5:1 the standard asks for.
     A page banner gets its own, flatter wash: never clear anywhere the words
     can reach. */
  .hero-cover-page .hero-cover-wash {
    background:
      linear-gradient(180deg,
        rgba(6, 20, 47, .74) 0%,
        rgba(6, 20, 47, .68) 35%,
        rgba(6, 20, 47, .82) 72%,
        rgba(6, 20, 47, .92) 100%);
  }
  /* A portrait window onto a wide photograph can only be moved sideways — the
     height already fills exactly. It is aimed at the S.N.A.P. Academy banner on
     the wall, which is the most recognisable part of the room at this size. */
  .hero-cover-media img, .hero-cover-img { object-position: 50% 50%; }
  .hero-cover-copy { max-width: none; }
  /* On a phone there is no "left half" to pool the shade in, so it runs from
     the bottom up instead: the words sit in the lower part of the screen and
     the top of the picture stays bright. */
  .hero-cover:not(.hero-cover-page) .hero-cover-wash {
    background:
      linear-gradient(180deg,
        rgba(5, 16, 38, .40) 0%,
        rgba(5, 16, 38, .14) 22%,
        rgba(5, 16, 38, .62) 44%,
        rgba(5, 16, 38, .88) 62%,
        rgba(5, 16, 38, .94) 100%);
  }
  .hero-cover:not(.hero-cover-page) .hero-cover-copy { padding: 0; }
  .hero-cover:not(.hero-cover-page) .hero-cover-eyebrow { margin-bottom: .55rem; font-size: .72rem; }
  .hero-cover:not(.hero-cover-page) .hero-cover-title {
    font-size: clamp(1.7rem, 7.4vw, 2.25rem); margin-bottom: .5rem;
  }
  .hero-cover:not(.hero-cover-page) .hero-cover-lede {
    font-size: 1rem; line-height: 1.48; margin-bottom: 1.05rem;
  }
  /* Two clean full-width bars rather than two short buttons stranded on the
     left: easier to hit with a thumb, and it keeps the block compact. */
  .hero-cover:not(.hero-cover-page) .btn-row { gap: .5rem; }
  .hero-cover:not(.hero-cover-page) .btn-row .btn {
    flex: 1 1 100%; width: 100%; padding: .7rem 1rem; font-size: .82rem; min-height: 48px;
  }
  .hero-cover:not(.hero-cover-page) .hero-cover-blocks { margin-top: 1.1rem; gap: .35rem; }
  .hero-cover:not(.hero-cover-page) .hero-cover-blocks b { width: 1.75rem; height: 1.75rem; font-size: .8rem; }
  /* A phone shows a slice of the banner, not the banner. Holding the crop a
     little right of centre keeps the group itself in that slice rather than the
     building behind them. Written for one photograph and still right for the
     one that replaced it — worth re-checking in a browser whenever the banner
     changes, because nothing here can check it for you. */
  .hero-cover:not(.hero-cover-page) .hero-cover-media img,
  .hero-cover:not(.hero-cover-page) .hero-cover-img { object-position: 60% 50%; }
}

/* ---------------------------------------------------------------------------
   The home banner on a phone: photograph, then card.

   A phone screen is tall and narrow and the photograph is wide. Filling the
   screen with it meant cropping it to a sliver down the middle, and in the
   owner's current photograph that sliver cut out the teacher altogether. Then
   the bottom half was darkened so the words could sit on it, which left very
   little of the picture to look at.

   So on a phone the banner stops trying to be one thing. The photograph sits at
   the top, nearly whole, bright and un-shaded, drifting very slowly so it feels
   alive. The words go on a solid navy card that rises over the bottom of the
   picture, with the four colours of the logo blocks running along its top edge
   as the join between the two. Nothing is printed over the faces, and the text
   is on a solid colour, so it is readable whatever the photograph is.
   --------------------------------------------------------------------------- */
@media (max-width: 899px) {
  .hero-cover:not(.hero-cover-page) {
    display: flex; flex-direction: column;
    min-height: 0; padding: 0;
    background: var(--navy-900);
  }
  .hero-cover:not(.hero-cover-page) .hero-cover-media {
    position: relative; inset: auto;
    aspect-ratio: 4 / 3;
    overflow: hidden;
  }
  /* Aimed a little left of centre: that keeps the teacher, the children around
     her and the screen on the wall all in frame. */
  .hero-cover:not(.hero-cover-page) .hero-cover-media img,
  .hero-cover:not(.hero-cover-page) .hero-cover-img {
    object-position: 40% 42%;
    transform-origin: 40% 45%;
    animation: hero-drift 22s ease-in-out infinite alternate;
  }
  /* No shade over the picture any more, only a short fade at the very bottom
     so it melts into the card instead of ending on a hard line. */
  .hero-cover:not(.hero-cover-page) .hero-cover-wash {
    background: linear-gradient(0deg,
      rgba(5, 16, 38, .85) 0,
      rgba(5, 16, 38, .35) 14%,
      rgba(5, 16, 38, 0) 30%);
  }
  .hero-cover:not(.hero-cover-page) .hero-cover-inner {
    position: relative; z-index: 2;
    margin-top: -2.4rem;
    width: 100%; max-width: none;
    padding: 1.9rem 1.25rem 2.1rem;
    background: var(--navy-900);
    border-radius: 1.5rem 1.5rem 0 0;
    box-shadow: 0 -14px 34px rgba(5, 16, 38, .35);
  }
  /* The four logo colours, as a stripe along the top of the card. */
  .hero-cover:not(.hero-cover-page) .hero-cover-inner::before {
    content: ''; position: absolute; top: 0; left: 1.5rem; right: 1.5rem; height: 5px;
    border-radius: 0 0 5px 5px;
    background: linear-gradient(90deg,
      #C8392D 0 25%, #1D6FD0 25% 50%, #D98C0A 50% 75%, #257A48 75% 100%);
  }
  /* A little grab-handle, the shape a phone user already reads as "this is a
     card on top of something". */
  .hero-cover:not(.hero-cover-page) .hero-cover-inner::after {
    content: ''; position: absolute; top: .75rem; left: 50%; width: 2.6rem; height: 4px;
    margin-left: -1.3rem; border-radius: 4px; background: rgba(255, 255, 255, .18);
  }
  .hero-cover:not(.hero-cover-page) .hero-cover-lede { text-shadow: none; }
  .hero-cover:not(.hero-cover-page) .hero-cover-blocks { justify-content: center; }
}

/* On a tablet the card is wide; the buttons do not need to be. */
@media (min-width: 601px) and (max-width: 899px) {
  .hero-cover:not(.hero-cover-page) .hero-cover-inner { padding: 2.2rem 2.5rem 2.4rem; }
  .hero-cover:not(.hero-cover-page) .btn-row { max-width: 30rem; }
  .hero-cover:not(.hero-cover-page) .hero-cover-blocks { justify-content: flex-start; }
}

@keyframes hero-drift {
  from { transform: scale(1); }
  to   { transform: scale(1.07) translate(1.5%, -1%); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-cover-media img, .hero-cover-img { animation: none !important; transform: none !important; }
}

/* ===========================================================================
   Brand colour as structure
   The four logo colours are used to tell one thing apart from another — which
   card you are on, where one section ends and the next begins — rather than
   being sprinkled on for decoration. Every pairing below is checked against
   WCAG AA for the text that sits on it.
   =========================================================================== */

/* ---- section heading rule ---------------------------------------------- */
/* The short bar under a section heading takes the section's own colour, so the
   page reads as a sequence of rooms rather than one long scroll. */
.section-head h2::after { background: var(--section-accent, var(--gold)); }
.section-cream { --section-accent: #C8412F; }
.section-white { --section-accent: #1D6FD0; }
.section-navy  { --section-accent: var(--gold); }
.section-gold  { --section-accent: #237A46; }

/* ---- the seam between two sections -------------------------------------- */
/* A hairline of the four colours where one band meets the next. It replaces the
   plain grey rule that used to sit there. */
.section + .section:not([class*="section-"]) {
  border-top: 0; position: relative;
}
.section + .section:not([class*="section-"])::before {
  content: ''; position: absolute; inset: 0 auto auto 0; height: 2px; width: 100%;
  background: linear-gradient(90deg,
    #D8483C 0 25%, #1D6FD0 25% 50%, #E9A31A 50% 75%, #2E8F55 75% 100%);
  opacity: .5;
}

/* ---- cards ------------------------------------------------------------- */
/* Each card in a row takes the next colour in the sequence, so a row of five
   reads as five distinct things at a glance. The colour is carried by a rail
   that grows across the top on hover and by the icon tile — never by the text,
   which stays at full contrast. */
.grid .card { --card-accent: #1D6FD0; --card-tint: var(--blue-soft); --card-ink: #14559F; position: relative; }
.grid .card:nth-child(4n+1) { --card-accent: #D8483C; --card-tint: var(--red-soft);   --card-ink: #96271A; }
.grid .card:nth-child(4n+2) { --card-accent: #1D6FD0; --card-tint: var(--blue-soft);  --card-ink: #14559F; }
.grid .card:nth-child(4n+3) { --card-accent: #E9A31A; --card-tint: var(--gold-soft);  --card-ink: #7A5104; }
.grid .card:nth-child(4n+4) { --card-accent: #2E8F55; --card-tint: var(--green-soft); --card-ink: #1D6B41; }

/* The rail is on the card from the start — it is what tells one card apart from
   the next — and runs the full width when the card is pointed at or focused. */
.grid .card::before {
  content: ''; position: absolute; inset: 0 auto auto 0; z-index: 3;
  height: 4px; width: 34%; background: var(--card-accent);
  border-radius: 0 4px 4px 0;
  transition: width .38s var(--ease);
}
.grid .card:hover::before, .grid .card:focus-within::before { width: 100%; }
.grid .card:has(.card-link):hover, .grid .card:has(a):hover {
  border-color: color-mix(in srgb, var(--card-accent) 45%, transparent);
}
.grid .card:focus-within {
  border-color: var(--card-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--card-accent) 22%, transparent), var(--shadow);
}
.grid .card .card-icon { background: var(--card-tint); color: var(--card-ink); }
.grid .card .card-link { color: var(--card-ink); }
.card-icon { transition: transform .3s var(--ease); }
.grid .card:hover .card-icon { transform: translateY(-2px) rotate(-4deg); }

/* A card with a photograph keeps the rail visible over the image. */
.grid .card:has(.card-media)::before { z-index: 4; }

/* ---- links ------------------------------------------------------------- */
/* The underline grows into place rather than simply changing colour. */
main a:not(.btn):not(.card-link):not(.snap-more) {
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  transition: color .16s var(--ease), text-decoration-color .16s var(--ease),
              text-decoration-thickness .16s var(--ease);
}
main a:not(.btn):not(.card-link):not(.snap-more):hover {
  text-decoration-color: currentColor; text-decoration-thickness: 2px;
}

/* ---- buttons ----------------------------------------------------------- */
.btn { position: relative; overflow: hidden; }
.btn-primary:hover { box-shadow: 0 10px 26px -10px rgba(29, 111, 208, .7); }
.btn-gold:hover { box-shadow: 0 10px 26px -10px rgba(240, 163, 26, .75); }

/* ---- navigation -------------------------------------------------------- */
/* The current page is marked with a growing brand-coloured underline instead of
   a tinted pill, so the header stays quiet until you look for your place. */
.site-nav a[aria-current="page"], .site-nav a.is-current { position: relative; }
.site-nav a::after {
  content: ''; position: absolute; left: .85rem; right: .85rem; bottom: .3rem; height: 2px;
  background: var(--blue); border-radius: 2px;
  transform: scaleX(0); transform-origin: center; transition: transform .25s var(--ease);
}
.site-nav a:hover::after, .site-nav a[aria-current="page"]::after { transform: scaleX(1); }

/* ---- statistics -------------------------------------------------------- */
.stat { --stat-accent: #1D6FD0; }
.stat:nth-child(4n+1) { --stat-accent: #C83428; }
.stat:nth-child(4n+2) { --stat-accent: #1C6BC8; }
.stat:nth-child(4n+3) { --stat-accent: #966008; }
.stat:nth-child(4n+4) { --stat-accent: #237A46; }
.stat strong, .stat .stat-value { color: var(--stat-accent); }

/* ---- the timeline ------------------------------------------------------ */
.timeline li::before { background: var(--step-accent, var(--navy)); }
.timeline li:nth-child(4n+1) { --step-accent: #C8412F; }
.timeline li:nth-child(4n+2) { --step-accent: #1D6FD0; }
.timeline li:nth-child(4n+3) { --step-accent: #8A5B04; }
.timeline li:nth-child(4n+4) { --step-accent: #237A46; }

/* ---- reduced motion ---------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .grid .card::before { transition: none !important; }
  .grid .card:hover .card-icon { transform: none !important; }
  .site-nav a::after { transition: none !important; }
  .snap-step .snap-rail, .snap-seg i { transition: none !important; }
}

/* A safety net for the same thing anywhere else: a facts panel with nothing in
   it is not drawn at all. */
.program-meta:empty { display: none; }


/* ---- the Donate button in the header ------------------------------------ */
/* The one button in the header that is asking for something rather than
   offering it, so it is the one that is allowed to catch the eye: a warm
   gradient in the brand's red and gold, a heart that beats once when you point
   at it, and a soft glow that grows underneath. The gradient colours are dark
   enough that the white lettering on them clears WCAG AA. */
.btn-donate {
  background: linear-gradient(135deg, #B8301F 0%, #C8392D 44%, #A56507 100%);
  color: #fff;
  border-color: transparent;
  box-shadow: 0 6px 18px -8px rgba(200, 57, 45, .8);
  position: relative;
  overflow: hidden;
}
.btn-donate:hover, .btn-donate:focus-visible {
  color: #fff;
  box-shadow: 0 10px 26px -8px rgba(200, 57, 45, .85);
  transform: translateY(-1px);
}
.btn-donate-heart { display: inline-grid; place-items: center; line-height: 0; }
.btn-donate-heart svg { display: block; }
/* One beat when the pointer arrives, rather than a heart that pulses forever
   in the corner of the screen while somebody is trying to read. */
.btn-donate:hover .btn-donate-heart { animation: donate-beat .7s var(--ease); }
@keyframes donate-beat {
  0%, 100% { transform: scale(1); }
  28%      { transform: scale(1.28); }
  56%      { transform: scale(.94); }
}
/* A light sweeps across the button on hover. Purely decorative and behind the
   lettering, so it never affects how readable the word is. */
.btn-donate::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 38%, rgba(255,255,255,.34) 50%, transparent 62%);
  transform: translateX(-130%);
  transition: transform .65s var(--ease);
}
.btn-donate:hover::after, .btn-donate:focus-visible::after { transform: translateX(130%); }
.btn-donate > * { position: relative; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
  .btn-donate:hover { transform: none !important; }
  .btn-donate:hover .btn-donate-heart { animation: none !important; }
  .btn-donate::after { transition: none !important; transform: translateX(-130%) !important; }
}

/* On a narrow screen the header only has room for two buttons. Donate keeps its
   place — it is the one that is easiest to lose and hardest to find again — and
   the parent portal moves into the menu, where it already has an entry. */
@media (max-width: 1100px) {
  .header-actions .btn-portal { display: none; }
}
@media (max-width: 720px) {
  .btn-donate { padding-inline: .8rem; }
}

/* ---------------------------------------------------------------------------
   The header on a phone.

   Four things were competing for about 320 pixels: the logo, a "Menu" pill,
   Donate and Get Started. The logo is the only one allowed to shrink, so it did
   — to nothing — and Get Started still ran off the right-hand edge.

   Now the logo is fixed and always shows, the two buttons sit together, and the
   menu becomes a round icon button at the far right, where people look for it.
   Its word "Menu" is still there for screen readers, only hidden from view. On
   the very narrowest phones Get Started moves into the menu, where it gets a
   full-width button of its own.
   --------------------------------------------------------------------------- */
.menu-start { display: none; }
@media (max-width: 600px) {
  .site-header .header-inner { gap: .5rem; width: min(100% - 1.5rem, 1320px); }
  .brand { flex: 0 0 auto; order: 0; }
  .brand img { height: 48px; }
  .header-actions { order: 1; margin-left: auto; gap: .4rem; }
  .header-actions .btn {
    min-height: 42px; padding-inline: .75rem; font-size: .72rem; letter-spacing: .04em;
  }
  .btn-donate { gap: .35rem; }
  .nav-toggle {
    order: 2; margin-left: 0; flex: 0 0 auto;
    width: 44px; height: 44px; min-height: 44px; padding: 0;
    justify-content: center; border-radius: 50%;
  }
  .nav-toggle-label {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}
/* The name comes back only once there is room for it beside both buttons. */
@media (max-width: 519px) {
  .brand-text { display: none; }
}
@media (min-width: 520px) and (max-width: 600px) {
  .brand { flex: 0 1 auto; min-width: 0; }
  .brand-text { min-width: 0; overflow: hidden; }
  .brand-name { overflow: hidden; text-overflow: ellipsis; }
}
@media (max-width: 359px) {
  .header-actions .btn-primary { display: none; }
  .site-header.nav-open .primary-nav .menu-start { display: block; padding: .75rem .75rem 0; }
  .menu-start .btn { width: 100%; justify-content: center; }
}

/* ===========================================================================
   A form in two steps
   Used on the contact page. Step one asks what the message is about; step two
   is the message. Until step one is answered, step two is dimmed and switched
   off, so there is never a question about where to begin. The two are joined by
   a line down the left so they read as one thing in two parts rather than two
   unrelated panels.
   =========================================================================== */
.form-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; min-width: 0; }
.form-step {
  position: relative;
  padding-left: 3.4rem;
  padding-bottom: 2rem;
}
.form-step:last-child { padding-bottom: 0; }

/* The line that joins the two steps. It runs from under the first marker to the
   second one, and fills with colour once step one has been answered. */
.form-step:not(:last-child)::before {
  content: ''; position: absolute; left: 1.22rem; top: 2.6rem; bottom: .6rem; width: 3px;
  background: var(--line); border-radius: 3px;
}
.form-step:not(:last-child)::after {
  content: ''; position: absolute; left: 1.22rem; top: 2.6rem; width: 3px; height: 0;
  background: linear-gradient(180deg, var(--blue), var(--green));
  border-radius: 3px;
  transition: height .5s var(--ease);
}
.is-step-2 .form-step:not(:last-child)::after { height: calc(100% - 3.2rem); }

.form-step-marker {
  position: absolute; left: 0; top: 0; z-index: 1;
  width: 2.5rem; height: 2.5rem; border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--font-body); font-weight: 800; font-size: 1rem; line-height: 1;
  background: var(--white); color: var(--muted);
  border: 3px solid var(--line);
  transition: background-color .3s var(--ease), color .3s var(--ease), border-color .3s var(--ease);
}
.form-step.is-open .form-step-marker { background: var(--blue); border-color: var(--blue); color: #fff; }
/* Once step two is open, step one is done — it gets a tick instead of its number. */
.is-step-2 .form-step:first-child .form-step-marker { background: var(--green); border-color: var(--green); }
.is-step-2 .form-step:first-child .form-step-marker b { font-size: 0; }
.is-step-2 .form-step:first-child .form-step-marker b::after {
  content: ''; display: block; width: .5rem; height: .85rem;
  border-right: 3px solid #fff; border-bottom: 3px solid #fff;
  transform: rotate(45deg) translate(-1px, -2px);
}

.form-step-label {
  font-size: .76rem; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); margin: .45rem 0 .6rem;
}
.form-step.is-open .form-step-label { color: var(--blue-dark); }
.form-step-card { padding: 1.25rem 1.5rem; }
.form-step-go { margin: 1rem 0 0; }
/* With the script running, choosing a subject moves the page on by itself, so
   the button is only there for a browser that cannot do that. */
.js-steps .form-step-go { display: none; }

.form-step-waiting {
  color: var(--muted); margin: 0 0 1rem; font-size: .95rem;
}

/* Step two before it is available: dimmed, and every control inside it switched
   off by the fieldset so it cannot be typed in, tabbed to or read out as though
   it were ready. */
.form-step-2.is-waiting .form-step-fields { opacity: .45; filter: grayscale(1); }
.form-step-fields { transition: opacity .35s var(--ease), filter .35s var(--ease); }
.form-step-fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }

@media (max-width: 599px) {
  .form-step { padding-left: 2.9rem; }
  .form-step-marker { width: 2.2rem; height: 2.2rem; }
  .form-step:not(:last-child)::before,
  .form-step:not(:last-child)::after { left: 1.07rem; }
}

@media (prefers-reduced-motion: reduce) {
  .form-step::after, .form-step-fields, .form-step-marker { transition: none !important; }
}

/* ===========================================================================
   The giving chooser

   A donation page asks somebody to make three decisions at once — how much,
   how often, and whether this organisation deserves it. This band answers the
   third one beside the first two, in the academy's own published figures, so
   nobody has to go hunting for the reason while they are holding their card.

   Every piece of state here is a radio button. The highlight, the switch, and
   which line of copy each amount shows are all CSS. Nothing on this panel
   needs JavaScript to work.
   =========================================================================== */
.giving-section { padding-block: clamp(3rem, 7vw, 5.5rem); }
.giving-grid {
  display: grid; gap: clamp(1.5rem, 4vw, 3rem);
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  align-items: start;
}
@media (max-width: 899px) { .giving-grid { grid-template-columns: 1fr; } }

.giving-title {
  /* Headings across the site capitalise every word. This one is a sentence
     somebody wrote, and "Put A Child In The Room." is not that sentence. */
  text-transform: none;
  margin-bottom: .5rem;
}
.giving-lede { margin-bottom: 1.6rem; max-width: 52ch; }

/* ---- the panel itself --------------------------------------------------- */
.give {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: clamp(1.25rem, 3vw, 2rem);
  box-shadow: 0 20px 50px rgba(12, 35, 80, .10), 0 2px 6px rgba(12, 35, 80, .06);
}
.give-legend {
  font-size: .78rem; letter-spacing: .13em; text-transform: uppercase;
  font-weight: 700; color: var(--ink-soft); padding: 0; margin-bottom: .55rem;
}
.give fieldset { border: 0; padding: 0; margin: 0 0 1.4rem; }

/* The radios themselves are never seen. They are not hidden with
   display:none, which would take them out of the tab order — they sit under
   their own label, so a keyboard reaches them and the focus ring shows. */
.give-radio {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---- monthly / once ------------------------------------------------------ */
.give-switch {
  display: grid; grid-template-columns: 1fr 1fr; gap: .4rem;
  background: var(--blue-soft); border-radius: 12px; padding: .35rem;
}
.give-switch-opt {
  display: grid; gap: .1rem; justify-items: center; text-align: center;
  padding: .6rem .5rem; border-radius: 9px; cursor: pointer;
  font-weight: 700; color: var(--navy-800); font-size: .97rem;
  transition: background-color .18s var(--ease), color .18s var(--ease),
              box-shadow .18s var(--ease);
}
.give-switch-note {
  font-weight: 500; font-size: .76rem; letter-spacing: .02em; color: var(--ink-soft);
}
.give-radio:checked + .give-switch-opt {
  background: #fff; color: var(--navy-900);
  box-shadow: 0 2px 8px rgba(12, 35, 80, .14);
}
.give-radio:checked + .give-switch-opt .give-switch-note { color: var(--blue-dark); }
.give-radio:focus-visible + .give-switch-opt,
.give-radio:focus-visible + .give-chip { outline: 3px solid var(--blue); outline-offset: 2px; }

/* ---- the amounts --------------------------------------------------------- */
.give-chips { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .6rem; }
@media (max-width: 479px) { .give-chips { grid-template-columns: 1fr; } }

.give-chip {
  position: relative; display: grid; gap: .22rem; align-content: start;
  border: 2px solid var(--line); border-radius: 13px;
  padding: .85rem .9rem; cursor: pointer; background: #fff;
  transition: border-color .16s var(--ease), background-color .16s var(--ease),
              transform .16s var(--ease), box-shadow .16s var(--ease);
}
.give-chip:hover { border-color: var(--blue); transform: translateY(-1px); }
.give-amt {
  font-family: var(--font-display); font-weight: 700; font-size: 1.45rem;
  line-height: 1; color: var(--navy-900);
}
.give-imp { font-size: .84rem; line-height: 1.35; color: var(--ink-soft); }
.give-radio:checked + .give-chip {
  border-color: var(--blue); background: var(--blue-soft);
  box-shadow: 0 6px 18px rgba(29, 111, 208, .18);
}
.give-radio:checked + .give-chip .give-amt { color: var(--blue-dark); }
.give-radio:checked + .give-chip .give-imp { color: var(--navy-800); }

/* One amount is marked as the one that covers a whole place. It is a fact,
   not a nudge: it is the academy's own published monthly tuition. */
.give-flag {
  position: absolute; top: -.62rem; left: .8rem;
  font-size: .64rem; letter-spacing: .1em; text-transform: uppercase; font-weight: 800;
  background: var(--navy-900); color: #FFC65C;
  padding: .2rem .45rem; border-radius: 5px;
}
.give-chip.is-featured { border-color: var(--navy-600); }

/* Only one of the two impact lines is shown at a time, decided by the switch
   above. Two rules rather than one per amount. */
.give:has(#give-monthly:checked) .give-imp-once,
.give:has([value="monthly"]:checked) .give-imp-once { display: none; }
.give:has([value="once"]:checked) .give-imp-monthly { display: none; }

/* ---- another amount ------------------------------------------------------ */
.give-chip-other { cursor: default; gap: .4rem; }
.give-chip-other:hover { border-color: var(--line); transform: none; }
.give-other-label { font-size: .84rem; font-weight: 700; color: var(--ink-soft); cursor: pointer; }
.give-other-field {
  display: flex; align-items: center; gap: .3rem;
  font-family: var(--font-display); font-weight: 700; font-size: 1.3rem; color: var(--navy-900);
}
.give-other-input {
  width: 100%; min-width: 0; border: 0; border-bottom: 2px solid var(--line);
  background: transparent; padding: .1rem 0; border-radius: 0;
  font: inherit; color: inherit;
}
.give-other-input:focus { outline: none; border-bottom-color: var(--blue); }

/* ---- the button ---------------------------------------------------------- */
.btn-give {
  width: 100%; margin-top: .4rem; gap: .6rem;
  background: linear-gradient(135deg, #B8301F 0%, #C8392D 44%, #A56507 100%);
  color: #fff; border-color: transparent; font-size: 1rem;
}
.btn-give:hover { color: #fff; transform: translateY(-1px); box-shadow: 0 12px 28px rgba(160, 45, 30, .35); }
.give-arrow { transition: transform .18s var(--ease); }
.btn-give:hover .give-arrow { transform: translateX(3px); }
.give-small { font-size: .8rem; color: var(--ink-soft); margin: .75rem 0 0; text-align: center; }
.give-note { font-size: .82rem; color: var(--ink-soft); margin: .6rem 0 0; text-align: center; }

/* ---- where the money goes ------------------------------------------------- */
.giving-side {
  background: var(--navy-900); color: #E4ECF8; border-radius: 18px;
  padding: clamp(1.4rem, 3vw, 2rem);
}
.giving-side-title { color: #fff; font-size: clamp(1.15rem, 2vw, 1.4rem); margin-bottom: 1rem; }
.giving-facts { list-style: none; margin: 0; padding: 0; display: grid; gap: 1rem; }
.giving-facts li {
  display: grid; gap: .2rem;
  padding-left: 1.1rem; position: relative;
}
.giving-facts li::before {
  content: ''; position: absolute; left: 0; top: .5rem;
  width: 5px; height: 5px; border-radius: 2px; background: #FFC65C;
}
.giving-fact-k { font-weight: 700; color: #fff; }
.giving-fact-v { font-size: .93rem; line-height: 1.5; color: #C9D8EE; }
.giving-facts-note { color: #9FB6D6 !important; margin-top: 1.1rem; }

@media (prefers-reduced-motion: reduce) {
  .give-chip, .btn-give, .give-arrow, .give-switch-opt { transition: none !important; }
  .give-chip:hover, .btn-give:hover { transform: none !important; }
}

/* ----------------------------------------------- already signed in, step 1
   A parent who is signed in and registering another child sees a short
   confirmation panel instead of an account form. */
.signed-in-panel {
  border: 1px solid var(--line);
  border-left: 4px solid var(--blue);
  border-radius: var(--radius-sm);
  background: var(--blue-soft);
  padding: 1rem 1.15rem;
  margin-bottom: 1.25rem;
}
.signed-in-who { margin: 0 0 .5rem; font-size: 1.05rem; }
.signed-in-swap { margin-top: .4rem; }
.signed-in-swap .form-note { margin: 0; }
.linkish {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--blue-dark);
  text-decoration: underline;
  cursor: pointer;
}
.linkish:hover { text-decoration: none; }

/* -------------------------------------------- two-column fields line up
   A help line above one input in a two-column row pushed that input down while
   the one beside it stayed put, so the pair no longer lined up. Both columns
   now share the row's own grid lines — label, help, input, error — so each of
   those bands is as tall as the tallest column and the inputs always start on
   the same line. A column with no help line still occupies the help band, so
   it is held down to match the one that has it. */
@media (min-width: 720px) {
  .field-row.two {
    grid-template-rows: auto auto auto auto;
    align-items: start;
    /* The row gap belongs between the two columns, not between a label and its
       own input — the bands are spaced by the fields' own margins. */
    row-gap: 0;
    column-gap: 1rem;
  }
  .field-row.two > .field {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 4;
    align-content: start;
  }
  .field-row.two > .field > label { grid-row: 1; }
  .field-row.two > .field > .help { grid-row: 2; margin-bottom: 0; }
  .field-row.two > .field > input,
  .field-row.two > .field > select,
  .field-row.two > .field > textarea { grid-row: 3; align-self: start; margin-top: .45rem; }
  .field-row.two > .field > .field-error { grid-row: 4; }
  /* A column with no help line still takes up the help band. */
  .field-row.two > .field:not(:has(> .help))::before { content: ""; grid-row: 2; display: block; }

  /* Where subgrid is not available the columns fall back to their own grid,
     and the reserved band keeps a single-line help text lined up. */
  @supports not (grid-template-rows: subgrid) {
    .field-row.two > .field { grid-template-rows: auto auto 1fr auto; }
    .field-row.two > .field:not(:has(> .help))::before { min-height: calc(.93rem * 1.5); }
  }
}

/* ---------------------------------------------------------------------------
   The interest-meeting RSVP page.

   The owner asked for "a flyer kind of a situation", and a flyer is a good
   instinct here: the four things a parent needs before they can say yes — when,
   where, what it costs, who it is for — are the four things a printed flyer
   puts in large type and nothing else competes with. So those four sit in a
   single card, each labelled, each one line, with the date set in the display
   face at poster size.

   The card is deliberately heavier than anything else on the page. It is the
   one thing somebody will screenshot and send to the other parent.
--------------------------------------------------------------------------- */
/* The owner asked for a big hero. A page banner is normally about half the
   height of the home one; this is the page a flyer or a text message lands on,
   so the picture gets room to work before anything else starts. */
.rsvp-hero.hero-cover-page { min-height: min(68vh, 34rem); }
.rsvp-hero .hero-cover-title { font-size: clamp(2rem, 4.4vw, 3.4rem); }

/* The flyer pulls up over the foot of the banner, so the two read as one unit
   and the card is already on screen when the banner is. */
.rsvp-flyer-section { padding-top: 0; margin-top: clamp(-4rem, -5vw, -2.5rem); position: relative; z-index: 2; }

.rsvp-flyer {
  position: relative;
  max-width: 54rem;
  margin-inline: auto;
  padding: clamp(1.75rem, 4vw, 3rem);
  border-radius: var(--radius-lg);
  background: linear-gradient(165deg, var(--navy) 0%, var(--navy-900) 70%);
  color: #fff;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
/* A quiet band of brand colour along the top edge, the same four in the same
   order as the logo blocks. */
.rsvp-flyer::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 6px;
  background: linear-gradient(90deg,
    var(--red) 0 25%, var(--blue) 25% 50%, var(--gold) 50% 75%, var(--green) 75% 100%);
}

.rsvp-flyer-mark { margin-bottom: 1.25rem; }
.rsvp-flyer-blocks { display: inline-flex; gap: .3rem; }
.rsvp-flyer-blocks b {
  width: 1.9rem; height: 1.9rem; display: grid; place-items: center;
  border-radius: 6px; font: 700 .95rem/1 var(--font-body); color: #fff;
}
.rsvp-flyer-blocks b:nth-child(1) { background: var(--red); }
.rsvp-flyer-blocks b:nth-child(2) { background: var(--blue); }
.rsvp-flyer-blocks b:nth-child(3) { background: var(--gold); color: var(--navy-900); }
.rsvp-flyer-blocks b:nth-child(4) { background: var(--green); }

.rsvp-flyer-kicker {
  margin: 0 0 .35rem; text-transform: uppercase; letter-spacing: .12em;
  font: 700 .78rem/1.2 var(--font-body); color: var(--gold);
}
.rsvp-flyer-title {
  margin: 0 0 1.5rem;
  font: 700 clamp(1.9rem, 5vw, 3rem)/1.05 var(--font-display);
  color: #fff;
}

/* Two by two rather than auto-fitting. Auto-fit gave three across and one
   orphaned underneath, which reads as a mistake on a card this deliberate. */
.rsvp-flyer-facts {
  display: grid; gap: 1.5rem 3rem; margin: 0 0 1.5rem;
  grid-template-columns: 1fr;
}
@media (min-width: 42rem) {
  .rsvp-flyer-facts { grid-template-columns: 1fr 1fr; }
}
.rsvp-flyer-facts div { margin: 0; }
.rsvp-flyer-facts dt {
  text-transform: uppercase; letter-spacing: .1em;
  font: 700 .72rem/1.2 var(--font-body);
  color: rgba(255, 255, 255, .62); margin-bottom: .3rem;
}
.rsvp-flyer-facts dd { margin: 0; font-size: 1.02rem; line-height: 1.5; color: rgba(255, 255, 255, .95); }
.rsvp-flyer-facts strong { color: #fff; }

/* The address is a link, so it has to look like one on a dark background and
   keep looking like one when it wraps across two lines on a phone. */
/* Heavier than a lone class on purpose: the house style for links in body
   copy is set at `main a:not(.btn):not(.card-link):not(.snap-more)`, which was
   winning and painting these a 40%-opacity blue — invisible on navy. */
main a.rsvp-flyer-address:not(.btn):not(.card-link) {
  color: #fff; text-decoration: underline; text-decoration-color: var(--gold);
  text-underline-offset: 3px; text-decoration-thickness: 2px;
}
main a.rsvp-flyer-address:not(.btn):not(.card-link):hover,
main a.rsvp-flyer-address:not(.btn):not(.card-link):focus-visible {
  color: var(--gold); text-decoration-color: var(--gold);
}
.rsvp-flyer-maphint {
  display: block; margin-top: .2rem;
  font-size: .82rem; color: rgba(255, 255, 255, .6);
}

.rsvp-flyer-note {
  margin: 0 0 1.5rem; padding: .85rem 1.1rem;
  border-left: 3px solid var(--gold);
  background: rgba(255, 255, 255, .07);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: .95rem; color: rgba(255, 255, 255, .88);
}
.rsvp-flyer-cta { margin: 0; }

.rsvp-form-section { padding-top: 0; }

@media (max-width: 36rem) {
  .rsvp-flyer-facts { gap: 1.1rem; grid-template-columns: 1fr; }
}

/* A link inside a paragraph of body copy — the venue address and the meeting
   date, which are written into prose the owner edits rather than sitting in
   fields of their own. Underlined rather than coloured, because this paragraph
   appears on navy as well as on cream and a colour that reads as a link on one
   disappears on the other. */
/* The selector is heavier than it looks because it has to be. Body copy links
   already have a house style set by `main a:not(.btn):not(.card-link)`, which
   is more specific than a lone class and was quietly winning: the rule below
   applied, and the underline came out thin and blue anyway. */
main a.inline-link:not(.btn):not(.card-link),
.hero-cover a.inline-link:not(.btn):not(.card-link) {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--gold);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
main a.inline-link:not(.btn):not(.card-link):hover,
main a.inline-link:not(.btn):not(.card-link):focus-visible,
.hero-cover a.inline-link:not(.btn):not(.card-link):hover {
  color: var(--gold);
  text-decoration-color: var(--gold);
}

/* A short list inside a text section, shown as labels rather than as a sentence
   with five commas in it. The speaking page listed six topics in one line of
   prose under no heading at all; the owner's note on it was "this doesn't look
   good", and the sentence was the reason — six items in a row is a list that
   has been flattened, and flattening it hides every item after the second. */
.topic-list {
  list-style: none; margin: 1.75rem auto 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center;
  max-width: 54rem;
}
.topic-list li {
  padding: .55rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--white);
  font: 600 .95rem/1.3 var(--font-body);
  color: var(--navy);
  box-shadow: var(--shadow-sm);
}
.section-navy .topic-list li {
  background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .22); color: #fff;
  box-shadow: none;
}
.section-head.prose + .topic-list { justify-content: flex-start; }

/* ---------------------------------------------------------------------------
   Team members.

   Deliberately the founder's layout repeated rather than a row of cards. A
   card would crop each biography to two lines and a "read more", and these are
   people's own accounts of their own work — the layout should not be the thing
   that edits them.
--------------------------------------------------------------------------- */
.team-list { display: grid; gap: clamp(2rem, 4vw, 3.25rem); }

.team-member {
  display: grid;
  gap: clamp(1.25rem, 3vw, 2.25rem);
  align-items: start;
}
@media (min-width: 52rem) {
  .team-member { grid-template-columns: 260px 1fr; }
}

.team-member-photo img {
  width: 100%;
  aspect-ratio: var(--ratio-portrait);
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
/* On a phone the portrait is capped, otherwise a 4:5 photo at full width
   pushes the person's name most of a screen down the page. */
@media (max-width: 52rem) {
  .team-member-photo { max-width: 15rem; }
}

.team-member-name { font-size: 1.5rem; margin: 0 0 .15rem; }
.team-member-role { margin: 0 0 1rem; }
.team-member-bio p { margin: 0 0 .9rem; }
.team-member-bio p:last-child { margin-bottom: 0; }

/* A hairline between people, so two biographies of this length do not read as
   one continuous passage. */
.team-member + .team-member {
  border-top: 1px solid var(--line);
  padding-top: clamp(2rem, 4vw, 3.25rem);
}

/* ---------------------------------------------------------------------------
   The book page: where to buy.

   One card per format, each with its own button. No prices are shown because
   none were given, and a price invented to fill a gap on a page that takes
   money is the worst kind of placeholder.
--------------------------------------------------------------------------- */
/* Narrow enough that five formats sit on one row on a desktop rather than
   four and an orphan, and still readable when they stack on a phone. */
.format-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
}
.format-card {
  display: flex; flex-direction: column; gap: .35rem;
  padding: 1.5rem 1.25rem;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  text-align: center;
}
.format-name { font: 700 1.15rem/1.2 var(--font-display); color: var(--navy); }
.format-note { font-size: .9rem; color: var(--muted); }
/* The button sits on the floor of the card, so a row of cards whose notes run
   to different numbers of lines still has its buttons in one straight line.
   `margin-top: auto` is the whole mechanism, and it has to live in the SAME
   rule as everything else that sets a top margin on this button — a second,
   more specific rule setting `margin-top: 1rem` is what put the hardcover
   button 24px above its neighbours, and it looked like a stray space rather
   than a stylesheet beating itself. The gap above the button comes from the
   note's own bottom margin instead, where nothing competes for it. */
.format-card .format-buy {
  margin-top: auto;
  padding-top: .7rem;
  padding-bottom: .7rem;
  /* Every button the same height, whether its label fits on one line or runs
     to two. The bottoms already lined up; the tops did not, and a row where
     two buttons start higher than the other two reads as a mistake even when
     the measurement says it is fine. */
  min-height: 4.85rem;
  display: grid;
  place-items: center;
}
.format-card .format-note { margin-bottom: .65rem; }

/* The cover, beside the words. Portrait, with a shadow so it reads as an
   object on the page rather than a picture pasted onto it. */
.book-cover img {
  width: 100%; max-width: 18rem;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
}

/* A picture shown whole rather than cropped to the house shape. For anything
   designed — a book cover, a poster, a flyer — where the crop would cut off
   words that are part of the artwork. */
.media-frame-whole { background: none; box-shadow: none; }
.media-frame-whole img {
  aspect-ratio: auto;
  object-fit: contain;
  width: auto;
  max-width: min(100%, 20rem);
  margin-inline: auto;
  display: block;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
}

/* ---------------------------------------------------------------------------
   Program page tables — October 2026 briefs
   ---------------------------------------------------------------------------
   A family comparing four recurring time slots against a school run, or three
   monthly rates against a budget, is doing a lookup. Prose makes a lookup
   harder than it needs to be, so these are real tables: a row heading in the
   first column, so a screen reader announces "Wednesday" before reading the
   times, and horizontal scrolling confined to the table rather than the page.
   --------------------------------------------------------------------------- */

.hero-tagline {
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 2.4vw, 1.5rem);
  font-weight: 600;
  color: var(--navy-700, var(--navy));
  margin: .35rem 0 .6rem;
  line-height: 1.25;
}

.hero-aside { margin-top: .85rem; font-size: .95rem; color: var(--ink-soft); max-width: 54ch; }

.table-wrap { overflow-x: auto; margin: 0 0 1rem; -webkit-overflow-scrolling: touch; }

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 1rem;
  min-width: 22rem;
  text-align: left;
}
.data-table th,
.data-table td {
  padding: .7rem .85rem;
  border-bottom: 1px solid var(--line, rgba(0, 0, 0, .1));
  vertical-align: top;
}
.data-table thead th {
  font-family: var(--font-display);
  font-size: .9rem;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom-width: 2px;
}
.data-table tbody th { font-weight: 700; color: var(--navy-900, var(--navy)); white-space: nowrap; }
.data-table tbody tr:last-child th,
.data-table tbody tr:last-child td { border-bottom: 0; }

.slot-pill {
  display: inline-block;
  margin: 0 .4rem .4rem 0;
  padding: .22rem .6rem;
  border-radius: 999px;
  background: var(--cream, #f7f3ea);
  border: 1px solid var(--line, rgba(0, 0, 0, .1));
  font-size: .95rem;
  white-space: nowrap;
}

.tuition-table tbody td { font-weight: 700; color: var(--navy-900, var(--navy)); }

/* The enrollment steps are a numbered sequence, and the step number is already
   written into each item, so the shared timeline's own counter is suppressed
   rather than printed twice. */
.timeline.numbered li::before { content: ''; }

@media (max-width: 560px) {
  .data-table { min-width: 0; font-size: .95rem; }
  .data-table th, .data-table td { padding: .6rem .5rem; }
}

/* The giving page with no payment processor connected. The chooser is not drawn
   at all in that state, so this is the paragraph that stands in its place. */
.giving-offline { font-size: 1.05rem; color: var(--ink-soft); max-width: 46ch; margin-bottom: 1.1rem; }

/* The Get Started chooser, in three labelled groups. */
.chooser-group + .chooser-group { margin-top: clamp(2rem, 4vw, 3rem); }
.chooser-group-title {
  font-size: clamp(1.25rem, 2.2vw, 1.6rem);
  margin-bottom: 1rem;
  padding-bottom: .5rem;
  border-bottom: 2px solid var(--line, rgba(0, 0, 0, .1));
}
.chooser-group .choice-card-title { font-size: 1.1rem; }

/* A written-out web address is one long unbreakable token. Left alone it pushed
   the giving page to twice the width of a phone screen; it is allowed to break
   wherever it has to rather than deciding the width of the page. */
.wrap-anywhere { overflow-wrap: anywhere; word-break: break-word; }
.prose p, .lede, .lede p { overflow-wrap: break-word; }
