/* ==========================================================================
   Paramount International School — design system
   Built on Apple's approach to interface design: response on pointer-down,
   interruptible spring motion, translucent materials, size-specific tracking.
   ========================================================================== */

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

:root {
  /* Brand — sampled from the original logo asset */
  --blue: #015a9c;
  --blue-600: #01487a;
  --blue-800: #012f52;
  --blue-tint: #e8f1f8;
  --green: #8bc53f;
  --green-600: #5c8a1f;
  --green-tint: #eef7e3;

  /* Neutrals — warm-tinted so photography feels at home */
  --ink: #10202e;
  --ink-2: #40525f;
  --ink-3: #6b7d8a;
  --ink-4: #9aa9b4;
  --line: rgba(16, 32, 46, 0.1);
  --line-strong: rgba(16, 32, 46, 0.18);
  --bg: #ffffff;
  --bg-sunk: #f4f7f9;
  --bg-warm: #fbfaf7;

  /* Translucent materials */
  --mat-chrome: rgba(255, 255, 255, 0.72);
  --mat-card: rgba(255, 255, 255, 0.66);
  --blur-chrome: 24px;
  --blur-card: 18px;

  /* Type scale — rem so Dynamic Type scaling holds the layout */
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", system-ui,
    "Helvetica Neue", Arial, sans-serif;
  --font-display: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI",
    system-ui, "Helvetica Neue", Arial, sans-serif;

  --step--1: clamp(0.875rem, 0.86rem + 0.1vw, 0.94rem);
  --step-0: clamp(1.03rem, 1rem + 0.15vw, 1.09rem);
  --step-1: clamp(1.2rem, 1.14rem + 0.3vw, 1.4rem);
  --step-2: clamp(1.45rem, 1.32rem + 0.62vw, 1.9rem);
  --step-3: clamp(1.75rem, 1.5rem + 1.2vw, 2.6rem);
  --step-4: clamp(2.1rem, 1.66rem + 2.1vw, 3.6rem);
  --step-5: clamp(2.5rem, 1.7rem + 3.8vw, 5.2rem);

  /* Spacing */
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;
  --sp-8: 4.5rem;
  --sp-9: 6.5rem;

  --radius-sm: 10px;
  --radius: 18px;
  --radius-lg: 28px;
  --radius-xl: 40px;

  --shell: 1200px;
  --shell-wide: var(--shell);
  --gutter: clamp(1.25rem, 4vw, 3rem);

  /* Depth: context-aware — heavier over photography, lighter over flat ground */
  --shadow-1: 0 1px 2px rgba(16, 32, 46, 0.05), 0 2px 8px rgba(16, 32, 46, 0.05);
  --shadow-2: 0 2px 6px rgba(16, 32, 46, 0.06), 0 12px 28px rgba(16, 32, 46, 0.09);
  --shadow-3: 0 4px 12px rgba(16, 32, 46, 0.08), 0 24px 60px rgba(16, 32, 46, 0.14);
  --shadow-blue: 0 10px 30px rgba(1, 90, 156, 0.28);

  /* Motion — spring presets, exposed so JS and CSS agree */
  --ease-standard: cubic-bezier(0.32, 0.72, 0, 1);
  --dur-1: 160ms;
  --dur-2: 320ms;
  --dur-3: 520ms;
}

/* ---------- 2. Reset & base ---------- */

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

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

body {
  margin: 0;
  font: 100%/1.6 var(--font);
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

body.is-locked { overflow: hidden; }

/* The `hidden` attribute must beat any layout `display` we set. Without this a
   `.stack`/`.grid` element toggled via `hidden` stays laid out and visible. */
[hidden] { display: none !important; }

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

h1, h2, h3, h4, h5, h6, p, figure, blockquote, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }

a {
  color: inherit;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}

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

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

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

::selection { background: var(--green); color: #fff; }

/* ---------- 3. Typography ---------- */

/* Tracking is size-specific: tight as type grows, neutral for body. */
.display-1,
.display-2,
.display-3 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.032em;
  line-height: 1.03;
  text-wrap: balance;
  font-optical-sizing: auto;
}
.display-1 { font-size: var(--step-5); }
.display-2 { font-size: var(--step-4); line-height: 1.06; }
.display-3 { font-size: var(--step-3); line-height: 1.12; letter-spacing: -0.024em; }

.h4 {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 650;
  line-height: 1.18;
  letter-spacing: -0.018em;
  text-wrap: balance;
}

.h5 {
  font-size: var(--step-1);
  font-weight: 620;
  line-height: 1.28;
  letter-spacing: -0.012em;
}

.eyebrow {
  font-size: var(--step--1);
  font-weight: 650;
  letter-spacing: 0.085em;
  text-transform: uppercase;
  color: var(--blue);
}
.eyebrow--light { color: rgba(255, 255, 255, 0.86); }

.lede {
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--ink-2);
  text-wrap: pretty;
}

.body-text {
  font-size: var(--step-0);
  line-height: 1.68;
  color: var(--ink-2);
  text-wrap: pretty;
  max-width: 68ch;
}
.body-text + .body-text { margin-top: var(--sp-4); }

.muted { color: var(--ink-3); }
.small { font-size: var(--step--1); line-height: 1.55; }
.mono-num {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.link-underline {
  color: var(--blue);
  font-weight: 550;
  /* `fit-content` keeps the rule the width of the text: as a grid/flex item the
     default `auto` width made the 1.5px line run the full cell. */
  width: fit-content;
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 100% 1.5px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  padding-bottom: 1px;
  transition: background-size var(--dur-2) var(--ease-standard),
    color var(--dur-1) var(--ease-standard);
}
.link-underline:hover { background-size: 0% 1.5px; background-position: 100% 100%; }
.link-underline:active { opacity: 0.6; }

/* ---------- 4. Layout primitives ---------- */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
/* One left edge for the whole page. The header, hero and footer used the wide
   shell while every content section used the narrow one, so the two families
   of blocks never lined up down the page. */
.shell--wide { max-width: var(--shell); }

.section { padding-block: clamp(3.5rem, 8vw, var(--sp-9)); }
.section--tight { padding-block: clamp(2.5rem, 5vw, var(--sp-7)); }
.section--sunk { background: var(--bg-sunk); }
.section--warm { background: var(--bg-warm); }
.section--blue {
  background: linear-gradient(165deg, var(--blue-800), var(--blue) 62%, var(--blue-600));
  color: #fff;
}
.section--blue .body-text,
.section--blue .lede { color: rgba(255, 255, 255, 0.82); }

/* `minmax(0, 1fr)`, not the implicit `auto`: an auto track is sized to its
   content's max-content width, and one wide child (a <select> with a long
   option list, a long unbreakable string) then pushes the whole column past
   its container and the page scrolls sideways. Same reason for min-width: 0
   on the children — grid and flex items default to min-width: auto. */


.section-head {
  display: grid;
  gap: var(--sp-3);
  max-width: 62ch;
  margin-bottom: var(--sp-6);
}
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center .body-text { margin-inline: auto; }

.grid { display: grid; gap: var(--sp-5); grid-template-columns: minmax(0, 1fr); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 13.5rem), 1fr)); }

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

.btn {
  --btn-bg: var(--blue);
  --btn-fg: #fff;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: 0.78rem 1.5rem;
  min-height: 46px;
  border-radius: 999px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: -0.006em;
  white-space: nowrap;
  box-shadow: var(--shadow-blue);
  transition: transform var(--dur-1) var(--ease-standard),
    box-shadow var(--dur-2) var(--ease-standard), background-color var(--dur-1) linear;
  will-change: transform;
  -webkit-user-select: none;
  user-select: none;
}
.btn:hover { background: var(--blue-600); box-shadow: 0 14px 38px rgba(1, 90, 156, 0.34); }
.btn:active { transform: scale(0.96); }
.btn[disabled] { opacity: 0.45; pointer-events: none; }

.btn--green { --btn-bg: var(--green-600); box-shadow: 0 10px 28px rgba(116, 168, 51, 0.32); }
.btn--green:hover { background: #4e761a; box-shadow: 0 14px 36px rgba(116, 168, 51, 0.4); }

.btn--glass {
  --btn-bg: rgba(255, 255, 255, 0.16);
  --btn-fg: #fff;
  border: 1px solid rgba(255, 255, 255, 0.4);
  box-shadow: none;
  -webkit-backdrop-filter: blur(14px) saturate(170%);
  backdrop-filter: blur(14px) saturate(170%);
}
.btn--glass:hover { background: rgba(255, 255, 255, 0.26); box-shadow: none; }

.btn--quiet {
  --btn-bg: transparent;
  --btn-fg: var(--blue);
  box-shadow: none;
  padding-inline: 0.2rem;
}
.btn--quiet::after {
  content: "";
  position: absolute;
  left: 0.2rem;
  right: 0.2rem;
  bottom: 0.35rem;
  height: 1.5px;
  background: currentColor;
  border-radius: 2px;
  transform-origin: 0 50%;
  transition: transform var(--dur-2) var(--ease-standard);
}
.btn--quiet:hover::after { transform: scaleX(0); }
.btn--quiet:active { transform: scale(0.97); }

.btn__arrow { transition: transform var(--dur-2) var(--ease-standard); }
.btn:hover .btn__arrow { transform: translateX(3px); }

/* Icon chip used for phone / mail / maps rows */
.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  flex: 0 0 42px;
  border-radius: 12px;
  background: var(--blue-tint);
  color: var(--blue);
}
.chip svg { width: 20px; height: 20px; }

