/* =========================================================================
   Madaraka Primary School — one stylesheet, no build step, no framework.
   =========================================================================

   DESIGN READ
   A public primary school site for parents and guardians in Nairobi, in a
   warm civic-editorial language. The audience is a parent deciding whether a
   free government school is a serious one — so the page has to read as
   competent and cared-for, not as a charity appeal and not as a nursery.

   Dials: VARIANCE 7 · MOTION 5 · DENSITY 5.

   Variance is a point above the M-PESA Academy site this benchmarks against
   (mixed aspect ratios, a deliberate overlap, an off-grid editorial band);
   motion is held level, because restraint is what reads as competence to the
   audience. All asymmetry collapses to one column below 768px.

   PALETTE
   Drawn from the name. "Madaraka" is Swahili for self-governance — Kenya's
   internal self-rule, 1 June 1963 — and the estate that carries it was built
   by the City Council in the early 1970s. So: a deep green, the flag's
   crimson as the single accent, an ochre for Nairobi light, over warm white
   and a near-black that keeps a green cast. Contrast was measured, not
   eyeballed — every colour used for text clears 4.5:1 on its own ground and
   the values are recorded beside each token.

   TYPE
   Fraunces (display) over Figtree (text). BOTH ARE VENDORED as woff2 in
   assets/font — deliberately, not for speed. The M-PESA demo links Google
   Fonts, and Google Fonts is blocked on some of the networks these sites get
   shown on, so that page silently falls back to Georgia in exactly the room
   where it matters. Nothing here makes an external request.

   MOTION RULES held to throughout
     - only transform, opacity and filter are animated;
     - nothing animates from scale(0);
     - one rAF loop; no scroll handler does layout work;
     - ambient drift runs ONLY while its figure is on screen;
     - prefers-reduced-motion means gentler, not zero.
   ========================================================================= */

/* ---------------------------------------------------------------- fonts --- */

@font-face {
  font-family: 'Fraunces';
  src: url('../font/fraunces.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Figtree';
  src: url('../font/figtree.woff2') format('woff2');
  font-weight: 300 800;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------- tokens --- */

:root {
  /* Warm cream, not #fff. A near-black that keeps a warm cast, not #000. */
  --paper: #fbf8f3;
  --paper-2: #f4efe5;
  --paper-3: #e8dfcf;
  --panel: #ffffff;

  --ink: #1b1416; /* 16.9:1 on paper */
  --ink-2: #4b3c3f; /* 8.7:1  */
  --muted: #7b6b6e; /* 4.7:1  */
  --line: #e6dccc;
  --line-soft: #f0e9dc;

  /* MAROON — the uniform. A deep crimson jumper over a white Peter Pan collar
     is what this school looks like from fifty metres, so it is the colour that
     carries every action on the page. */
  --maroon: #a32639; /* 7.2:1 on paper */
  --maroon-deep: #8c1f2c; /* 9.0:1 */
  --maroon-lit: #d9606f; /* large display only on dark — 4.7:1 */
  --maroon-tint: #f8e7e6;

  /* TEAL — the buildings. Every railing, stair rail, window frame and roof
     fascia on the compound is this sea-green against cream render. It is the
     school's other half and it does the structural work: dark grounds,
     section labels, the second button. */
  --teal: #1f7a72; /* 5.1:1 on paper */
  --teal-deep: #0f5c56; /* 7.8:1 */
  --teal-lit: #5fc0b5; /* 7.7:1 on --ink-deep */
  --teal-tint: #dceeec;

  /* The dark ground, mixed from the teal rather than from black, so the dark
     sections read as the same building and not as a different site. */
  --ink-deep: #12201e;

  /* CORAL — the red-and-white gingham skirt, which reads coral at any distance.
     One accent, used rarely: the stat numbers, a chip dot, a rule. */
  --coral: #e8785f; /* 3.0:1 — fill and mark only, never text on light */
  --coral-deep: #9c4430; /* 6.1:1 on paper */
  --coral-lit: #f0947e; /* 7.4:1 on --ink-deep */
  --coral-tint: #fbe8e2;


  --e-out: cubic-bezier(0.23, 1, 0.32, 1);
  --e-io: cubic-bezier(0.77, 0, 0.175, 1);
  --e-rev: cubic-bezier(0.16, 1, 0.3, 1);

  --r-sm: 9px;
  --r-md: 15px;
  --r-lg: 24px;

  --shadow-1: 0 1px 2px rgba(27, 20, 22, 0.05), 0 6px 20px rgba(27, 20, 22, 0.05);
  --shadow-2: 0 2px 4px rgba(27, 20, 22, 0.07), 0 16px 44px rgba(27, 20, 22, 0.1);
  --shadow-3: 0 4px 10px rgba(27, 20, 22, 0.09), 0 32px 80px rgba(27, 20, 22, 0.18);

  --wrap: 1260px;
  --nav-h: 76px;
}

/* ---------------------------------------------------------------- reset --- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 110px;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: 'Figtree', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-size: 17px;
  line-height: 1.62;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

a {
  color: inherit;
  text-decoration: none;
}
button {
  font: inherit;
  color: inherit;
}

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

::selection {
  background: var(--maroon-tint);
  color: var(--ink);
}

/* Sections below the fold are skipped during layout until they approach the
   viewport. The intrinsic-size hint keeps the scrollbar honest meanwhile. */
.defer {
  content-visibility: auto;
  contain-intrinsic-size: auto 900px;
}

/* ------------------------------------------------------------ typography --- */

h1,
h2,
h3,
h4,
.display {
  font-family: 'Fraunces', ui-serif, Georgia, 'Times New Roman', serif;
  font-weight: 500;
  /* Optical sizing is the reason Fraunces is here: display sizes get the high
     contrast and tight joins, small headings get the sturdy ones, from one
     file. Tracking stays size-specific — never one value for all sizes. */
  font-optical-sizing: auto;
  letter-spacing: -0.02em;
  line-height: 1.08;
  margin: 0;
  text-wrap: balance;
}

h1 {
  font-size: clamp(2.65rem, 6.2vw, 4.9rem);
  letter-spacing: -0.035em;
  line-height: 1.02;
}
h2 {
  font-size: clamp(1.95rem, 3.9vw, 3.05rem);
  letter-spacing: -0.028em;
  line-height: 1.1;
}
h3 {
  font-size: clamp(1.22rem, 1.9vw, 1.52rem);
  letter-spacing: -0.016em;
  line-height: 1.24;
}
h4 {
  font-size: 1.05rem;
  letter-spacing: -0.008em;
  line-height: 1.3;
}

p {
  margin: 0 0 1em;
}
p:last-child {
  margin-bottom: 0;
}

.lede {
  font-size: clamp(1.05rem, 1.5vw, 1.25rem);
  line-height: 1.58;
  color: var(--ink-2);
  max-width: 60ch;
}

.muted {
  color: var(--muted);
}
.small {
  font-size: 0.88rem;
  line-height: 1.55;
}

/* The section label. A rule and a word — a masthead, not a decoration. */
.kicker {
  display: flex;
  align-items: center;
  gap: 0.85em;
  font-size: 0.71rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--maroon-deep);
  margin: 0 0 1.15rem;
}

.kicker::before {
  content: '';
  width: 26px;
  height: 2px;
  background: var(--teal);
  flex: none;
}

.on-dark .kicker,
.kicker.on-dark {
  color: var(--coral-lit);
}
.on-dark .kicker::before,
.kicker.on-dark::before {
  background: var(--coral-lit);
}

.link {
  color: var(--maroon-deep);
  font-weight: 600;
  box-shadow: inset 0 -1px 0 0 rgba(140, 31, 44, 0.35);
  transition: box-shadow 200ms var(--e-out);
}
.link:hover {
  box-shadow: inset 0 -2px 0 0 var(--maroon-deep);
}

/* Swahili, set alongside the English it translates. */
.sw {
  font-style: italic;
  color: var(--muted);
}

/* --------------------------------------------------------------- layout --- */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 26px;
}
.wrap-narrow {
  max-width: 840px;
}

section {
  position: relative;
}

.section {
  padding: clamp(76px, 9vw, 138px) 0;
}
.section-tight {
  padding: clamp(52px, 6vw, 86px) 0;
}

.section-head {
  max-width: 700px;
  margin-bottom: clamp(38px, 4.5vw, 62px);
}

/* Header left, standfirst right — asymmetric, one grid. */
.head-split {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(22px, 4vw, 68px);
  align-items: end;
  margin-bottom: clamp(38px, 4.5vw, 62px);
}

@media (max-width: 900px) {
  .head-split {
    grid-template-columns: 1fr;
    align-items: start;
  }
}

.bg-2 {
  background: var(--paper-2);
}
.bg-dark {
  background: var(--ink-deep);
  color: #dde5e0;
}
.bg-dark h2,
.bg-dark h3,
.bg-dark h4 {
  color: #fff;
}
.bg-dark .lede {
  color: rgba(228, 222, 214, 0.8);
}
.bg-dark .muted {
  color: rgba(228, 222, 214, 0.62);
}

.rule {
  height: 1px;
  background: var(--line);
  border: 0;
  margin: 0;
}

/* -------------------------------------------------------------- buttons --- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.92em 1.6em;
  border: 0;
  border-radius: 999px;
  font-size: 0.94rem;
  font-weight: 600;
  letter-spacing: 0.004em;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 200ms ease, color 200ms ease, box-shadow 220ms var(--e-out),
    transform 140ms var(--e-out);
}

.btn:active {
  transform: scale(0.97);
}

.btn svg {
  flex: none;
  transition: transform 220ms var(--e-out);
}

@media (hover: hover) and (pointer: fine) {
  .btn:hover svg.arrow {
    transform: translate(2px, -2px);
  }
}

.btn-primary {
  background: var(--maroon-deep);
  color: #fff;
  box-shadow: 0 2px 4px rgba(140, 31, 44, 0.22), 0 12px 30px rgba(140, 31, 44, 0.24);
}
.btn-primary:hover {
  background: #74131f;
}

/*
 * Over a photograph the primary button becomes glass too, because a solid slab
 * on a picture reads as a sticker. On a paper section it stays solid — maroon
 * at 72% over cream is a washed pink, and the one button that matters most on
 * the page is the wrong place to lose contrast for an effect.
 */
.hero .btn-primary,
.page-head .btn-primary {
  background: rgba(140, 31, 44, 0.86);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.3),
    0 2px 5px rgba(140, 31, 44, 0.26), 0 12px 30px rgba(140, 31, 44, 0.24);
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .hero .btn-primary,
  .page-head .btn-primary {
    background: rgba(140, 31, 44, 0.74);
    -webkit-backdrop-filter: blur(14px) saturate(1.6);
    backdrop-filter: blur(14px) saturate(1.6);
  }
}

.hero .btn-primary:hover,
.page-head .btn-primary:hover {
  background: rgba(116, 19, 31, 0.9);
}

.btn-crimson {
  background: var(--teal-deep);
  color: #fff;
  box-shadow: 0 2px 4px rgba(15, 92, 86, 0.22), 0 12px 30px rgba(15, 92, 86, 0.22);
}
.btn-crimson:hover {
  background: #0a4640;
}

.btn-ghost {
  background: transparent;
  color: var(--ink);
  box-shadow: inset 0 0 0 1.5px rgba(27, 20, 22, 0.22);
}
.btn-ghost:hover {
  background: rgba(27, 20, 22, 0.045);
  box-shadow: inset 0 0 0 1.5px rgba(27, 20, 22, 0.5);
}

.btn-ghost.on-dark {
  color: #fff;
  background: rgba(255, 255, 255, 0.1);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.26),
    inset 0 0 0 1px rgba(255, 255, 255, 0.2);
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .btn-ghost.on-dark {
    background: rgba(255, 255, 255, 0.07);
    -webkit-backdrop-filter: blur(14px) saturate(1.6);
    backdrop-filter: blur(14px) saturate(1.6);
  }
}

.btn-ghost.on-dark:hover {
  background: rgba(255, 255, 255, 0.17);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.4),
    inset 0 0 0 1px rgba(255, 255, 255, 0.4);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

/* --------------------------------------------------------- liquid glass --- */

/*
 * The frosted surface, done properly.
 *
 * The part that separates this from a cheap translucent box is the BORDER: a
 * `::before` with `padding` and two masks composited with `exclude`, so only a
 * 1.4px ring shows and that ring is a GRADIENT — bright at the top edge where
 * light would catch a real bevel, gone across the middle, bright again at the
 * bottom. A flat 1px border reads as a rectangle with an outline; this reads
 * as an edge with thickness.
 *
 * Plus an inset highlight along the top, which is the specular line on the
 * upper lip of a piece of glass, and a saturation lift in the backdrop filter,
 * because real glass concentrates the colour behind it rather than greying it.
 *
 * TWO VARIANTS, AND THE DIFFERENCE IS NOT COSMETIC.
 * `background-blend-mode: luminosity` is tuned for a DARK backdrop; over a
 * light one it pulls the backdrop's luminance through the fill and the panel
 * renders as a grey smear across whatever is behind it. So the light variant
 * sets `normal` and a white-ish fill, and swaps the ring to dark stops. And
 * light glass is NEVER stacked on a light surface — see the nav, where the
 * links pill is glass only while it is over the photograph.
 */

.glass {
  position: relative;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.06);
  background-blend-mode: luminosity;
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.22);
}

.glass::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.4px;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.5) 0%,
    rgba(255, 255, 255, 0.16) 22%,
    rgba(255, 255, 255, 0) 42%,
    rgba(255, 255, 255, 0) 58%,
    rgba(255, 255, 255, 0.14) 80%,
    rgba(255, 255, 255, 0.4) 100%
  );
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
  z-index: 0;
}

/* Anything inside has to sit above the ring. */
.glass > * {
  position: relative;
  z-index: 1;
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .glass {
    background: rgba(255, 255, 255, 0.04);
    -webkit-backdrop-filter: blur(16px) saturate(1.7);
    backdrop-filter: blur(16px) saturate(1.7);
  }
}

/* Over paper. Fill, not luminosity — see the note above. */
.glass-light {
  background: rgba(255, 255, 255, 0.55);
  background-blend-mode: normal;
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.9);
}

.glass-light::before {
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.95) 0%,
    rgba(27, 20, 22, 0.06) 40%,
    rgba(27, 20, 22, 0.06) 60%,
    rgba(27, 20, 22, 0.13) 100%
  );
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .glass-light {
    background: rgba(251, 248, 243, 0.62);
    -webkit-backdrop-filter: blur(18px) saturate(1.5);
    backdrop-filter: blur(18px) saturate(1.5);
  }
}

/* Tinted glass — the maroon still reads as the action, the glass still reads
   as glass. Used on the one button that matters most on the page. */
.glass-maroon {
  background: rgba(140, 31, 44, 0.82);
  background-blend-mode: normal;
  color: #fff;
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.3),
    0 2px 5px rgba(140, 31, 44, 0.24), 0 12px 30px rgba(140, 31, 44, 0.22);
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .glass-maroon {
    background: rgba(140, 31, 44, 0.72);
    -webkit-backdrop-filter: blur(14px) saturate(1.6);
    backdrop-filter: blur(14px) saturate(1.6);
  }
}