/* ---------- 6. Materials ---------- */

.glass {
  position: relative;
  background: var(--mat-card);
  -webkit-backdrop-filter: blur(var(--blur-card)) saturate(180%);
  backdrop-filter: blur(var(--blur-card)) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
}
/* Bright top edge reads as light catching the material */
.glass::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0) 42%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

.card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-2) var(--ease-standard),
    box-shadow var(--dur-2) var(--ease-standard), border-color var(--dur-2) var(--ease-standard);
}
.card--lift:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-3);
  border-color: var(--line-strong);
}
.card--pad { padding: clamp(1.25rem, 2.4vw, 2rem); }

/* ---------- 7. Media ---------- */

.media {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--bg-sunk);
  box-shadow: var(--shadow-2);
  isolation: isolate;
}
.media img,
.media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.media--4x5 { aspect-ratio: 4 / 5; }
/* A wide classroom scene cropped to a portrait column: bias towards the
   children rather than the middle of the wall. */
.media--4x5.media--subject img { object-position: 32% 50%; }
/* The admissions campaign clip is a designed poster — its text IS the content.
   Give it a frame that matches the source (402x720) and letterbox rather than
   crop, so "ENTRANCE TEST 22 JUNE" is never sliced in half. */
.media--campaign { aspect-ratio: 402 / 720; background: var(--blue-800); }
.media--campaign img,
.media--campaign video { object-fit: contain; }
.media--16x9 { aspect-ratio: 16 / 9; }

/* Photo zoom: the frame stays still, the image scales — cheaper and calmer
   than transforming the clipping box. */
.media--zoom img {
  transition: transform 900ms var(--ease-standard);
  will-change: transform;
}
.media--zoom:hover img { transform: scale(1.045); }

/* Video is NOT opacity-gated the way <img> is: the `poster` attribute paints
   immediately, and a <video> never fires a `load` event we could hang
   `data-loaded` off — gating it left the clip permanently invisible. */
.media__video,
.media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* A caption plate that sits on the media itself — the material reads as
   arriving rather than pasted on. */
.media__badge {
  position: absolute;
  left: var(--sp-4);
  right: var(--sp-4);
  bottom: var(--sp-4);
  padding: 0.8rem 1rem;
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.86);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  backdrop-filter: blur(18px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.7);
  box-shadow: var(--shadow-3);
  font-size: var(--step-0);
  font-weight: 650;
  letter-spacing: -0.012em;
  line-height: 1.3;
  color: var(--blue-800);
  text-align: center;
}

/* Placeholder until the image decodes — avoids layout jank on slow networks */
.media[data-loaded] img { opacity: 1; }
.media img { opacity: 0; transition: opacity 420ms var(--ease-standard); }

/* ---------- 8. Scroll reveal (JS adds .in) ---------- */

.reveal {
  opacity: 0;
  transform: translate3d(0, 26px, 0);
  transition: opacity 760ms var(--ease-standard), transform 760ms var(--ease-standard);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: transform, opacity;
}
.reveal.in { opacity: 1; transform: none; }

.reveal--scale { transform: translate3d(0, 34px, 0) scale(0.965); }
.reveal--scale.in { transform: none; }

.reveal--line {
  opacity: 0;
  clip-path: inset(0 0 100% 0);
  transform: translate3d(0, 12px, 0);
  transition: opacity 620ms var(--ease-standard), transform 620ms var(--ease-standard),
    clip-path 860ms var(--ease-standard);
  transition-delay: var(--reveal-delay, 0ms);
}
.reveal--line.in { opacity: 1; clip-path: inset(0 0 -12% 0); transform: none; }

/* ---------- 9. Header / chrome ---------- */

.skip-link {
  position: fixed;
  top: 0.5rem;
  left: 50%;
  z-index: 200;
  translate: -50% -160%;
  padding: 0.6rem 1.1rem;
  border-radius: 999px;
  background: var(--blue);
  color: #fff;
  font-weight: 600;
  box-shadow: var(--shadow-3);
  transition: translate 260ms var(--ease-standard);
}
.skip-link:focus-visible { translate: -50% 0; }

.header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  background: transparent;
  transition: background-color var(--dur-3) var(--ease-standard),
    box-shadow var(--dur-3) var(--ease-standard),
    -webkit-backdrop-filter var(--dur-3) linear, backdrop-filter var(--dur-3) linear;
}
/* Scroll-edge effect: a soft fade where content meets floating chrome,
   not a hard 1px rule. */
.header::after {
  content: "";
  position: absolute;
  inset: 100% 0 auto;
  height: 28px;
  background: linear-gradient(180deg, rgba(16, 32, 46, 0.09), rgba(16, 32, 46, 0));
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-standard);
  pointer-events: none;
}
.header.is-stuck .header__inner { min-height: 66px; }
.header.is-stuck {
  background: var(--mat-chrome);
  -webkit-backdrop-filter: blur(var(--blur-chrome)) saturate(180%);
  backdrop-filter: blur(var(--blur-chrome)) saturate(180%);
  box-shadow: 0 1px 0 rgba(16, 32, 46, 0.07), 0 8px 30px rgba(16, 32, 46, 0.08);
}
.header.is-stuck::after { opacity: 1; }

/* Transparent over the dark hero, frosted once scrolled or on inner pages */
/* Over the hero the nav sits on photography, so it is light until the header
   materialises into frosted chrome. */
.header--over .nav-link { color: rgba(255, 255, 255, 0.9); }
.header--over .nav-link:hover,
.header--over .nav-link[aria-current="page"] { color: #fff; }
.header--over .nav__sublink { color: var(--ink); }
.header--over .brand__lockup { opacity: 1; }

.header__inner {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  min-height: 76px;
  padding-block: 0.6rem;
  transition: min-height var(--dur-3) var(--ease-standard);
}

/* The logo is a full lockup (tree mark + wordmark). It is shown at its own
   aspect — never squashed into a square, which made it illegible. */
/* The lockup is blue-and-green artwork. Over the hero's dark scrim the blue
   wordmark disappears into it, so the over-hero state sets the brand on a
   light plate. The plate is the brand's own material, not a chrome pill. */
.brand {
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  margin-right: auto;
  padding: 0.45rem 0.7rem;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.92);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  backdrop-filter: blur(16px) saturate(180%);
  box-shadow: 0 2px 10px rgba(1, 47, 82, 0.16);
  transition: transform var(--dur-1) var(--ease-standard),
    background-color var(--dur-3) var(--ease-standard),
    box-shadow var(--dur-3) var(--ease-standard);
}
.brand:active { transform: scale(0.97); }
.brand__lockup {
  display: block;
  width: clamp(84px, 9.5vw, 128px);
  height: auto;
  object-fit: contain;
}

.nav { display: flex; align-items: center; min-width: 0; }
.nav__list { display: flex; align-items: center; gap: 0.1rem; flex-wrap: nowrap; }
.nav__item { position: relative; flex: 0 0 auto; }
.nav__item--menu { padding-bottom: 0.75rem; margin-bottom: -0.75rem; }
.nav__caret {
  flex: none;
  margin-left: 0.32rem;
  opacity: 0.65;
  transition: transform var(--dur-2) var(--ease-standard);
}
.nav__item.is-open .nav__caret { transform: rotate(180deg); }

/* The panel is anchored to its trigger and grows out of it, so the spatial
   relationship between the item and its contents is never in doubt. */
.nav__menu {
  position: absolute;
  top: calc(100% + 0.5rem);
  left: 50%;
  translate: -50% 0;
  min-width: 15rem;
  padding: 0.4rem;
  border-radius: var(--radius);
  /* Heavier than the light-page card material: this panel most often opens
     over photography, where 66% white leaves it murky. */
  background: rgba(255, 255, 255, 0.92);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  backdrop-filter: blur(28px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.7);
  box-shadow: var(--shadow-3);
  transform-origin: 50% 0;
}
.nav__menu::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0) 46%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}
.nav__menu[hidden] { display: none; }
.nav__menu.is-open { animation: menu-in 240ms var(--ease-standard) both; }
@keyframes menu-in {
  from { opacity: 0; transform: translateY(-6px) scale(0.97); }
  to { opacity: 1; transform: none; }
}
.nav__sublink {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 0.65rem 0.8rem;
  border-radius: 12px;
  font-size: var(--step-0);
  font-weight: 560;
  letter-spacing: -0.008em;
  color: var(--ink);
  transition: background-color var(--dur-1) var(--ease-standard),
    color var(--dur-1) var(--ease-standard), transform var(--dur-2) var(--ease-standard);
}
.nav__sublink:hover { background: var(--blue-tint); color: var(--blue); transform: translateX(2px); }
.nav__sublink:active { transform: translateX(2px) scale(0.98); }
.nav__sublink[aria-current="page"] { color: var(--blue); font-weight: 650; }
.nav__sublink svg { width: 14px; height: 14px; color: var(--ink-4); flex: none; }
.nav-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
  padding: 0.5rem 0.72rem;
  border-radius: 10px;
  font-size: 0.925rem;
  font-weight: 550;
  letter-spacing: -0.005em;
  color: var(--ink-2);
  transition: color var(--dur-1) var(--ease-standard), background-color var(--dur-1) var(--ease-standard),
    transform var(--dur-1) var(--ease-standard);
}
.nav-link:hover { color: var(--blue); background: rgba(1, 90, 156, 0.07); }
.nav-link:active { transform: scale(0.95); }
.nav-link[aria-current="page"] { color: var(--blue); font-weight: 650; }
.nav-link[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 0.72rem;
  right: 0.72rem;
  bottom: 0.18rem;
  height: 2px;
  border-radius: 2px;
  background: var(--green);
}