.glass-maroon:hover {
  background: rgba(116, 19, 31, 0.88);
}

/*
 * Reduced transparency is a separate signal from reduced motion, and it means
 * something different: not "stop moving" but "I cannot read this". So the fill
 * goes opaque and the blur goes entirely, on every variant.
 */
@media (prefers-reduced-transparency: reduce) {
  .glass,
  .glass-light,
  .glass-maroon {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }
  .glass {
    background: rgba(18, 32, 30, 0.92) !important;
  }
  .glass-light {
    background: var(--paper) !important;
  }
  .glass-maroon {
    background: var(--maroon-deep) !important;
  }
}

/* ------------------------------------------------------------------ nav --- */

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 900;
  padding: 14px 0;
  transition: background-color 300ms ease, box-shadow 300ms ease, padding 300ms var(--e-out);
}

/* Transparent over the photographic hero; an opaque paper bar once it leaves.
   The FILL swaps, not just the text colour — light glass over a light page is
   a grey smear. */
.nav.is-stuck {
  background: rgba(251, 248, 243, 0.94);
  box-shadow: 0 1px 0 rgba(27, 20, 22, 0.07), 0 10px 30px rgba(27, 20, 22, 0.06);
  padding: 8px 0;
}

/* A hairline of light along the bottom lip of the bar, so the glass has an
   edge rather than just stopping. */
.nav.is-stuck::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    rgba(27, 20, 22, 0) 0%,
    rgba(27, 20, 22, 0.1) 18%,
    rgba(27, 20, 22, 0.1) 82%,
    rgba(27, 20, 22, 0) 100%
  );
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .nav.is-stuck {
    background: rgba(251, 248, 243, 0.6);
    -webkit-backdrop-filter: blur(20px) saturate(1.6);
    backdrop-filter: blur(20px) saturate(1.6);
  }
}

.nav-inner {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 26px;
  display: flex;
  align-items: center;
  gap: 18px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-right: auto;
  color: #fff;
  transition: color 300ms ease;
}
.nav.is-stuck .brand {
  color: var(--ink);
}

.brand .crest {
  width: 46px;
  height: 46px;
  flex: none;
}

.brand-name {
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-weight: 600;
  font-size: 1.04rem;
  letter-spacing: -0.015em;
  line-height: 1.1;
}

.brand-sub {
  display: block;
  font-family: 'Figtree', sans-serif;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  opacity: 0.72;
  margin-top: 3px;
}

/*
 * A floating pill, glass while it is over the photograph.
 *
 * It carries `.glass` in the markup, and that is switched OFF once the bar
 * itself turns to glass — light glass stacked on light glass is the grey smear
 * the material rules warn about, and it would land directly under the brand.
 */
.nav-links {
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 4px 6px;
  border-radius: 999px;
  transition: background-color 300ms ease, box-shadow 300ms ease;
}

.nav.is-stuck .nav-links {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: none;
}

.nav.is-stuck .nav-links::before {
  opacity: 0;
}

.nav-links a {
  padding: 0.55em 0.9em;
  border-radius: 999px;
  font-size: 0.9rem;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.88);
  transition: color 180ms ease, background-color 180ms ease;
}
.nav-links a:hover {
  color: #fff;
  background: rgba(255, 255, 255, 0.14);
}
.nav-links a[aria-current='page'] {
  color: #fff;
  background: rgba(255, 255, 255, 0.18);
}

.nav.is-stuck .nav-links a {
  color: var(--ink-2);
}
.nav.is-stuck .nav-links a:hover {
  color: var(--ink);
  background: rgba(27, 20, 22, 0.06);
}
.nav.is-stuck .nav-links a[aria-current='page'] {
  color: var(--maroon-deep);
  background: var(--maroon-tint);
}

.nav-cta {
  padding: 0.66em 1.2em;
  font-size: 0.87rem;
}

.nav-toggle {
  display: none;
  position: relative;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  cursor: pointer;
}
.nav.is-stuck .nav-toggle {
  background: rgba(27, 20, 22, 0.07);
}

.nav-toggle span {
  position: absolute;
  left: 12px;
  width: 20px;
  height: 2px;
  border-radius: 2px;
  background: #fff;
  transition: transform 300ms var(--e-out), opacity 180ms ease, top 300ms var(--e-out),
    background-color 300ms ease;
}
.nav.is-stuck .nav-toggle span {
  background: var(--ink);
}

.nav-toggle span:nth-child(1) {
  top: 16px;
}
.nav-toggle span:nth-child(2) {
  top: 21px;
}
.nav-toggle span:nth-child(3) {
  top: 26px;
}

.nav-toggle[aria-expanded='true'] span:nth-child(1) {
  top: 21px;
  transform: rotate(45deg);
}
.nav-toggle[aria-expanded='true'] span:nth-child(2) {
  opacity: 0;
  transform: scaleX(0.4);
}
.nav-toggle[aria-expanded='true'] span:nth-child(3) {
  top: 21px;
  transform: rotate(-45deg);
}

@media (max-width: 1040px) {
  .nav-links,
  .nav-cta {
    display: none;
  }
  .nav-toggle {
    display: block;
  }
}

/* Fullscreen overlay menu. opacity + visibility, never display:none. */
.menu {
  position: fixed;
  inset: 0;
  z-index: 890;
  /* Opaque, for the same reason the lightbox is: a blur under a 98% fill is a
     full-viewport composite nobody can see. */
  background: #12201e;
  color: #fff;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 100px 30px 40px;
  opacity: 0;
  visibility: hidden;
  transition: opacity 300ms var(--e-out), visibility 300ms;
}
.menu.is-open {
  opacity: 1;
  visibility: visible;
}

.menu a {
  display: block;
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-weight: 500;
  font-size: clamp(1.85rem, 7.5vw, 2.9rem);
  letter-spacing: -0.03em;
  line-height: 1.55;
  color: #fff;
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 400ms var(--e-out), transform 400ms var(--e-out);
}
.menu.is-open a {
  opacity: 1;
  transform: none;
}

.menu-foot {
  margin-top: auto;
  padding-top: 28px;
  font-size: 0.9rem;
  color: rgba(228, 222, 214, 0.7);
  opacity: 0;
  transition: opacity 400ms var(--e-out) 240ms;
}
.menu.is-open .menu-foot {
  opacity: 1;
}

.skip {
  position: absolute;
  left: 16px;
  top: -60px;
  z-index: 999;
  background: var(--maroon-deep);
  color: #fff;
  padding: 10px 16px;
  border-radius: 8px;
  transition: top 180ms var(--e-out);
}
.skip:focus {
  top: 16px;
}

/* ----------------------------------------------------------------- hero --- */

.hero {
  position: relative;
  min-height: min(94svh, 920px);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  background: var(--ink-deep);
  padding: 150px 0 clamp(48px, 6vw, 84px);
  isolation: isolate;
}

.hero-media {
  position: absolute;
  inset: 0;
  z-index: -2;
}

.hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 38%;
  /* A single slow push-in, once. It is the only ambient motion above the
     fold, and it ends — a permanently breathing hero is a slow loop near
     0.2 Hz, which is the thing reduced-motion guidance names by hand. */
  animation: heroDrift 30s var(--e-out) forwards;
  will-change: transform;
}

@keyframes heroDrift {
  from {
    transform: scale(1.085);
  }
  to {
    transform: scale(1);
  }
}

/*
 * Two shallow gradients, not one steep one. A single dark overlay reads as a
 * black box laid on a photograph; two gradients keyed to the corner the copy
 * sits in read as light falling off. No backdrop-filter — that would frost
 * the photograph, and the photograph is the point.
 */
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(
      to top,
      rgba(14, 26, 24, 0.96) 0%,
      rgba(14, 26, 24, 0.78) 30%,
      rgba(14, 26, 24, 0.34) 62%,
      rgba(14, 26, 24, 0.52) 100%
    ),
    linear-gradient(
      to right,
      rgba(14, 26, 24, 0.82) 0%,
      rgba(14, 26, 24, 0.34) 52%,
      rgba(14, 26, 24, 0.04) 82%
    );
}

.hero .wrap {
  position: relative;
  z-index: 1;
}

.hero-inner {
  max-width: 980px;
  color: #fff;
}

.hero h1 {
  color: #fff;
  margin-bottom: 0.4em;
}

.hero .lede {
  color: rgba(255, 255, 255, 0.87);
  max-width: 56ch;
  margin-bottom: 2rem;
}

.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 7px 16px 7px 7px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.13);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
  font-size: 0.79rem;
  font-weight: 500;
  letter-spacing: 0.015em;
  color: rgba(255, 255, 255, 0.92);
  margin-bottom: 1.5rem;
}

.hero-badge b {
  display: inline-flex;
  padding: 4px 11px;
  border-radius: 999px;
  background: var(--maroon-deep);
  color: #fff;
  font-weight: 700;
  font-size: 0.69rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

@media (max-width: 620px) {
  .hero-badge {
    gap: 8px;
    padding: 5px 12px 5px 5px;
    font-size: 0.72rem;
    line-height: 1.35;
    margin-bottom: 1.1rem;
  }
  .hero-badge b {
    padding: 3px 9px;
    font-size: 0.62rem;
    letter-spacing: 0.05em;
  }
}

.hero-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 44px;
  margin-top: 2.4rem;
  padding-top: 1.7rem;
  border-top: 1px solid rgba(255, 255, 255, 0.22);
}

.hero-facts div {
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.74);
  max-width: 23ch;
}

.hero-facts b {
  display: block;
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-optical-sizing: auto;
  font-size: 1.72rem;
  font-weight: 500;
  letter-spacing: -0.03em;
  color: #fff;
  line-height: 1.05;
  margin-bottom: 3px;
  font-variant-numeric: tabular-nums;
}

/* A photographic page header for the interior pages. */
.page-head {
  position: relative;
  min-height: min(56svh, 520px);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  background: var(--ink-deep);
  padding: 160px 0 clamp(38px, 4.5vw, 62px);
  isolation: isolate;
}

.page-head .hero-media img {
  animation: none;
  transform: none;
}

.page-head h1 {
  color: #fff;
  font-size: clamp(2.25rem, 5vw, 3.8rem);
  max-width: 17ch;
  margin-bottom: 0.4em;
}

.page-head .lede {
  color: rgba(255, 255, 255, 0.85);
  max-width: 56ch;
}

.page-head .wrap {
  position: relative;
  z-index: 1;
}

/* ---------------------------------------------------------------- stats --- */

.stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(20px, 3vw, 44px);
}

.stat b {
  display: block;
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-optical-sizing: auto;
  font-size: clamp(2.4rem, 4.6vw, 3.5rem);
  font-weight: 500;
  letter-spacing: -0.042em;
  line-height: 1;
  color: var(--maroon-deep);
  font-variant-numeric: tabular-nums;
}

.bg-dark .stat b {
  color: var(--coral-lit);
}

.stat span {
  display: block;
  margin-top: 0.6em;
  padding-top: 0.6em;
  border-top: 1px solid var(--line);
  font-size: 0.87rem;
  color: var(--muted);
  line-height: 1.4;
}

.bg-dark .stat span {
  border-color: rgba(255, 255, 255, 0.18);
  color: rgba(228, 222, 214, 0.72);
}

@media (max-width: 820px) {
  .stats {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ---------------------------------------------------------- feature rows --- */

/* The workhorse: a photograph beside a block of text, alternating side. The
   column ratio deliberately varies between instances so a page of these does
   not read as a template. */
.feature {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
  gap: clamp(30px, 5vw, 78px);
  align-items: center;
}

.feature.wide-media {
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
}

.feature.flip > :first-child {
  order: 2;
}

.feature-body {
  max-width: 56ch;
}

@media (max-width: 900px) {
  .feature,
  .feature.wide-media {
    grid-template-columns: 1fr;
  }
  .feature.flip > :first-child {
    order: 0;
  }
}

/* ------------------------------------------------------------ photographs --- */

/*
 * A photograph with a caption. `figure` + `figcaption`, so it is a figure to
 * a screen reader too.
 *
 * The slow drift lives on the IMAGE and the click affordance on the FIGURE,
 * which is not arbitrary: a running CSS animation beats a transition on the
 * same property, so a hover-scale on the image would simply never apply. Two
 * nested elements keep the two transforms independent.
 */
.shot {
  position: relative;
  margin: 0;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--paper-3);
  isolation: isolate;
}

.shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  aspect-ratio: 4 / 3;
  transform: scale(1.06);
}

.shot.tall img {
  aspect-ratio: 3 / 4;
}
.shot.wide img {
  aspect-ratio: 16 / 9;
}
.shot.square img {
  aspect-ratio: 1 / 1;
}
.shot.portrait img {
  aspect-ratio: 4 / 5;
}

/*
 * The ambient zoom. `.is-drifting` is added by the observer when the figure
 * is on screen and removed when it leaves, so nothing animates off-screen and
 * a long page is not running twenty compositor animations at once.
 */
.shot.is-drifting img {
  animation: kenBurns 30s ease-in-out infinite alternate;
  will-change: transform;
}

@keyframes kenBurns {
  from {
    transform: scale(1.06) translate3d(0, 0, 0);
  }
  to {
    transform: scale(1.14) translate3d(-1.1%, -1.4%, 0);
  }
}

/* Every other figure drifts the other way, so a grid of them never pulses in
   unison — which is what makes ambient motion read as a screensaver. */
.shot.drift-alt.is-drifting img {
  animation-name: kenBurnsAlt;
  animation-duration: 36s;
}

@keyframes kenBurnsAlt {
  from {
    transform: scale(1.12) translate3d(1.2%, 0.8%, 0);
  }
  to {
    transform: scale(1.04) translate3d(0, 0, 0);
  }
}

.shot figcaption {
  position: absolute;
  overflow: hidden;
  left: 14px;
  bottom: 14px;
  right: 14px;
  padding: 8px 14px;
  border-radius: 999px;
  background: rgba(18, 32, 30, 0.74);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: #fff;
  font-size: 0.79rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  width: fit-content;
  max-width: calc(100% - 28px);
  z-index: 2;
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.18);
}

.shot figcaption::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.42) 0%,
    rgba(255, 255, 255, 0) 45%,
    rgba(255, 255, 255, 0) 60%,
    rgba(255, 255, 255, 0.28) 100%
  );
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

/* An accent slab tucked against a photograph — the one deliberate overlap. */
.shot-badge {
  position: absolute;
  right: -1px;
  top: -1px;
  z-index: 2;
  padding: 14px 20px;
  background: var(--coral);
  color: #3a2705;
  border-bottom-left-radius: var(--r-lg);
}

.shot-badge b {
  display: block;
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-optical-sizing: auto;
  font-size: 1.5rem;
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.shot-badge span {
  font-size: 0.69rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  opacity: 0.8;
}

/* ---- the click affordance on an openable photograph ---- */

.shot[data-open] {
  cursor: zoom-in;
}

.shot[data-open]::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(18, 32, 30, 0);
  transition: background-color 260ms var(--e-out);
  pointer-events: none;
}