.header__actions { display: flex; align-items: center; gap: var(--sp-2); }

.header__cta { display: inline-flex; }

/* Language switcher. The trigger shares the menu button's footprint (42px,
   same radius, same materials) so the two read as one row of chrome on a
   phone. The menu is a native popover: it lives in the top layer, so no
   header overflow or stacking context can clip it. */
.lang { position: relative; flex: none; }
.lang__trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.34rem;
  height: 42px;
  padding: 0 0.66rem 0 0.6rem;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  color: var(--ink);
  font-size: 0.84rem;
  font-weight: 650;
  letter-spacing: 0.04em;
  line-height: 1;
  transition: transform var(--dur-1) var(--ease-standard), background-color var(--dur-2) linear,
    border-color var(--dur-2) linear, color var(--dur-2) linear;
}
.lang__trigger:hover { background: #fff; }
.lang__trigger:active { transform: scale(0.94); }
.lang__globe { flex: none; opacity: 0.85; }
.lang__trigger .nav__caret { margin-left: 0; }
.lang__trigger[aria-expanded="true"] .nav__caret { transform: rotate(180deg); }
.header--over .lang__trigger { background: rgba(255, 255, 255, 0.18); border-color: rgba(255, 255, 255, 0.35); color: #fff; }
.header--over .lang__trigger:hover { background: rgba(255, 255, 255, 0.28); }
.header--over.is-stuck .lang__trigger { background: #fff; border-color: var(--line); color: var(--ink); }

.lang__menu {
  /* Undo the UA popover box (centred, bordered) and anchor under the
     trigger. app.js measures the exact spot; the fallback here is the
     header's right edge, which is where the trigger sits at every width. */
  position: fixed;
  inset: auto;
  top: 4.25rem;
  right: max(var(--gutter), calc((100vw - var(--shell)) / 2 + var(--gutter)));
  margin: 0;
  min-width: 13rem;
  padding: 0.4rem;
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.94);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  backdrop-filter: blur(28px) saturate(180%);
  box-shadow: var(--shadow-3);
  color: var(--ink);
  transform-origin: 100% 0;
}
.lang__menu:popover-open { animation: menu-in 240ms var(--ease-standard) both; }
.lang__option {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 0.62rem 0.75rem;
  border-radius: 12px;
  font-size: var(--step-0);
  font-weight: 560;
  letter-spacing: -0.006em;
  color: var(--ink);
  transition: background-color var(--dur-1) var(--ease-standard), color var(--dur-1) var(--ease-standard);
}
.lang__option:hover,
.lang__option:focus-visible { background: var(--blue-tint); color: var(--blue); }
.lang__option:focus-visible { outline-offset: -2px; }
.lang__option:active { transform: scale(0.98); }
.lang__name { flex: 1 1 auto; }
.lang__short { font-size: 0.72rem; font-weight: 650; letter-spacing: 0.08em; color: var(--ink-4); }
/* The current language is marked three ways — checked state for assistive
   tech, a tick and weight for the eye — never by colour alone. */
.lang__check { flex: none; color: var(--green-600); visibility: hidden; }
.lang__option[aria-checked="true"] { color: var(--blue); font-weight: 650; }
.lang__option[aria-checked="true"] .lang__check { visibility: visible; }

/* Menu toggle — only below the nav breakpoint */
.menu-btn {
  display: none;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-1) var(--ease-standard), background-color var(--dur-2) linear;
}
.menu-btn:active { transform: scale(0.93); }
.header--over .menu-btn { background: rgba(255, 255, 255, 0.18); border-color: rgba(255, 255, 255, 0.35); }

.menu-btn__bars { position: relative; width: 18px; height: 12px; }
.menu-btn__bars i {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  border-radius: 2px;
  background: var(--ink);
  transition: transform 320ms var(--ease-standard), opacity 160ms linear,
    background-color var(--dur-3) var(--ease-standard);
}
.menu-btn__bars i:nth-child(1) { top: 0; }
.menu-btn__bars i:nth-child(2) { top: 5px; }
.menu-btn__bars i:nth-child(3) { top: 10px; }
.menu-btn[aria-expanded="true"] .menu-btn__bars i { background: var(--ink); }
.menu-btn[aria-expanded="true"] .menu-btn__bars i:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.menu-btn[aria-expanded="true"] .menu-btn__bars i:nth-child(2) { opacity: 0; }
.menu-btn[aria-expanded="true"] .menu-btn__bars i:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

/* ---------- 10. Mobile sheet ---------- */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 99;
  display: grid;
  grid-template-rows: 1fr;
  pointer-events: none;
}
.sheet[hidden] { display: none; }

.sheet__scrim {
  position: absolute;
  inset: 0;
  background: rgba(8, 26, 41, 0.42);
  opacity: 0;
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  transition: opacity var(--dur-3) var(--ease-standard);
}
.sheet.is-open .sheet__scrim { opacity: 1; }

.sheet__panel {
  position: relative;
  align-self: end;
  max-height: 88vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: var(--sp-3) var(--gutter) calc(var(--sp-6) + env(safe-area-inset-bottom));
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  background: rgba(255, 255, 255, 0.88);
  -webkit-backdrop-filter: blur(30px) saturate(180%);
  backdrop-filter: blur(30px) saturate(180%);
  box-shadow: 0 -8px 60px rgba(8, 26, 41, 0.24);
  /* No CSS transition on transform: the spring drives this property every
     frame, and a transition in parallel makes the rendered position lag the
     spring by hundreds of pixels. JS owns it end to end. */
  transform: translate3d(0, 100%, 0);
  transition: opacity 200ms linear;
  touch-action: pan-y;
}
.sheet.is-open { pointer-events: auto; }

.sheet__grab {
  width: 38px;
  height: 5px;
  margin: 0 auto var(--sp-4);
  border-radius: 3px;
  background: var(--line-strong);
}

.sheet__list { display: grid; gap: 0.15rem; }
.sheet__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 0.9rem 0.5rem;
  border-radius: 14px;
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: -0.014em;
  color: var(--ink);
  border-bottom: 1px solid var(--line);
  transition: background-color var(--dur-1) var(--ease-standard), transform var(--dur-2) var(--ease-standard);
}
.sheet__link:last-child { border-bottom: 0; }
.sheet__link:active { background: rgba(1, 90, 156, 0.08); transform: translateX(6px); }
.sheet__link[aria-current="page"] { color: var(--blue); }
.sheet__link svg { width: 16px; height: 16px; color: var(--ink-4); flex: none; }

.sheet__foot { margin-top: var(--sp-5); display: grid; gap: var(--sp-3); }
.sheet__contact { display: grid; gap: 0.2rem; font-size: var(--step-0); }
.sheet__contact a { font-weight: 600; }
.sheet__actions { display: grid; gap: var(--sp-2); }
.sheet__actions .btn { width: 100%; }

/* ---------- 11. Hero ---------- */

.hero {
  position: relative;
  min-height: min(92svh, 900px);
  display: grid;
  align-items: end;
  padding-block: calc(68px + var(--sp-8)) var(--sp-8);
  overflow: hidden;
  color: #fff;
  isolation: isolate;
}
.hero--compact { min-height: min(62svh, 560px); }

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
  overflow: hidden;
}
.hero__media img,
.hero__media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Held slightly large so parallax never exposes an edge */
  transform: translate3d(0, var(--parallax, 0px), 0) scale(1.12);
  will-change: transform;
}
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(180deg, rgba(1, 47, 82, 0.55) 0%, rgba(1, 47, 82, 0.18) 34%, rgba(1, 47, 82, 0.78) 100%),
    linear-gradient(105deg, rgba(1, 47, 82, 0.7) 0%, rgba(1, 47, 82, 0.12) 62%);
}
.hero__content { position: relative; max-width: 54rem; }
.hero__title { color: #fff; }
.hero__sub {
  /* The display title runs at line-height 1.03, so its box ends before its
     glyphs do. Without this the tagline sits flush against the last line and
     the two overlap. */
  margin-top: 0.35em;
  font-size: var(--step-2);
  font-weight: 600;
  letter-spacing: -0.018em;
  line-height: 1.2;
  color: #a9dd6a;
  text-wrap: balance;
  /* The tagline crosses whatever the photo happens to be doing, so it carries
     its own separation rather than relying on the scrim. */
  text-shadow: 0 1px 14px rgba(1, 32, 57, 0.75), 0 1px 3px rgba(1, 32, 57, 0.5);
}
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-top: var(--sp-6);
}

.hero__badges {
  position: absolute;
  right: var(--gutter);
  bottom: var(--sp-8);
  display: flex;
  gap: 0.6rem;
  z-index: 1;
}

/* When the header materialises into frosted chrome it also has to give up its
   over-hero colours. Without this the white nav sits on near-white glass: 1.2:1
   in normal mode and 1:1 under `prefers-contrast: more`, where the chrome goes
   fully opaque. Colour flips with the background, not on a timer. */
.header--over.is-stuck .brand { background: transparent; box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
.header--over.is-stuck .nav-link { color: var(--ink-2); }
.header--over.is-stuck .nav-link:hover,
.header--over.is-stuck .nav-link[aria-current="page"] { color: var(--blue); }
.header--over.is-stuck .menu-btn { background: #fff; border-color: var(--line); }
.header--over.is-stuck .menu-btn__bars i { background: var(--ink); }

/* Scroll cue */
.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: 1.1rem;
  /* The hero content is a centred column; without this the cue's box sits on
     top of the last line of the CTA row. */
  pointer-events: none;
  opacity: 0.9;
  translate: -50% 0;
  display: grid;
  justify-items: center;
  gap: 0.4rem;
  font-size: 0.66rem;
  font-weight: 650;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
  z-index: 1;
}
.scroll-cue__track {
  width: 22px;
  height: 34px;
  border: 1.5px solid rgba(255, 255, 255, 0.5);
  border-radius: 12px;
  position: relative;
}
.scroll-cue__dot {
  position: absolute;
  left: 50%;
  top: 6px;
  width: 3px;
  height: 6px;
  margin-left: -1.5px;
  border-radius: 2px;
  background: #fff;
  animation: cue 1.9s var(--ease-standard) infinite;
}
@keyframes cue {
  0% { transform: translateY(0); opacity: 0; }
  25% { opacity: 1; }
  75% { transform: translateY(13px); opacity: 0; }
  100% { transform: translateY(13px); opacity: 0; }
}

/* ---------- 12. Split feature ---------- */

.split {
  display: grid;
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 60rem) {
  .split { grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); }
  .split--reverse > :first-child { order: 2; }
  .split--wide { grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr); }
}

.split__media { position: relative; }
.split__body { display: grid; gap: var(--sp-4); align-content: center; }
.split__body .body-text { max-width: 56ch; }

.media-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-3);
  align-items: start;
}
.media-pair > :nth-child(2) { margin-top: var(--sp-6); }
.media-pair--stack > :nth-child(2) { margin-top: calc(var(--sp-8) * -0.4); }

/* ---------- 13. Cards ---------- */

.icon-card { display: grid; gap: var(--sp-3); align-content: start; }
.icon-card__glyph {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: 14px;
  background: linear-gradient(150deg, var(--blue-tint), #fff);
  border: 1px solid var(--line);
  color: var(--blue);
}
.icon-card__glyph svg { width: 22px; height: 22px; }

/* One shape for all six curriculum cards: fixed media band, copy block that
   grows, and a link row pinned to the bottom so the baselines line up. */
.subject {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: var(--sp-3);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-2) var(--ease-standard), box-shadow var(--dur-2) var(--ease-standard);
}
.subject:hover { transform: translateY(-5px); box-shadow: var(--shadow-3); }

/* The media band has one shape for all six subjects. Photographs fill it; how
   many there are changes the split, never the height — that is what makes the
   grid line up instead of reading as six unrelated cards. */
.subject__media {
  position: relative;
  display: grid;
  gap: var(--sp-2);
  grid-template-columns: minmax(0, 1fr);
  grid-auto-rows: 1fr;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  overflow: hidden;
}
/* Two photographs stack rather than sit side by side. Splitting a 4:5 band
   into halves gives each image a 1:2.5 slot, which cuts a classroom scene
   down to an unreadable sliver; stacked, each gets a usable frame. */
.subject__media--pair {
  grid-template-rows: repeat(2, minmax(0, 1fr));
  aspect-ratio: 4 / 5;
}

.subject__media--empty {
  background:
    radial-gradient(90% 90% at 22% 14%, rgba(139, 197, 63, 0.3), transparent 62%),
    linear-gradient(155deg, var(--blue) 0%, var(--blue-800) 100%);
  border: 0;
}

.subject__photo {
  height: 100%;
  aspect-ratio: auto;
  border-radius: 0;
}
.subject__media--single .subject__photo { border-radius: 0; }

.subject__body { display: grid; gap: var(--sp-2); align-content: start; flex: 1; }
.subject__head { display: flex; align-items: baseline; gap: var(--sp-3); }
.subject__index {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--green);
  font-variant-numeric: tabular-nums;
}
.subject__body .body-text { font-size: var(--step-0); }

.subject__foot {
  min-height: 2.75rem;
  display: flex;
  align-items: flex-end;
  padding-inline: var(--sp-2);
}

/* ---------- 14. Steps ---------- */

/* ---------- 15. Contact rows ---------- */

.contact-rows { display: grid; gap: 0; }
.contact-row {
  display: flex;
  min-width: 0;
  /* Wrap at every width: the address is one long value and a nowrap flex line
     pushes it off the right edge of a phone screen. */
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4) 0;
  border-bottom: 1px solid var(--line);
  transition: transform var(--dur-2) var(--ease-standard);
}
.contact-row:last-child { border-bottom: 0; }
.contact-row__label {
  font-size: var(--step--1);
  font-weight: 650;
  letter-spacing: 0.085em;
  text-transform: uppercase;
  color: var(--ink-3);
  flex: 0 0 100%;
}
@media (min-width: 34rem) {
  .contact-row__label { flex: 0 0 9.5rem; }
  .contact-row__value { flex: 1 1 14rem; }
}
.contact-row__value {
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: -0.008em;
  line-height: 1.4;
}
/* On the deep-blue safety band the same rows invert. Colour lives on a solid
   layer (the section), never on the translucent foreground. */
.contact-rows--on-dark .contact-row { border-bottom-color: rgba(255, 255, 255, 0.16); }
.contact-rows--on-dark .contact-row__label { color: rgba(255, 255, 255, 0.62); }
.contact-rows--on-dark .contact-row__value,
.contact-rows--on-dark a.contact-row__value { color: #fff; }

a.contact-row__value { color: var(--blue); }
a.contact-row__value:hover { text-decoration: underline; text-underline-offset: 3px; }
a.contact-row__value:active { opacity: 0.6; }

/* ---------- 16. Map ---------- */

.map-frame {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--bg-sunk);
  box-shadow: var(--shadow-2);
  border: 1px solid var(--line);
}
@media (min-width: 48rem) { .map-frame { aspect-ratio: 2.4 / 1; } }
.map-frame iframe {
  position: relative;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  opacity: 0;
  transition: opacity 500ms var(--ease-standard);
}
/* The embed takes a moment to negotiate with Google; until it paints, hold a
   map-toned placeholder with the campus pin so the panel never reads as broken. */
.map-frame[data-map="ready"] iframe { opacity: 1; }

.map-frame__skeleton {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background:
    linear-gradient(115deg, #eef3f0 25%, #e4ece7 37%, #eef3f0 55%),
    #eef3f0;
  background-size: 300% 100%;
  animation: map-sheen 1.6s linear infinite;
}
.map-frame[data-map="ready"] .map-frame__skeleton { display: none; }

.map-frame__label {
  position: absolute;
  bottom: 5.5rem;
  left: 50%;
  translate: -50% 0;
  font-size: var(--step-0);
  font-weight: 650;
  letter-spacing: -0.012em;
  color: var(--ink-3);
  white-space: nowrap;
}
.map-frame[data-map="ready"] .map-frame__label { display: none; }

.map-frame__pin {
  width: 30px;
  height: 30px;
  border-radius: 50% 50% 50% 0;
  rotate: -45deg;
  background: var(--blue);
  box-shadow: 0 6px 16px rgba(1, 90, 156, 0.35);
  opacity: 0.85;
}
.map-frame__pin::after {
  content: "";
  position: absolute;
  inset: 10px;
  border-radius: 50%;
  background: #fff;
}

@keyframes map-sheen {
  from { background-position: 150% 0; }
  to { background-position: -50% 0; }
}

.map-cta {
  position: absolute;
  left: var(--sp-4);
  right: var(--sp-4);
  bottom: var(--sp-4);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  align-items: center;
  justify-content: space-between;
  padding: 0.85rem 1rem;
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.86);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.7);
  box-shadow: var(--shadow-3);
}
.map-cta__addr { font-size: var(--step--1); line-height: 1.4; font-weight: 550; color: var(--ink-2); }

/* ---------- 17. Forms ---------- */