.shot-expand {
  position: absolute;
  top: 13px;
  left: 13px;
  z-index: 2;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: rgba(18, 32, 30, 0.6);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: #fff;
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 240ms var(--e-out), transform 240ms var(--e-out);
  pointer-events: none;
}

@media (hover: hover) and (pointer: fine) {
  .shot[data-open]:hover::after {
    background: rgba(18, 32, 30, 0.16);
  }
  .shot[data-open]:hover .shot-expand,
  .shot[data-open]:focus-within .shot-expand {
    opacity: 1;
    transform: scale(1);
  }
}

/* On touch there is no hover to reveal it, and "you can tap this" has to be
   visible without one. */
@media (hover: none) {
  .shot[data-open] .shot-expand {
    opacity: 0.92;
    transform: none;
  }
}

/* The whole figure is one button. A transparent overlay button keeps the
   hit area honest and the accessible name on something real. */
.shot-hit {
  position: absolute;
  inset: 0;
  z-index: 3;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: zoom-in;
  font-size: 0;
}

/* --------------------------------------------------------------- mosaic --- */

/* An asymmetric photograph grid — one tall, one wide, one big, the rest
   square, so it reads as an edit rather than a contact sheet. */
.mosaic {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 196px;
  gap: 14px;
}

.mosaic .shot {
  height: 100%;
}
.mosaic .shot img {
  height: 100%;
  aspect-ratio: auto;
}

.mosaic .m-tall {
  grid-row: span 2;
}
.mosaic .m-wide {
  grid-column: span 2;
}
.mosaic .m-big {
  grid-column: span 2;
  grid-row: span 2;
}

@media (max-width: 900px) {
  .mosaic {
    grid-template-columns: repeat(2, 1fr);
    grid-auto-rows: 162px;
  }
}
@media (max-width: 520px) {
  .mosaic {
    grid-template-columns: 1fr;
  }
  .mosaic .m-wide,
  .mosaic .m-big {
    grid-column: span 1;
  }
  .mosaic .m-tall,
  .mosaic .m-big {
    grid-row: span 1;
  }
}

/* ------------------------------------------------------------- lightbox --- */

/*
 * Tap a photograph and it opens full-screen and begins to drift.
 *
 * Built rather than borrowed: no dependency, one instance reused for every
 * image on the page, and the drift is the point — a still image at full
 * screen is a dead end, a slowly moving one invites you to look at it.
 */
.lb {
  position: fixed;
  inset: 0;
  /*
   * Above the KAVRYN widget, which mounts `.kv-root` at 2147483000. A modal
   * that a floating launcher can sit on top of is not a modal — measured
   * here: the photograph counter rendered underneath the chat button.
   */
  z-index: 2147483001;
  display: grid;
  grid-template-rows: 1fr auto;
  /*
   * A solid fill, and deliberately NOT frosted glass.
   *
   * This was `rgba(8,14,11,0.94)` over `backdrop-filter: blur(18px)`. At 94%
   * opacity there is essentially nothing left to blur — the effect was
   * invisible — while a full-viewport backdrop-filter is one of the most
   * expensive things a page can composite, and it is over the exact element
   * whose whole job is to show a photograph at full size. Keeping the look and
   * dropping the filter costs nothing and buys back the frame.
   */
  background: #0b1413;
  opacity: 0;
  visibility: hidden;
  transition: opacity 320ms var(--e-out), visibility 320ms;
}

.lb.is-open {
  opacity: 1;
  visibility: visible;
}

.lb-stage {
  position: relative;
  display: grid;
  place-items: center;
  padding: clamp(40px, 6vh, 76px) clamp(16px, 6vw, 96px) 8px;
  overflow: hidden;
  min-height: 0;
}

.lb-frame {
  position: relative;
  max-width: 100%;
  max-height: 100%;
  border-radius: var(--r-md);
  overflow: hidden;
  box-shadow: var(--shadow-3);
  background: #0b120e;
  line-height: 0;
}

.lb-frame img {
  max-width: 100%;
  /* The stage's own padding is already subtracted by the grid; capping the
     picture in vh as well is what stops a portrait image pushing the caption
     off the bottom of a short window. */
  max-height: calc(100svh - 168px);
  width: auto;
  height: auto;
  transform: scale(1.02);
  opacity: 0;
  transition: opacity 420ms var(--e-out);
}

.lb.is-ready .lb-frame img {
  opacity: 1;
  animation: lbDrift 34s ease-in-out infinite alternate;
  will-change: transform;
}

@keyframes lbDrift {
  from {
    transform: scale(1.02) translate3d(0, 0, 0);
  }
  to {
    transform: scale(1.1) translate3d(-1.2%, -1%, 0);
  }
}

.lb-bar {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px clamp(16px, 4vw, 34px) clamp(18px, 3vh, 30px);
  color: rgba(255, 255, 255, 0.9);
}

.lb-cap {
  flex: 1;
  min-width: 0;
  font-size: 0.92rem;
  line-height: 1.45;
}

.lb-cap b {
  display: block;
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-optical-sizing: auto;
  font-weight: 500;
  font-size: 1.06rem;
  letter-spacing: -0.015em;
  color: #fff;
}

.lb-count {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.lb-btn {
  width: 46px;
  height: 46px;
  flex: none;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 999px;
  color: #fff;
  cursor: pointer;
  transition: background-color 200ms ease, transform 140ms var(--e-out);
}
.lb-btn:hover {
  background: rgba(255, 255, 255, 0.2);
}
.lb-btn:active {
  transform: scale(0.94);
}

.lb-close {
  position: absolute;
  top: clamp(14px, 2.5vh, 26px);
  right: clamp(14px, 4vw, 30px);
  z-index: 4;
}

.lb-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 4;
}
.lb-nav.prev {
  left: clamp(8px, 2vw, 26px);
}
.lb-nav.next {
  right: clamp(8px, 2vw, 26px);
}
.lb-nav:active {
  transform: translateY(-50%) scale(0.94);
}

body.lb-open {
  overflow: hidden;
}

/*
 * Hide the KAVRYN launcher while the viewer is open.
 *
 * It is fixed to the bottom-right, which is exactly where the photograph
 * counter sits. Reaching into the widget's own class is a soft coupling and a
 * deliberate one: it is CSS only, it is reversible the instant the class comes
 * off, and if the widget ever renames `.kv-root` this rule quietly does
 * nothing rather than breaking. The chip handler in site.js already couples to
 * the widget's public DOM the same way.
 *
 * `!important` because the widget injects its own stylesheet after this one
 * and sets `display` on the same element — this is the case the keyword is
 * actually for, overriding third-party CSS you do not own, and it is scoped to
 * a body class that exists only while a modal is open.
 */
body.lb-open .kv-root {
  display: none !important;
}

@media (max-width: 640px) {
  .lb-frame img {
    max-height: calc(100svh - 196px);
  }
  .lb-nav.prev {
    left: 6px;
  }
  .lb-nav.next {
    right: 6px;
  }
}

/* ---------------------------------------------------------------- cards --- */

.grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}
.grid-2 {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 20px;
}
.grid-4 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 18px;
}