.field { display: grid; gap: 0.35rem; }
.field__label {
  font-size: var(--step--1);
  font-weight: 620;
  letter-spacing: 0.01em;
  color: var(--ink-2);
}
.field__control {
  width: 100%;
  min-width: 0;
  padding: 0.8rem 1rem;
  min-height: 48px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid var(--line-strong);
  box-shadow: inset 0 1px 2px rgba(16, 32, 46, 0.04);
  transition: border-color var(--dur-1) linear, box-shadow var(--dur-2) var(--ease-standard),
    background-color var(--dur-1) linear;
}
.field__control:hover { border-color: var(--ink-4); }
.field__control:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 4px rgba(1, 90, 156, 0.16);
}
textarea.field__control { min-height: 7.5rem; resize: vertical; }
select.field__control { appearance: none; -webkit-appearance: none; }
.field--select { position: relative; }
.field--select::after {
  content: "";
  position: absolute;
  right: 1rem;
  bottom: 1.35rem;
  width: 7px;
  height: 7px;
  border-right: 1.6px solid var(--ink-3);
  border-bottom: 1.6px solid var(--ink-3);
  transform: translateY(-50%) rotate(45deg);
  pointer-events: none;
}
.form-grid { display: grid; gap: var(--sp-4); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 40rem) { .form-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Safety net for the same class of bug: grid and flex items default to
   `min-width: auto`, so one wide child (a <select> with a long option list, an
   unbreakable string) can push its track past the container and scroll the page
   sideways. Scoped to children only — the multi-column layouts (.split, .grid-2,
   .subject__media, .event, .steps) keep their own tracks untouched. */
.grid > *,
.form-grid > *,
.section-head > *,
.contact-rows > *,
.media-pair > *,
.split > *,
.header__inner > *,
.footer__bar > * { min-width: 0; }

/* ---------- 18. Staff ---------- */

.staff-card {
  display: grid;
  gap: var(--sp-3);
  align-content: start;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-2) var(--ease-standard), box-shadow var(--dur-2) var(--ease-standard);
}
.staff-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-3); }
.staff-card__media { position: relative; aspect-ratio: 4 / 5; overflow: hidden; background: var(--bg-sunk); }
.staff-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 22%;
  transition: transform 800ms var(--ease-standard);
}
.staff-card:hover .staff-card__media img { transform: scale(1.05); }
.staff-card__body { padding: 0 var(--sp-4) var(--sp-4); }
.staff-card__name {
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: 660;
  letter-spacing: -0.014em;
  line-height: 1.25;
}
.staff-card__role {
  margin-top: 0.2rem;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--green-600);
}

/* ---------- 19. List (documents) ---------- */

.check-list { display: grid; gap: var(--sp-3); counter-reset: doc; }
.check-list li {
  display: flex;
  gap: var(--sp-3);
  align-items: flex-start;
  font-size: var(--step-0);
  line-height: 1.5;
  color: var(--ink-2);
}
.check-list li::before {
  counter-increment: doc;
  content: counter(doc);
  display: grid;
  place-items: center;
  flex: 0 0 28px;
  width: 28px;
  height: 28px;
  margin-top: -1px;
  border-radius: 9px;
  background: var(--blue-tint);
  color: var(--blue);
  font-size: 0.82rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ---------- 20. Events ---------- */

.event {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4);
  border-radius: var(--radius-lg);
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-2) var(--ease-standard), box-shadow var(--dur-2) var(--ease-standard),
    border-color var(--dur-2) var(--ease-standard);
}
.event:hover { transform: translateY(-3px); box-shadow: var(--shadow-3); border-color: var(--line-strong); }
.event:active { transform: scale(0.99); }
.event__date {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  min-width: 6.5rem;
  padding: 0.6rem 0.85rem;
  border-radius: var(--radius-sm);
  background: var(--blue-tint);
  color: var(--blue);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.01em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.event__title {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 650;
  letter-spacing: -0.016em;
  line-height: 1.25;
}
.event__loc { margin-top: 0.15rem; font-size: var(--step--1); color: var(--ink-3); }
.event__more {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--step--1);
  font-weight: 650;
  color: var(--blue);
  white-space: nowrap;
}
.event__more svg { width: 14px; height: 14px; transition: transform var(--dur-2) var(--ease-standard); }
.event:hover .event__more svg { transform: translateX(3px); }

/* ---------- 21. Empty state ---------- */

.empty {
  display: grid;
  justify-items: center;
  gap: var(--sp-3);
  padding: clamp(2.5rem, 6vw, 4.5rem) var(--sp-5);
  text-align: center;
  border-radius: var(--radius-lg);
  background: var(--bg-sunk);
  border: 1px dashed var(--line-strong);
}
.empty__glyph {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 18px;
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  color: var(--blue);
}
.empty__glyph svg { width: 24px; height: 24px; }
.empty__title {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 660;
  letter-spacing: -0.02em;
}
.empty__body { color: var(--ink-3); max-width: 44ch; }

/* ---------- 22. Footer ---------- */

.footer {
  position: relative;
  background: linear-gradient(180deg, var(--blue-800), #011d33);
  color: rgba(255, 255, 255, 0.78);
  overflow: hidden;
}
.footer::before {
  content: "";
  position: absolute;
  inset: -40% -20% auto;
  height: 70%;
  background: radial-gradient(60% 100% at 50% 0%, rgba(139, 197, 63, 0.16), transparent 70%);
  pointer-events: none;
}
.footer__inner {
  position: relative;
  display: grid;
  gap: var(--sp-7);
  padding-block: clamp(3rem, 6vw, 4.5rem) var(--sp-5);
  grid-template-columns: 1fr;
}
@media (min-width: 52rem) {
  .footer__inner { grid-template-columns: 1.3fr 0.8fr 1fr; }
}

.footer__brand { display: grid; gap: var(--sp-4); justify-items: start; align-content: start; }
.footer__lockup {
  width: clamp(120px, 16vw, 172px);
  height: auto;
  /* The lockup is dark artwork on white; the footer is dark, so it is knocked
     back onto a light plate rather than being inverted (which would ruin the
     green). */
  padding: 0.7rem 0.9rem;
  border-radius: var(--radius);
  background: #fff;
  box-shadow: var(--shadow-2);
}

.footer__title {
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.58);
  margin-bottom: var(--sp-4);
}
.footer__links { display: grid; gap: 0.15rem; }
.footer__link {
  display: inline-block;
  padding: 0.42rem 0;
  font-size: var(--step-0);
  font-weight: 550;
  color: rgba(255, 255, 255, 0.82);
  transition: color var(--dur-1) linear, transform var(--dur-2) var(--ease-standard);
}
.footer__link:hover { color: #fff; transform: translateX(3px); }
.footer__link:active { opacity: 0.6; transform: translateX(3px) scale(0.98); }

.footer__contact { display: grid; gap: var(--sp-3); font-size: var(--step-0); line-height: 1.5; }
.footer__contact a { color: #fff; font-weight: 600; }
.footer__contact a:hover { text-decoration: underline; text-underline-offset: 3px; }
.footer__address { font-style: normal; color: rgba(255, 255, 255, 0.78); }

.footer__bar .link-underline { color: rgba(255, 255, 255, 0.86); }

.footer__bar {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: center;
  justify-content: space-between;
  padding-block: var(--sp-4);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  font-size: var(--step--1);
  color: rgba(255, 255, 255, 0.6);
}

/* ---------- 23. Misc utilities ---------- */

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

.to-top {
  position: fixed;
  right: clamp(1rem, 3vw, 2rem);
  bottom: calc(clamp(1rem, 3vw, 2rem) + env(safe-area-inset-bottom));
  z-index: 80;
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.8);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  backdrop-filter: blur(18px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.7);
  box-shadow: var(--shadow-3);
  color: var(--blue);
  opacity: 0;
  transform: scale(0.7) translateY(8px);
  pointer-events: none;
  transition: opacity var(--dur-2) var(--ease-standard), transform 420ms var(--ease-standard),
    background-color var(--dur-1) linear;
}
.to-top.is-visible { opacity: 1; transform: none; pointer-events: auto; }
.to-top:hover { background: #fff; }
.to-top:active { transform: scale(0.9); }
.to-top svg { width: 18px; height: 18px; }

/* Reserve room for the fixed header — but NOT on a page that opens with a
   full-bleed hero. There the hero starts at y=0 and the translucent header
   floats over it; padding would open a white gap under the header and leave
   the white-on-hero nav links sitting on white. */
.page { padding-top: 68px; }
.has-hero .page { padding-top: 0; }
.has-chero .page { padding-top: 0; }
.has-hero .hero { min-height: min(100svh, 1000px); }

/* ---------- 24. Responsive ---------- */

@media (max-width: 64rem) {
  .nav { display: none; }
  .menu-btn { display: grid; }
  .header__cta { display: none; }
}

@media (max-width: 40rem) {
  .hero__badges {
    position: static;
    margin: var(--sp-6) var(--gutter) 0;
    display: flex;
  }
  .hero { padding-bottom: var(--sp-6); }
  /* On a phone the hero already fills the viewport, so the scroll cue is
     redundant chrome — and it collides with the social tiles, which move into
     the flow at this width. */
  .scroll-cue { display: none; }
  .media-pair > :nth-child(2) { margin-top: var(--sp-4); }
  .event { flex-wrap: wrap; }
  .event__more { margin-left: 0; width: 100%; }
}

/* ---------- 25. Accessibility preferences ---------- */

/* Reduced motion keeps the feedback, drops the travel. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 200ms !important;
    scroll-behavior: auto !important;
  }
  .reveal,
  .reveal--scale,
  .reveal--line { opacity: 1; transform: none; clip-path: none; transition: none; }
  .hero__media img,
  .hero__media video { transform: none; }
  .scroll-cue__dot { animation: none; opacity: 1; }
  .to-top { transition: opacity 200ms linear; transform: none; }
  .to-top.is-visible { transform: none; }
  .sheet__panel { transition: opacity 240ms linear; }
}

@media (prefers-reduced-transparency: reduce) {
  .glass,
  .header.is-stuck,
  .menu-btn,
  
  .map-cta,
  .to-top,
  .sheet__panel,
  .hero__badges {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }
  .glass { background: #fff; }
  .header.is-stuck .header__inner { min-height: 66px; }
.header.is-stuck { background: #fff; }
  .menu-btn { background: #fff; }
   .map-cta, .to-top { background: #fff; }
  .sheet__panel { background: #fff; }
  .hero__badges { background: var(--blue-800); }
  .btn--glass { background: rgba(255, 255, 255, 0.9); color: var(--blue); }
}

@media (prefers-contrast: more) {
  :root { --line: rgba(16, 32, 46, 0.42); --line-strong: rgba(16, 32, 46, 0.7); --ink-2: #1d2c37; --ink-3: #33454f; }
  .glass { background: #fff; border-color: var(--line-strong); }
  .header.is-stuck .header__inner { min-height: 66px; }
.header.is-stuck { background: #fff; }
  .btn { border: 2px solid transparent; }
  .body-text { color: var(--ink); }
  .section--blue .body-text, .section--blue .lede, .step__body { color: #fff; }
  .footer { background: #011d33; }
  .footer__contact, .footer__link { color: #fff; }
}

@media print {
  .header, .sheet, .to-top, .scroll-cue, .hero__badges { display: none !important; }
  .page { padding-top: 0; }
  .reveal { opacity: 1 !important; transform: none !important; clip-path: none !important; }
  body { color: #000; }
}

/* ---------- 26. Social tiles ----------
   The brand marks are circular, so the tile is circular too — a rounded square
   around a round mark just reads as an empty box. */

.social-tiles { display: flex; flex-wrap: wrap; gap: 0.6rem; }

/* The Home social strip is copy plus its links — the links belong with the
   copy, not floating alone at the far edge of the grid. */
.social-strip .split__body { align-content: center; }
.social-strip .social-tiles { margin-top: var(--sp-5); }

.social-tile {
  display: block;
  width: 52px;
  height: 52px;
  flex: 0 0 52px;
  border-radius: 50%;
  overflow: hidden;
  /* The glyph is a transparent cut-out in a white disc — it only reads on the
     brand blue, never on white. */
  background: var(--blue);
  box-shadow: var(--shadow-2);
  border: 1px solid rgba(255, 255, 255, 0.5);
  transition: transform var(--dur-2) var(--ease-standard), box-shadow var(--dur-2) var(--ease-standard);
}
.social-tile:hover { transform: translateY(-3px) scale(1.04); box-shadow: var(--shadow-3); }
.social-tile:active { transform: scale(0.93); }
/* `contain`, not `cover`: the disc is the full bleed of the source and
   cropping it shaves the bottom off the glyph. */
.social-tile img { width: 100%; height: 100%; object-fit: contain; border-radius: 50%; }

/* ---------- 27. XSEED programme ----------
   The original site shipped a full-colour lockup, a hand-drawn cycle diagram
   and an icon per step. They are line art on white, so they read on a light
   plate and need the brand green to sit against. */

.lockup {
  padding: clamp(1rem, 2vw, 1.6rem);
  border-radius: var(--radius-lg);
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-2);
}
.lockup img { width: 100%; height: auto; object-fit: contain; }

.step__glyph img { width: 34px; height: 34px; object-fit: contain; }

.xseed-principles { display: grid; gap: var(--sp-3); }
.xseed-principle {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4);
  border-radius: var(--radius);
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-2) var(--ease-standard), box-shadow var(--dur-2) var(--ease-standard);
}
.xseed-principle:hover { transform: translateY(-3px); box-shadow: var(--shadow-3); }
.xseed-principle__icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  border-radius: 12px;
  background: var(--green-tint);
}
.xseed-principle__icon img { width: 24px; height: 24px; object-fit: contain; }
.xseed-principle__label {
  font-size: var(--step-0);
  font-weight: 620;
  letter-spacing: -0.008em;
  line-height: 1.3;
}


/* The hero title animates in, but NOT via `.reveal` / `.reveal--line`.
   Those begin at `opacity: 0` / `clip-path: inset(0 0 100% 0)`, which removes
   the element from the largest-contentful-paint candidate set — LCP would then
   silently report a header link, and any regression in hero weight or speed
   would be invisible in the field. This variant never reaches opacity 0. */
.hero__title--enter {
  animation: hero-title-in 760ms var(--ease-standard) both;
}
@keyframes hero-title-in {
  from { opacity: 0; transform: translate3d(0, 18px, 0); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .hero__title--enter { animation: none; }
}

/* ---------- 28. Forced colours ----------
   Under `forced-colors` the browser drops background images and backdrops, so
   the white disc social tiles lose the cut-out glyph entirely and the frosted
   chrome loses its separation. Restore a visible boundary and a visible mark. */
@media (forced-colors: active) {
  .social-tile { border: 2px solid CanvasText; }
  .social-tile img { forced-color-adjust: none; }

  .glass,
  .header.is-stuck,
  .sheet__panel,
  
  .map-cta,
  .to-top { background: Canvas; border: 1px solid CanvasText; }

  .hero { background: Canvas; }
  .hero__scrim { background: linear-gradient(180deg, Canvas 30%, rgba(0, 0, 0, 0.72)); }
  .hero__title, .hero__sub, .hero__content { color: CanvasText; }

  .section--blue, .footer { background: Canvas; color: CanvasText; }
  .section--blue .body-text, .section--blue .lede, .step__body,
  .footer__link, .footer__contact, .footer__address { color: CanvasText; }
  .step { border: 1px solid CanvasText; }

  .lang__menu { background: Canvas; border: 1px solid CanvasText; }
  .lang__option[aria-checked="true"] { outline: 1px solid CanvasText; outline-offset: -1px; }

  .reveal, .reveal--scale, .reveal--line {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
  }
}

/* A focus ring that survives on the dark footer. */
.footer :focus-visible,
.section--blue :focus-visible {
  outline-color: var(--green);
  outline-offset: 3px;
}

/* Tap targets: nothing interactive smaller than 44px on a touch device. */
@media (pointer: coarse) {
  .nav-link { min-height: 44px; display: inline-flex; align-items: center; }
  .footer__link { min-height: 40px; display: inline-flex; align-items: center; }
  .social-tile { width: 48px; height: 48px; flex-basis: 48px; }
  .link-underline { padding-block: 0.35rem; }
}

/* ---------- 29. XSEED method ----------
   The cycle diagram is black line art on a white ground. It therefore sits
   DIRECTLY on the page: no card, no shadow, no padding. Wrapping it in a
   white panel on a white section only added a seam and made a small graphic
   look like a pasted-on image. */

.cycle {
  margin: var(--sp-6) auto var(--sp-7);
  max-width: 20rem;
}
.cycle img {
  display: block;
  width: 100%;
  height: auto;
  object-fit: contain;
}

.xsteps {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12.5rem), 1fr));
}
@media (min-width: 62rem) {
  /* All five steps on one line — a 4 + 1 wrap leaves a single orphan card. */
  .xsteps { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

.xstep {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-5) var(--sp-4) var(--sp-4);
  border-radius: var(--radius-lg);
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  overflow: hidden;
  transition: transform var(--dur-2) var(--ease-standard), box-shadow var(--dur-2) var(--ease-standard);
}
.xstep:hover { transform: translateY(-4px); box-shadow: var(--shadow-2); }

/* The hand-drawn mark sits on the card it belongs to, above the title. */
.xstep__mark {
  display: grid;
  place-items: center;
  width: 68px;
  height: 68px;
  margin-bottom: var(--sp-2);
  border-radius: 18px;
  background: var(--green-tint);
  transition: background-color var(--dur-2) var(--ease-standard), transform var(--dur-2) var(--ease-standard);
}
.xstep:hover .xstep__mark { transform: rotate(-4deg) scale(1.04); }
.xstep__mark img { width: 46px; height: 46px; object-fit: contain; }

.xstep__num {
  position: absolute;
  top: var(--sp-4);
  right: var(--sp-4);
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--green);
  opacity: 0.65;
  font-variant-numeric: tabular-nums;
}

.xstep__title {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 660;
  letter-spacing: -0.018em;
  line-height: 1.2;
  color: var(--ink);
}
.xstep__body {
  font-size: var(--step-0);
  line-height: 1.58;
  color: var(--ink-2);
}

/* ---------- 30. Campaign call to action ----------
   The admissions clip is a designed poster, so it is given a 16:9 frame with
   the copy beside it rather than being letterboxed into a portrait column.
   The campaign reads at any size now, and it is where a recruitment video
   belongs: next to the button that acts on it. */

.campaign {
  display: grid;
  gap: clamp(1.75rem, 4vw, 3.5rem);
  align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 56rem) {
  .campaign { grid-template-columns: minmax(0, 0.62fr) minmax(0, 1fr); }
}

.campaign .media {
  /* The clip is 402x720 portrait poster artwork. A 16:9 frame letterboxes it
     down to an illegible sliver, so it keeps a portrait frame at a size where
     its own campaign text can actually be read. */
  aspect-ratio: 4 / 5;
  background: var(--blue-800);
}
/* `contain` letterboxes the portrait clip inside the frame: its own campaign
   text is the content, so it must never be cropped. */
.campaign .media video,
.campaign .media img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  display: block;
}

.campaign__body { display: grid; gap: var(--sp-3); align-content: center; }

@media (max-width: 56rem) {
  .campaign .media { max-width: 22rem; margin-inline: auto; }
}