@media (max-width: 1000px) {
  .grid-4 {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 900px) {
  .grid-3,
  .grid-2 {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 560px) {
  .grid-4 {
    grid-template-columns: 1fr;
  }
}

.card {
  position: relative;
  background: var(--panel);
  border-radius: var(--r-md);
  box-shadow: inset 0 0 0 1px var(--line), var(--shadow-1);
  padding: clamp(26px, 2.8vw, 36px);
  transition: transform 320ms var(--e-out), box-shadow 320ms var(--e-out);
}

.bg-dark .card {
  background: rgba(255, 255, 255, 0.05);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);
}

@media (hover: hover) and (pointer: fine) {
  .card-lift:hover {
    transform: translateY(-4px);
    box-shadow: inset 0 0 0 1px var(--line), var(--shadow-2);
  }
}

.card h3 {
  margin-bottom: 0.5em;
}
.card p {
  color: var(--ink-2);
  font-size: 0.97rem;
}
.bg-dark .card p {
  color: rgba(228, 222, 214, 0.78);
}

.card-num {
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-optical-sizing: auto;
  font-size: 0.98rem;
  font-weight: 600;
  color: var(--teal-deep);
  letter-spacing: 0.06em;
  margin-bottom: 1.4rem;
  display: block;
  font-variant-numeric: tabular-nums;
}
.bg-dark .card-num {
  color: var(--coral-lit);
}

.icon-badge {
  width: 46px;
  height: 46px;
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
  margin-bottom: 1.2rem;
  background: var(--maroon-tint);
  color: var(--maroon-deep);
}
.icon-badge.crimson {
  background: var(--teal-tint);
  color: var(--teal-deep);
}
.icon-badge.amber {
  background: var(--coral-tint);
  color: var(--coral-deep);
}

/* ---------------------------------------------------- the grades ladder --- */

/*
 * Kenya's 2-6-3-3-3 structure, as a ladder a parent can read left to right.
 * Deliberately NOT cards: this is one continuous path through a school, and
 * boxes would break it into four unrelated things.
 */
.ladder {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
  border-top: 2px solid var(--line);
}

.rung {
  position: relative;
  padding: 28px 26px 30px 0;
  border-right: 1px solid var(--line);
}
.rung:last-child {
  border-right: 0;
  padding-right: 0;
}
.rung:not(:first-child) {
  padding-left: 26px;
}

.rung::before {
  content: '';
  position: absolute;
  top: -2px;
  left: 0;
  width: 42%;
  height: 2px;
  background: var(--teal);
}
.rung:not(:first-child)::before {
  left: 26px;
}

.rung .yrs {
  display: block;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--teal-deep);
  margin-bottom: 0.8rem;
}

.rung h3 {
  margin-bottom: 0.45em;
}

.rung p {
  color: var(--ink-2);
  font-size: 0.93rem;
  margin: 0;
}

.rung .exam {
  display: inline-block;
  margin-top: 1rem;
  padding: 4px 11px;
  border-radius: 999px;
  background: var(--maroon-tint);
  color: var(--maroon-deep);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.rung.is-outside .yrs,
.rung.is-outside::before {
  color: var(--muted);
  background: var(--line);
}
.rung.is-outside {
  opacity: 0.72;
}

@media (max-width: 1000px) {
  .ladder {
    grid-template-columns: repeat(2, 1fr);
  }
  .rung:nth-child(2n) {
    border-right: 0;
    padding-right: 0;
  }
  .rung:nth-child(n + 3) {
    border-top: 1px solid var(--line);
    margin-top: -1px;
  }
}
@media (max-width: 620px) {
  .ladder {
    grid-template-columns: 1fr;
  }
  .rung {
    border-right: 0;
    padding: 26px 0 28px;
    border-top: 1px solid var(--line);
  }
  .rung:first-child {
    border-top: 0;
  }
  .rung:not(:first-child) {
    padding-left: 0;
  }
  .rung:not(:first-child)::before {
    left: 0;
  }
}

/* ----------------------------------------------------- weighting figure --- */

/*
 * How a Grade 9 result is made up. One stacked bar, direct-labelled, because
 * these are parts of a single whole — three separate bars would invite a
 * comparison between them that means nothing.
 *
 * The colour ramp is one hue light-to-dark. It encodes size, not identity, so
 * a categorical palette here would imply three unrelated things.
 */
.figure-block {
  margin: 0;
  padding: clamp(24px, 3vw, 36px);
  background: var(--panel);
  border-radius: var(--r-md);
  box-shadow: inset 0 0 0 1px var(--line), var(--shadow-1);
}

.weights {
  display: flex;
  height: 54px;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px var(--line);
}

.weights span {
  display: grid;
  place-items: center;
  color: #fff;
  font-size: 0.86rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 900ms var(--e-rev);
}

.is-in .weights span {
  transform: scaleX(1);
}

.weights span:nth-child(1) {
  flex: 60;
  background: var(--maroon-deep);
  transition-delay: 0ms;
}
.weights span:nth-child(2) {
  flex: 20;
  background: #2f9366;
  transition-delay: 140ms;
}
.weights span:nth-child(3) {
  flex: 20;
  background: #79bf9c;
  color: #10352a;
  transition-delay: 280ms;
}

.weights-key {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 26px;
  margin-top: 1.1rem;
  font-size: 0.88rem;
  color: var(--ink-2);
}

.weights-key b {
  font-weight: 600;
  color: var(--ink);
}

/* ---------------------------------------------------------------- steps --- */

.steps {
  counter-reset: step;
}

.step {
  display: grid;
  grid-template-columns: 74px minmax(0, 1fr);
  gap: clamp(16px, 2.4vw, 34px);
  padding: clamp(24px, 3vw, 34px) 0;
  border-top: 1px solid var(--line);
}

.step:last-child {
  border-bottom: 1px solid var(--line);
}

.step::before {
  counter-increment: step;
  content: counter(step, decimal-leading-zero);
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-optical-sizing: auto;
  font-size: 1.5rem;
  font-weight: 500;
  color: var(--teal);
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

.step h3 {
  margin-bottom: 0.4em;
}
.step p {
  color: var(--ink-2);
  max-width: 64ch;
  margin: 0;
}

@media (max-width: 560px) {
  .step {
    grid-template-columns: 1fr;
    gap: 8px;
  }
}

/* ------------------------------------------------------------- timeline --- */

.timeline {
  position: relative;
  padding-left: 34px;
  margin: 0;
}

.timeline::before {
  content: '';
  position: absolute;
  left: 7px;
  top: 10px;
  bottom: 10px;
  width: 2px;
  background: linear-gradient(to bottom, var(--teal), var(--maroon), var(--paper-3));
}

.timeline li {
  position: relative;
  list-style: none;
  padding: 0 0 32px;
}
.timeline li:last-child {
  padding-bottom: 0;
}

.timeline li::before {
  content: '';
  position: absolute;
  left: -34px;
  top: 9px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--paper);
  box-shadow: inset 0 0 0 3px var(--maroon);
}

.timeline .year {
  display: block;
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-optical-sizing: auto;
  font-weight: 600;
  font-size: 1.3rem;
  color: var(--maroon-deep);
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
}

.timeline p {
  margin: 0.3em 0 0;
  color: var(--ink-2);
  max-width: 62ch;
}

/* ---------------------------------------------------------------- table --- */

.table-card {
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--panel);
  box-shadow: inset 0 0 0 1px var(--line), var(--shadow-1);
}

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.96rem;
}

table.data caption {
  text-align: left;
  padding: 20px clamp(18px, 2.6vw, 28px) 0;
  font-size: 0.82rem;
  color: var(--muted);
}

table.data th,
table.data td {
  padding: 14px clamp(14px, 2.2vw, 28px);
  text-align: left;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
}

table.data thead th {
  font-family: 'Figtree', sans-serif;
  font-size: 0.71rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--paper-2);
}

table.data tbody th {
  font-weight: 600;
}

table.data tbody th small {
  display: block;
  font-weight: 400;
  font-size: 0.8rem;
  color: var(--muted);
}

table.data tr:last-child th,
table.data tr:last-child td {
  border-bottom: 0;
}

/* Numbers are tabular so a column of them lines up and can be scanned. */
.num {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}

@media (max-width: 640px) {
  table.data .hide-sm {
    display: none;
  }
}

/* ------------------------------------------------------------ provenance --- */

/*
 * The label a figure carries when it is not the school's own statement.
 * Deliberately legible, not a whisper — this site is built on public records
 * about a real school, and every one of them says so where it appears.
 */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 0.67rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  background: var(--maroon-tint);
  color: var(--maroon-deep);
}

.tag-check {
  background: var(--coral-tint);
  color: var(--coral-deep);
}

.tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-top: 1rem;
}

/* A source note under a block of sourced facts. */
.source {
  margin-top: 1.4rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  font-size: 0.81rem;
  line-height: 1.55;
  color: var(--muted);
  max-width: 68ch;
}
.bg-dark .source {
  border-color: rgba(255, 255, 255, 0.16);
  color: rgba(228, 222, 214, 0.6);
}

/* ------------------------------------------------------------------ faq --- */

.faq details {
  border-bottom: 1px solid var(--line);
}
.faq details:first-child {
  border-top: 1px solid var(--line);
}

.faq summary {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding: 21px 0;
  cursor: pointer;
  list-style: none;
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-optical-sizing: auto;
  font-size: clamp(1.06rem, 1.8vw, 1.2rem);
  font-weight: 500;
  letter-spacing: -0.016em;
  line-height: 1.35;
  transition: color 180ms ease;
}

.faq summary::-webkit-details-marker {
  display: none;
}
.faq summary:hover {
  color: var(--maroon-deep);
}

.faq summary::after {
  content: '';
  flex: none;
  width: 24px;
  height: 24px;
  margin-top: 3px;
  border-radius: 999px;
  background: var(--maroon-tint);
  background-image: linear-gradient(var(--maroon-deep) 0 0), linear-gradient(var(--maroon-deep) 0 0);
  background-size: 10px 1.8px, 1.8px 10px;
  background-position: center, center;
  background-repeat: no-repeat;
  transition: transform 280ms var(--e-out);
}

.faq details[open] summary::after {
  transform: rotate(135deg);
}

.faq details p {
  padding: 0 56px 22px 0;
  margin: 0;
  color: var(--ink-2);
  max-width: 70ch;
  animation: faqIn 300ms var(--e-out) both;
}

.faq details p + p {
  padding-top: 0.9em;
}

@keyframes faqIn {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

/* -------------------------------------------------------------- contact --- */

.contact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(226px, 1fr));
  gap: 18px;
}

.contact-card {
  display: block;
  padding: clamp(22px, 2.6vw, 30px);
  border-radius: var(--r-md);
  background: var(--panel);
  box-shadow: inset 0 0 0 1px var(--line);
  transition: transform 260ms var(--e-out), box-shadow 260ms var(--e-out);
}

@media (hover: hover) and (pointer: fine) {
  a.contact-card:hover {
    transform: translateY(-3px);
    box-shadow: inset 0 0 0 1px var(--maroon), var(--shadow-1);
  }
}

.contact-card b {
  display: block;
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-optical-sizing: auto;
  font-size: 1.14rem;
  font-weight: 500;
  margin: 0.8rem 0 0.2rem;
  overflow-wrap: anywhere;
}

.contact-card span {
  font-size: 0.88rem;
  color: var(--muted);
}

.hours {
  list-style: none;
  margin: 0;
  padding: 0;
}

.hours li {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 11px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 0.95rem;
}
.hours li:last-child {
  border-bottom: 0;
}

.hours li[data-today] {
  font-weight: 700;
  color: var(--maroon-deep);
}

.hours li[data-today] .day-name::after {
  content: 'today';
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--maroon-tint);
  font-size: 0.64rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  vertical-align: 1px;
}

/* ------------------------------------------------------------------ map --- */

/*
 * The map is a flat WebP rendered from OpenStreetMap's own vector data in
 * this site's palette (build/render-map.py), with no labels baked in — every
 * label and the marker are HTML on top, so they stay crisp at any zoom, can
 * be selected, and are reachable by a screen reader.
 */
.map {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--paper-2);
  box-shadow: inset 0 0 0 1px var(--line);
  aspect-ratio: 14 / 9;
}

.map > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.map-pin {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 22px;
  height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
  background: var(--maroon-deep);
  box-shadow: 0 0 0 4px rgba(251, 248, 243, 0.95), 0 4px 14px rgba(27, 20, 22, 0.28);
}

.map-pin::after {
  content: '';
  position: absolute;
  inset: -9px;
  border-radius: 50%;
  border: 2px solid var(--maroon-deep);
  opacity: 0;
  animation: pinPulse 3.4s var(--e-out) infinite;
}

@keyframes pinPulse {
  0% {
    transform: scale(0.72);
    opacity: 0.85;
  }
  70%,
  100% {
    transform: scale(1.9);
    opacity: 0;
  }
}

.map-label {
  position: absolute;
  transform: translate(-50%, -50%);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  pointer-events: none;
}

.map-label.road {
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: none;
  font-size: 0.74rem;
  color: #9c9285;
}

.map-here {
  position: absolute;
  left: 50%;
  top: 50%;
  margin: 20px 0 0 -1px;
  transform: translateX(-50%);
  padding: 7px 13px;
  border-radius: 999px;
  background: var(--panel);
  box-shadow: 0 2px 6px rgba(27, 20, 22, 0.14), inset 0 0 0 1px var(--line);
  font-size: 0.76rem;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
}

.map-credit {
  position: absolute;
  right: 10px;
  bottom: 8px;
  font-size: 0.64rem;
  color: #9c9285;
  background: rgba(251, 248, 243, 0.8);
  padding: 2px 8px;
  border-radius: 999px;
}

@media (max-width: 700px) {
  .map {
    aspect-ratio: 4 / 3;
  }
  .map-label {
    display: none;
  }
}

/* ------------------------------------------------------------------ ask --- */

.ask {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr);
  gap: clamp(28px, 5vw, 70px);
  align-items: center;
}

@media (max-width: 900px) {
  .ask {
    grid-template-columns: 1fr;
  }
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 1.6rem;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0.6em 1.1em;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
  color: #fff;
  font-size: 0.89rem;
  font-weight: 500;
  cursor: pointer;
  text-align: left;
  transition: background-color 180ms ease, box-shadow 180ms ease, transform 140ms var(--e-out);
}

.chip:hover {
  background: rgba(163, 38, 57, 0.32);
  box-shadow: inset 0 0 0 1px var(--maroon);
}
.chip:active {
  transform: scale(0.97);
}

/* The dot moved to ::after, because ::before is now the glass ring.
   `order: -1` puts it back on the left of the flex row. */
.chip::after {
  content: '';
  order: -1;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--coral-lit);
  flex: none;
  position: relative;
  z-index: 1;
}

.on-light .chip::after {
  background: var(--coral-deep);
}

.on-light .chip {
  background: rgba(255, 255, 255, 0.55);
  background-blend-mode: normal;
  color: var(--ink-2);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.9);
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .on-light .chip {
    background: rgba(255, 255, 255, 0.5);
    -webkit-backdrop-filter: blur(14px) saturate(1.5);
    backdrop-filter: blur(14px) saturate(1.5);
  }
}