/* An outline button for LIGHT backgrounds. `.btn--glass` is white text on a
   translucent white fill, which is correct over a photo or the deep-blue band
   and completely invisible on a white page. */
/* A primary button for the deep-blue band: the brand blue disappears into it. */
.btn--on-dark {
  --btn-bg: #fff;
  --btn-fg: var(--blue);
  box-shadow: 0 10px 28px rgba(1, 47, 82, 0.3);
}
.btn--on-dark:hover { background: var(--blue-tint); color: var(--blue-600); }

.btn--outline {
  --btn-bg: transparent;
  --btn-fg: var(--blue);
  border: 1.5px solid var(--blue);
  box-shadow: none;
}
.btn--outline:hover {
  background: var(--blue-tint);
  color: var(--blue-600);
  box-shadow: none;
}

.nextcard {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  min-height: 11rem;
}
.nextcard__body { font-size: var(--step-0); color: var(--ink-3); }
.nextcard__go {
  margin-top: auto;
  padding-top: var(--sp-4);
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--step-0);
  font-weight: 650;
  color: var(--blue);
  transition: gap var(--dur-2) var(--ease-standard);
}
.nextcard:hover .nextcard__go { gap: 0.6rem; }
.nextcard__go svg { width: 15px; height: 15px; }

/* ---------- 31. Staff grid on small screens ----------
   One full-width portrait per row made the teachers page over 10,000px tall on
   a phone: fifteen screens of scrolling to meet fifteen people. Two columns
   with a 3:4 portrait halves it and still reads. */
@media (max-width: 34rem) {
  .grid--staff { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
  .grid--staff .staff-card__body { padding-inline: 0.25rem; }
  .staff-card__media { aspect-ratio: 3 / 4; }
}

/* The visit block: copy + campaign on one side, the form needs room on the
   other. Three equal columns squeezed all three. */
.visit {
  display: grid;
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: start;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 62rem) {
  .visit { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); }
  .visit__aside { display: grid; gap: var(--sp-5); align-content: start; }
}
@media (max-width: 61.99rem) {
  .visit__aside { order: 2; }
}

/* The date selects open blank, which reads as an unfinished control. */
.field__control:invalid,
.field__control[data-placeholder] { color: var(--ink-3); }

/* ---------- 32. Film grain over photography ----------
   Several of the school's originals are far smaller than the box they are
   shown in — the smallest is 258px wide. Those are enlarged on a Lanczos3
   kernel and lightly unsharp-masked at build time, which holds the edges far
   better than a browser stretch. What no upscale can invent is fine texture,
   so a very low film grain sits over the hero: it breaks up the smooth
   gradients an upscale leaves behind and reads as photographic depth rather
   than as softness. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: -2%;
  pointer-events: none;
  opacity: 0.07;
  mix-blend-mode: soft-light;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='140' height='140' filter='url(%23n)'/></svg>");
  background-size: 140px 140px;
}

/* A whisper of defocus at the frame edge: the subject stays crisp while the
   corners fall away, so a soft photograph still has a clear focal point. */
.hero__media::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(120% 90% at 50% 45%, transparent 45%, rgba(1, 47, 82, 0.28) 100%);
}

@media (prefers-reduced-transparency: reduce) {
  .hero__media::after { display: none; }
  .lang__menu { -webkit-backdrop-filter: none; backdrop-filter: none; background: #fff; }
}

/* A curriculum subject with no photograph in the original. The band carries
   the subject's numeral over the brand tint so it reads as a deliberate
   absence rather than a frame that failed to load. */
.subject__placeholder {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-size: clamp(3.5rem, 9vw, 5.5rem);
  font-weight: 700;
  letter-spacing: -0.05em;
  line-height: 1;
  color: #fff;
  opacity: 0.42;
}
/* A hairline so the panel has an edge rather than dissolving. */
.subject__media--empty::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid rgba(255, 255, 255, 0.22);
  pointer-events: none;
}

/* ---------- 33. Typographic masthead ----------
   Seven of the school's pages carry no photograph. Borrowing another page's
   picture to fill a hero is the single easiest way to make a site read as
   dishonest, so those pages get a masthead instead: the brand wash, the
   school's own lockup at large scale, and the type. */
.hero--plain,
.has-hero .hero--plain {
  background: var(--blue-800);
  min-height: min(58svh, 520px);
  isolation: isolate;
}
@media (max-width: 48rem) {
  .hero--plain, .has-hero .hero--plain { min-height: min(46svh, 400px); }
}
.hero--plain .hero__media--plain {
  z-index: -2;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.hero__wash {
  width: min(62rem, 150%);
  max-width: none;
  height: auto;
  object-fit: contain;
  transform: none;
  opacity: 0.1;
  filter: saturate(0.6);
}
.hero__scrim--plain {
  background:
    radial-gradient(90% 120% at 12% 0%, rgba(139, 197, 63, 0.22), transparent 58%),
    linear-gradient(180deg, rgba(1, 32, 57, 0.55), rgba(1, 32, 57, 0.86));
}
/* A short typographic masthead is centred rather than pinned to the bottom:
   bottom-aligned, a single line of type leaves the top half of the frame
   empty and the block reads as an accident. */
.hero--plain { align-items: center; }
.hero--plain .hero__content { max-width: 52rem; }
@media (min-width: 60rem) {
  .hero--plain .hero__content { padding-left: 8%; }
}

/* ---------- 34. Typographic statement ----------
   Where the original gives no photograph, a section earns its space with type
   and a brand wash rather than by borrowing a picture that is already on the
   page. */
.statement {
  display: grid;
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 60rem) {
  .statement { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); }
}

.statement__media {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 16rem;
  padding: var(--sp-6) var(--sp-5);
  border-radius: var(--radius-lg);
  background: linear-gradient(150deg, var(--blue-800), var(--blue) 70%, var(--blue-600));
  box-shadow: var(--shadow-3);
  overflow: hidden;
}
.statement__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(70% 90% at 30% 0%, rgba(139, 197, 63, 0.28), transparent 62%);
}
.statement__mark {
  position: relative;
  z-index: 1;
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 3.4vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  text-align: center;
  color: #fff;
  max-width: 12ch;
}

.statement__body { display: grid; gap: var(--sp-3); align-content: center; }

/* ---------- 32. Header fit with the language switcher ----------
   The switcher is the one new piece of chrome, and on desktop it shares the
   row with seven nav links. Beside them it drops the caret and some pill
   padding (it keeps the globe and the language code). In the narrow band
   just above the nav breakpoint the English links also give up a little of
   their side padding, so the row still fits in one line at 1025px. Outside
   that band the English nav is exactly as it was. */

@media (min-width: 64.01rem) {
  .lang__trigger { padding-inline: 0.56rem; }
  .lang__trigger .nav__caret { display: none; }
}
@media (min-width: 64.01rem) and (max-width: 70rem) {
  .nav-link { padding-inline: 0.56rem; }
  .nav-link[aria-current="page"]::after { left: 0.56rem; right: 0.56rem; }
}

/* ---------- 33. Russian and Uzbek ----------
   Everything here is scoped by `:lang()`, which follows <html lang>, so the
   English rendering is untouched. Uzbek is set in Latin, but it shares the
   problems Russian has with this system: words run long (agglutination,
   oʻ / gʻ / ʼ modifier letters), and capitals set from a template read as
   shouting. */

/* Navigation. Translated labels are 20–35% wider than the English ones, and
   seven of them do not fit beside the brand until ~1180px. Below that the
   row collapses to the menu button, the same as English does on a tablet,
   rather than wrapping or running under the switcher. */
@media (max-width: 74rem) {
  html:lang(ru) .nav,
  html:lang(uz) .nav { display: none; }
  html:lang(ru) .menu-btn,
  html:lang(uz) .menu-btn { display: grid; }
  /* Beside the menu button the trigger keeps its phone shape. */
  html:lang(ru) .lang__trigger,
  html:lang(uz) .lang__trigger { padding-inline: 0.6rem 0.66rem; }
  html:lang(ru) .lang__trigger .nav__caret,
  html:lang(uz) .lang__trigger .nav__caret { display: block; }
}
@media (min-width: 74.01rem) {
  html:lang(ru) .nav-link,
  html:lang(uz) .nav-link { padding-inline: 0.56rem; }
  html:lang(ru) .nav-link[aria-current="page"]::after,
  html:lang(uz) .nav-link[aria-current="page"]::after { left: 0.56rem; right: 0.56rem; }
}

/* Display tracking. -0.032em is tuned for Latin at display sizes, where
   counters are open and there are ascenders to separate the letters.
   Cyrillic lower case is x-height-dense (н, п, и, ш, щ are all stems), and
   at -0.032em the stems of neighbouring letters close up; Uzbek's oʻ/gʻ
   marks collide with the next letter the same way. Tracking is relaxed
   roughly by half at each step, and stays size-specific. */
html:lang(ru) :is(.display-1, .display-2),
html:lang(uz) :is(.display-1, .display-2) { letter-spacing: -0.014em; line-height: 1.08; }
html:lang(ru) .display-3,
html:lang(uz) .display-3 { letter-spacing: -0.01em; line-height: 1.16; }
html:lang(ru) :is(.h4, .hero__sub, .xstep__title, .statement__mark, .empty__title),
html:lang(uz) :is(.h4, .hero__sub, .xstep__title, .statement__mark, .empty__title) { letter-spacing: -0.006em; }
html:lang(ru) :is(.h5, .sheet__link, .staff-card__name, .event__title, .media__badge, .map-frame__label),
html:lang(uz) :is(.h5, .sheet__link, .staff-card__name, .event__title, .media__badge, .map-frame__label) { letter-spacing: -0.004em; }