.on-light .chip::before {
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.95) 0%,
    rgba(27, 20, 22, 0.07) 40%,
    rgba(27, 20, 22, 0.07) 60%,
    rgba(27, 20, 22, 0.14) 100%
  );
}
.on-light .chip:hover {
  background: var(--maroon-tint);
  box-shadow: inset 0 0 0 1px var(--maroon);
  color: var(--ink);
}
.chat-mock {
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--panel);
  box-shadow: var(--shadow-3);
  max-width: 390px;
  margin-left: auto;
  width: 100%;
}

.chat-mock-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  background: var(--maroon-deep);
  color: #fff;
}

.chat-mock-head b {
  font-size: 0.9rem;
  font-weight: 700;
}
.chat-mock-head span {
  display: block;
  font-size: 0.73rem;
  opacity: 0.85;
}

.chat-mock-body {
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: var(--paper-2);
}

.bubble {
  max-width: 88%;
  padding: 10px 14px;
  border-radius: 14px;
  font-size: 0.89rem;
  line-height: 1.5;
  color: var(--ink);
}

.bubble.them {
  background: #fff;
  align-self: flex-start;
  border-bottom-left-radius: 4px;
  box-shadow: 0 1px 2px rgba(27, 20, 22, 0.08);
}

.bubble.me {
  background: var(--maroon-deep);
  color: #fff;
  align-self: flex-end;
  border-bottom-right-radius: 4px;
}

/* --------------------------------------------------------- the name band --- */

/*
 * The one deliberately off-grid moment on the site — VARIANCE 7 earns exactly
 * one of these, and it goes on the thing that makes this school this school.
 */
.namewall {
  display: grid;
  grid-template-columns: minmax(0, 0.42fr) minmax(0, 0.58fr);
  gap: clamp(30px, 6vw, 90px);
  align-items: center;
}

.namewall-word {
  font-family: 'Fraunces', ui-serif, Georgia, serif;
  font-optical-sizing: auto;
  font-weight: 600;
  font-size: clamp(3.4rem, 9vw, 7.2rem);
  line-height: 0.9;
  letter-spacing: -0.05em;
  color: var(--maroon-deep);
  margin: 0 0 0.35em;
}

.namewall-word em {
  display: block;
  font-family: 'Figtree', sans-serif;
  font-style: normal;
  font-size: 0.15em;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--teal-deep);
  margin-top: 1.1em;
}

.namewall-def {
  font-size: 1rem;
  color: var(--ink-2);
  border-left: 2px solid var(--teal);
  padding-left: 18px;
  max-width: 34ch;
}

@media (max-width: 900px) {
  .namewall {
    grid-template-columns: 1fr;
  }
}

/* --------------------------------------------------------------- footer --- */

footer.site {
  background: var(--ink-deep);
  color: rgba(228, 222, 214, 0.76);
  padding: clamp(58px, 7vw, 86px) 0 30px;
  font-size: 0.93rem;
}

footer.site h4 {
  font-family: 'Figtree', sans-serif;
  font-size: 0.71rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(228, 222, 214, 0.5);
  margin: 0 0 1rem;
}

footer.site a:hover {
  color: var(--coral-lit);
}

.foot-grid {
  display: grid;
  grid-template-columns: 1.5fr repeat(3, 1fr);
  gap: clamp(28px, 4vw, 48px);
}

@media (max-width: 900px) {
  .foot-grid {
    grid-template-columns: 1fr 1fr;
  }
}
@media (max-width: 520px) {
  .foot-grid {
    grid-template-columns: 1fr;
  }
}

.foot-grid ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 9px;
}

.foot-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  color: #fff;
  margin-bottom: 1rem;
}

.foot-brand .crest {
  width: 48px;
  height: 48px;
  flex: none;
}

.demo-note {
  border-radius: var(--r-md);
  padding: 20px 24px;
  background: rgba(255, 255, 255, 0.05);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.15);
  font-size: 0.85rem;
  line-height: 1.65;
  color: rgba(228, 222, 214, 0.74);
  margin-top: 30px;
}

.demo-note b {
  color: #fff;
}

.demo-note p + p {
  margin-top: 0.8em;
}

.foot-bottom {
  margin-top: clamp(34px, 4vw, 52px);
  padding-top: 22px;
  border-top: 1px solid rgba(255, 255, 255, 0.13);
  display: flex;
  flex-wrap: wrap;
  gap: 12px 28px;
  justify-content: space-between;
  font-size: 0.82rem;
  color: rgba(228, 222, 214, 0.5);
}

/* -------------------------------------------------------- reveal system --- */

.r {
  opacity: 0;
  transform: translate3d(0, 20px, 0);
  transition: opacity 800ms var(--e-rev), transform 800ms var(--e-rev);
}

.r-left {
  transform: translate3d(-24px, 0, 0);
}
.r-right {
  transform: translate3d(24px, 0, 0);
}
.r-scale {
  transform: scale(0.975);
}

.r.is-in {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}
.r-scale.is-in {
  transform: scale(1);
}

.d1 {
  transition-delay: 80ms;
}
.d2 {
  transition-delay: 160ms;
}
.d3 {
  transition-delay: 240ms;
}
.d4 {
  transition-delay: 320ms;
}
.d5 {
  transition-delay: 400ms;
}

html.no-js .r {
  opacity: 1;
  transform: none;
}

/* Word-by-word headline reveal. The wrapper is the clip mask; the inner span
   rises out of it. vertical-align:top stops the baseline jumping, and the
   bottom padding keeps descenders off the clip edge. */
.wr {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  margin-right: 0.22em;
  padding-bottom: 0.12em;
}

.wr i {
  display: inline-block;
  font-style: inherit;
  transform: translateY(110%);
  filter: blur(4px);
  opacity: 0;
  transition: transform 900ms var(--e-rev), filter 900ms var(--e-rev), opacity 700ms var(--e-rev);
}

.wr.is-in i {
  transform: translateY(0);
  filter: blur(0);
  opacity: 1;
}

html.no-js .wr i {
  transform: none;
  filter: none;
  opacity: 1;
}

/* The stressed word in a headline. */
.lit {
  color: var(--coral-lit);
}
.lit-dark {
  color: var(--teal-deep);
}

/* A table kept for screen readers and copy-paste, out of the visual flow. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------- reduced motion --- */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .r,
  .r-left,
  .r-right,
  .r-scale {
    transform: none !important;
    transition: opacity 300ms ease;
  }

  .wr i {
    transform: none !important;
    filter: none !important;
    transition: opacity 300ms ease;
  }

  /* The ambient drift goes entirely — it is the one thing on this page that
     moves without being asked to. The entrance fades stay, because they aid
     comprehension. */
  .hero-media img,
  .shot.is-drifting img,
  .lb.is-ready .lb-frame img {
    animation: none !important;
    transform: none !important;
  }

  .shot img {
    transform: none;
  }

  .map-pin::after {
    animation: none;
    opacity: 0;
  }

  .card,
  .btn,
  .chip,
  .contact-card {
    transition: background-color 200ms ease, color 200ms ease;
  }

  .faq details p {
    animation: none;
  }

  .lb,
  .lb-frame img {
    transition: opacity 200ms ease;
  }
}

@media (prefers-reduced-transparency: reduce) {
  .nav.is-stuck {
    background: var(--paper);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .shot figcaption {
    background: rgba(18, 32, 30, 0.92);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .lb {
    background: #0b1413;
  }
}

@media print {
  .nav,
  .menu,
  .hero-media,
  .hero-scrim,
  .lb,
  .shot-hit,
  .shot-expand {
    display: none !important;
  }
  body {
    background: #fff;
  }
  .hero,
  .page-head {
    min-height: 0;
    padding-top: 20px;
    background: #fff;
    color: var(--ink);
  }
  .hero h1,
  .page-head h1,
  .hero .lede,
  .page-head .lede {
    color: var(--ink);
  }
}