/* Headings never hyphenate, but a long compound must still be allowed to
   break rather than push past a 390px screen. */
html:lang(ru) :is(.display-1, .display-2, .display-3, .h4, .h5),
html:lang(uz) :is(.display-1, .display-2, .display-3, .h4, .h5) { overflow-wrap: break-word; hyphens: manual; }

/* The largest display step is sized for "Welcome to Paramount International
   School". The Russian and Uzbek headlines are a third longer, so the top
   of the scale comes down a notch on the widest screens only. */
html:lang(ru) .display-1,
html:lang(uz) .display-1 { font-size: clamp(2.3rem, 1.6rem + 3.3vw, 4.6rem); }

/* Labels. The English eyebrows, row labels, footer titles and scroll cue are
   uppercase and widely tracked. Russian and Uzbek readers take a paragraph
   of capitals as shouting, and the copy for these locales is written in
   sentence case, so it is shown as written: no transform, and the
   capital-only tracking goes with it. */
html:lang(ru) :is(.eyebrow, .contact-row__label, .footer__title, .scroll-cue),
html:lang(uz) :is(.eyebrow, .contact-row__label, .footer__title, .scroll-cue) {
  text-transform: none;
  letter-spacing: 0.005em;
}
html:lang(ru) :is(.eyebrow, .contact-row__label),
html:lang(uz) :is(.eyebrow, .contact-row__label) { font-size: var(--step-0); font-weight: 620; }
html:lang(ru) .scroll-cue,
html:lang(uz) .scroll-cue { font-size: 0.75rem; }
@media (min-width: 34rem) {
  html:lang(ru) .contact-row__label,
  html:lang(uz) .contact-row__label { flex-basis: 11rem; }
}

/* Running text. The 68ch measure holds ~62–66 Cyrillic characters, because
   Cyrillic lower case is a little wider than the "0" a `ch` is measured on
   — still inside the 60–75 a reader expects, so the measure stays. What
   Russian does need is hyphenation: its words are long enough that an
   unhyphenated ragged right turns into rivers in a 56ch column. Chromium
   and Safari ship a Russian dictionary; Uzbek has none, so `auto` is a
   no-op there and the rag is left as it is. */
html:lang(ru) :is(.body-text, .lede, .step__body, .xstep__body, .empty__body, .nextcard__body),
html:lang(uz) :is(.body-text, .lede, .step__body, .xstep__body, .empty__body, .nextcard__body) {
  hyphens: auto;
  -webkit-hyphens: auto;
  hyphenate-limit-chars: 7 3 3;
}

/* Buttons are `nowrap` so an English label never breaks. A few translated
   labels ("Запишитесь на визит уже сегодня!") are wider than a phone, so
   they may wrap onto a second centred line instead of running off-screen. */
html:lang(ru) .btn,
html:lang(uz) .btn { white-space: normal; text-align: center; line-height: 1.3; }
html:lang(ru) .btn > span,
html:lang(uz) .btn > span { min-width: 0; }

/* The big XSEED statement mark is capped at 12ch — enough for "The Five-Step
   Teaching Method", not for "Пятиэтапный метод обучения". */
html:lang(ru) .statement__mark,
html:lang(uz) .statement__mark { max-width: 15ch; overflow-wrap: break-word; }

/* ---------- 35. Composed masthead ----------
   For a page whose only photograph is much smaller than the viewport, the
   photograph is shown near its own size and the school ground carries the
   headline. The image is then used about 1:1 instead of being stretched four
   times, which is the only way a small original can look deliberate. */

.chero {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  min-height: min(100svh, 860px);
  background: var(--blue-800);
  color: #fff;
  isolation: isolate;
}
@media (min-width: 62rem) {
  .chero { grid-template-columns: minmax(0, 48fr) minmax(0, 52fr); }
  .chero--flip { grid-template-columns: minmax(0, 52fr) minmax(0, 48fr); }
  .chero--flip .chero__panel { order: 2; }
}

.chero__panel {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-6);
  padding: clamp(2.5rem, 6vw, 5rem) var(--gutter) clamp(2rem, 5vw, 3.5rem);
  /* The brand green enters as a wash from the top corner, so the panel is
     never a flat field of one blue. */
  background:
    radial-gradient(120% 90% at 0% 0%, rgba(139, 197, 63, 0.26), transparent 58%),
    radial-gradient(90% 120% at 100% 100%, rgba(1, 90, 156, 0.55), transparent 62%),
    linear-gradient(165deg, var(--blue-800), #012741 70%, #02121f);
  overflow: hidden;
}
/* A hairline where the two halves meet, instead of a hard cut. */
.chero__panel::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-end: -1px;
  width: 1px;
  background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.28), transparent);
}

.chero__content { position: relative; z-index: 1; max-width: 40rem; }

.chero__title {
  color: #fff;
  margin-top: 0.2em;
}
.chero__title--enter { animation: hero-title-in 760ms var(--ease-standard) both; }

.chero__sub {
  margin-top: 0.5em;
  font-size: var(--step-2);
  font-weight: 600;
  letter-spacing: -0.018em;
  line-height: 1.2;
  color: #a9dd6a;
  text-shadow: 0 1px 14px rgba(1, 32, 57, 0.6);
}

.chero__body {
  margin-top: var(--sp-4);
  max-width: 34ch;
  font-size: var(--step-1);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.84);
}

.chero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-top: var(--sp-6);
}

/* The school name as a quiet watermark, bottom-left of the panel. */
.chero__mark {
  position: relative;
  z-index: 1;
  font-size: var(--step--1);
  font-weight: 650;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.34);
}

.chero__media {
  position: relative;
  margin: 0;
  min-height: 46svh;
  overflow: hidden;
  background: #04121e;
}
.chero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* A soft top edge so the photograph settles into the panel instead of
   butting against it. */
.chero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(1, 32, 57, 0.45), transparent 26%, transparent 72%, rgba(1, 32, 57, 0.4)),
    radial-gradient(120% 100% at 50% 50%, transparent 55%, rgba(1, 32, 57, 0.3));
}

.chero__credit {
  position: absolute;
  left: var(--gutter);
  bottom: var(--sp-4);
  font-size: var(--step--1);
  color: rgba(255, 255, 255, 0.66);
  z-index: 1;
}

@media (max-width: 61.99rem) {
  /* The header is fixed, so the panel has to start below it. */
  .chero__panel { padding-top: 6.5rem; }
  .chero__media { min-height: 52svh; }
  .chero__content { max-width: none; }
  .chero__title { font-size: clamp(2.2rem, 8.4vw, 3.4rem); }
  .chero__actions .btn { flex: 1 1 auto; }
}

/* ---------- 36. Header over the composed masthead ----------
   The masthead is two materials — brand ground and photograph — so a single
   flat header colour cannot stay legible across both. A soft strip behind the
   header keeps white navigation readable wherever it crosses the image. */
.has-chero .header {
  background: linear-gradient(180deg, rgba(1, 32, 57, 0.9) 0%, rgba(1, 32, 57, 0.72) 58%, rgba(1, 32, 57, 0.34) 84%, transparent 100%);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow: none;
}
.has-chero .header::after { display: none; }
.has-chero .header__inner { min-height: 78px; }
.has-chero .header.is-stuck {
  background: var(--mat-chrome);
  -webkit-backdrop-filter: blur(var(--blur-chrome)) saturate(180%);
  backdrop-filter: blur(var(--blur-chrome)) saturate(180%);
  box-shadow: 0 1px 0 rgba(16, 32, 46, 0.07), 0 8px 30px rgba(16, 32, 46, 0.08);
}
.has-chero .header.is-stuck::after { display: block; }
.has-chero .header.is-stuck .nav-link { color: var(--ink-2); }
.has-chero .header.is-stuck .nav-link:hover,
.has-chero .header.is-stuck .nav-link[aria-current="page"] { color: var(--blue); }
.has-chero .header.is-stuck .brand { background: transparent; box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
.has-chero .header.is-stuck .nav__sublink { color: var(--ink); }
.has-chero .header.is-stuck .menu-btn { background: #fff; border-color: var(--line); }
.has-chero .header.is-stuck .menu-btn__bars i { background: var(--ink); }

/* ---------- 37. A page's own photograph, in the body ----------
   On the original, only the homepage carries a full-bleed photograph. Every
   other page sets a masthead, then a wide photograph band, then its text.
   Promoting one of those body photographs into a hero is what made a botanical
   plate read as a misplaced hero shot. */
.media--wide { aspect-ratio: 3 / 2; }

.plate-figure {
  max-width: 62rem;
  margin-inline: auto;
}
.plate-figure .media {
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-3);
}

.reading {
  display: grid;
  gap: var(--sp-3);
  max-width: 58ch;
  margin-inline: auto;
  text-align: center;
  justify-items: center;
}

/* A contact row carrying more than one value stacks them, one per line. */
.contact-row__stack {
  display: grid;
  gap: 0.35rem;
  flex: 1 1 14rem;
  min-width: 0;
}
.contact-row__stack .contact-row__value { line-height: 1.45; }
