/* ===========================================================================
   Public school website — design system.

   Every selector the Razor views use is defined here; the markup is unchanged.
   Institutional colour comes from the tenant's branding (--brand / --accent are
   set inline per site), so the system is built on neutrals and lets those two
   values carry the identity.
   =========================================================================== */

:root {
  /* Set per site by the layout; these are safe fallbacks. */
  --brand: #0f2d5c;
  --accent: #c9a227;

  --ink: #131b2b;
  --ink-soft: #47536b;
  --muted: #667287;
  --faint: #8b95a7;

  --canvas: #fbfaf8;
  --surface: #ffffff;
  --surface-soft: #f5f4f1;
  --line: rgba(19, 27, 43, .11);
  --line-strong: rgba(19, 27, 43, .18);

  --shadow-xs: 0 1px 2px rgba(16, 24, 40, .05);
  --shadow-sm: 0 2px 8px rgba(16, 24, 40, .06);
  --shadow-md: 0 12px 28px rgba(16, 24, 40, .09);
  --shadow-lg: 0 28px 64px rgba(16, 24, 40, .14);

  --r-xs: .5rem;
  --r-sm: .75rem;
  --r-md: 1rem;
  --r-lg: 1.375rem;
  --r-xl: 1.875rem;

  --gutter: clamp(1.25rem, 4vw, 2.5rem);
  --section-y: clamp(3.25rem, 6.5vw, 5.5rem);
  --ease: cubic-bezier(.22, .61, .36, 1);

  --font-display: "Cormorant Garamond", Georgia, serif;
  --font-body: "Source Sans 3", "Segoe UI", system-ui, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
html, body { margin: 0; padding: 0; }

body {
  min-height: 100vh;
  color: var(--ink);
  background: var(--canvas);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
img, iframe { display: block; max-width: 100%; border: 0; }
::selection { background: color-mix(in srgb, var(--accent) 32%, transparent); }

:where(a, button, input, textarea, select, [tabindex]):focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 3px;
  border-radius: 4px;
}

/* The content band. It held at 1200px however wide the screen was, so on a large monitor the
   whole site ran down the middle third with the notice strip and the banner going edge to edge
   either side of it — the header, the menu and every section boxed into a strip while the
   picture above them filled the glass.
 
   It now grows with the window past about 1540px, up to 1520px. Below that nothing moves, so
   laptops are unchanged; and the cap keeps a line of text a readable length on a very wide
   display. Body copy has its own narrower measure regardless. */
.shell {
  width: min(clamp(1200px, 88vw, 1520px), calc(100% - var(--gutter) * 2));
  margin-inline: auto;
}

/* Typography --------------------------------------------------------------- */

h1, h2, h3 {
  margin: 0 0 .6rem;
  color: var(--ink);
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -.015em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.5rem, 5.4vw, 4.1rem); line-height: 1.04; letter-spacing: -.025em; }
h2 { font-size: clamp(1.75rem, 3vw, 2.5rem); }
h3 { font-size: clamp(1.15rem, 1.6vw, 1.35rem); font-family: var(--font-body); font-weight: 650; letter-spacing: -.01em; }

p { margin: 0 0 1rem; color: var(--ink-soft); line-height: 1.7; }
p:last-child { margin-bottom: 0; }

.eyebrow {
  /* Inline, not block: a flex box fills its container and then ignores the text-align that
     centres everything around it, which left the eyebrow hard against the left margin of every
     centred section while its own heading sat in the middle. As an inline box it goes wherever
     the surrounding text is aligned, and is unchanged in left-aligned sections. */
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  margin: 0 0 .85rem;
  color: var(--accent);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
}
.eyebrow::before {
  width: 1.6rem;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
  content: "";
}

.lede { max-width: 44rem; color: var(--ink-soft); font-size: clamp(1.05rem, 1.4vw, 1.2rem); line-height: 1.62; }
.muted { color: var(--muted); }
.name { margin: 0; color: var(--brand); font-weight: 700; }

.rich-text { max-width: 46rem; }
.rich-text h2, .rich-text h3 { margin-top: 2rem; }
.rich-text ul, .rich-text ol { margin: 0 0 1rem; padding-left: 1.35rem; color: var(--ink-soft); }
.rich-text li { margin-bottom: .4rem; }
.rich-text a { color: var(--brand); text-decoration: underline; text-underline-offset: 2px; }

/* Announcement bar --------------------------------------------------------- */

.notice-bar {
  color: #fff;
  background: var(--brand);
  font-size: .875rem;
}
.notice-bar .shell { padding-block: .6rem; }
.notice-bar span { display: block; font-weight: 600; letter-spacing: .005em; }

/* Header ------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 30;
  /* Solid, and with no rule under it.
     It used to be 88% opaque over a blur, which let the banner underneath show through as a
     milky ghost with a hairline drawn across the bottom of it — a bar over the top of the
     picture, and the one thing keeping the header and the banner from reading as one piece. */
  background: var(--surface);
}
.site-header .shell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  min-height: 76px;
  /* The brand, the contact blocks, the button and the menu are four things on one row. When
     they do not fit — a long school name, ten menu items, a laptop rather than a desktop — the
     menu used to run off the side of the page instead of moving, so the last item sat under
     the edge of the window and the whole page scrolled sideways to reach it. It wraps now. */
  flex-wrap: wrap;
}
.site-header nav { flex-wrap: wrap; }

.brand { display: flex; align-items: center; gap: .8rem; min-width: 0; }
.brand img {
  /* Schools send crests of wildly different proportions, so the height is theirs to set from
     the console. The width follows so nothing is squashed.

     The negative margin absorbs whatever the crest gains over the default, so a taller logo
     hangs past the bar instead of pushing it open: a bigger crest was the ask, a fatter header
     that shoves the whole page down was not. */
  width: auto;
  height: var(--logo-size, 46px);
  margin-block: calc((var(--logo-size, 46px) - 46px) / -2);
  max-width: min(220px, 40vw);
  flex: none;
  object-fit: contain;
}
/* The name is set to truncate rather than wrap, but an ellipsis only appears if the box is
   allowed to be narrower than the text. Without this the name kept its full width and ran
   straight over the phone number beside it. */
.brand > span { min-width: 0; }
.brand strong {
  display: block;
  overflow: hidden;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 1.9vw, 1.5rem);
  font-weight: 600;
  letter-spacing: -.015em;
  line-height: 1.15;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.brand small {
  display: block;
  overflow: hidden;
  color: var(--faint);
  font-size: .76rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.site-header nav { display: flex; align-items: center; gap: .1rem; justify-content: flex-end; }
.site-header nav a {
  position: relative;
  padding: .55rem .75rem;
  color: var(--ink-soft);
  font-size: .93rem;
  font-weight: 600;
  transition: color .18s var(--ease);
}
.site-header nav a::after {
  position: absolute;
  inset: auto .75rem .3rem;
  height: 2px;
  background: var(--accent);
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .22s var(--ease);
  content: "";
}
.site-header nav a:hover { color: var(--brand); }
.site-header nav a:hover::after, .site-header nav a.is-active::after { transform: scaleX(1); }
.site-header nav a.is-active { color: var(--brand); }

.nav-toggle {
  display: none;
  padding: .55rem .85rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-xs);
  font: inherit;
  font-size: .88rem;
  font-weight: 650;
  cursor: pointer;
}

/* Buttons ------------------------------------------------------------------ */

.btn, .contact-form button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  padding: .8rem 1.4rem;
  color: #1c1403;
  background: var(--accent);
  border: 1px solid transparent;
  border-radius: var(--r-xs);
  box-shadow: var(--shadow-xs);
  font: inherit;
  font-size: .95rem;
  font-weight: 700;
  letter-spacing: .005em;
  cursor: pointer;
  transition: transform .18s var(--ease), box-shadow .18s var(--ease), filter .18s var(--ease);
}
.btn:hover, .contact-form button:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
  filter: brightness(1.04);
}
.btn:active, .contact-form button:active { transform: none; }

.btn-ghost {
  color: #fff;
  background: transparent;
  border-color: rgba(255, 255, 255, .5);
  box-shadow: none;
}
.btn-ghost:hover { background: rgba(255, 255, 255, .1); box-shadow: none; filter: none; }

.text-link {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  margin-top: 1rem;
  color: var(--brand);
  font-weight: 700;
  font-size: .95rem;
}
.text-link::after { transition: transform .18s var(--ease); content: "→"; }
.text-link:hover::after { transform: translateX(3px); }

.cta-row { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1.75rem; }

.tag {
  display: inline-flex;
  align-items: center;
  padding: .2rem .65rem;
  color: var(--brand);
  background: color-mix(in srgb, var(--brand) 8%, transparent);
  border-radius: 999px;
  font-size: .69rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
}

/* Hero --------------------------------------------------------------------- */

.hero {
  position: relative;
  display: grid;
  align-items: end;
  min-height: min(78vh, 720px);
  padding: 7rem 0 5rem;
  overflow: hidden;
  color: #fff;
  /* One shade, low and behind the copy only.
     There used to be two, each covering the whole frame — a dark wash from the bottom and a
     second from the left — and together they greyed out the entire banner. Since the slideshow
     started working they were painted over the pictures as well, so a school's own banner
     arrived on the page dimmed end to end. The top three fifths are untouched now, and the
     shade only deepens where the heading and the buttons actually sit. */
  background:
    linear-gradient(to top, rgba(7, 16, 33, .72) 0%, rgba(7, 16, 33, .34) 22%, transparent 58%),
    var(--hero) center / cover no-repeat;
}
/* There used to be a 70px band across the bottom of the hero, meant to fade the picture into
   the page. --canvas is not opaque, so the two stops resolved to the same translucent white and
   it painted as a flat grey bar over the bottom of the banner instead of fading anything. The
   banner ends where it ends. */
/* The heading carries its own contrast now that the picture behind it is not dimmed. */
.hero h1 { text-shadow: 0 2px 10px rgba(7, 16, 33, .55), 0 1px 32px rgba(7, 16, 33, .45); }
.hero-copy p, .hero .shell p { text-shadow: 0 1px 8px rgba(7, 16, 33, .6); }
.hero-copy, .hero .shell { position: relative; z-index: 1; max-width: 46rem; }
.hero h1 { color: #fff; }
.hero p { color: rgba(255, 255, 255, .88); font-size: clamp(1.05rem, 1.4vw, 1.2rem); }
.hero .eyebrow { color: var(--accent); }

.hero-classic { }
.hero-campus { min-height: min(70vh, 640px); }
.hero-academic {
  background:
    linear-gradient(150deg, rgba(9, 20, 40, .95), rgba(28, 60, 100, .72)),
    var(--hero, url("https://images.unsplash.com/photo-1541339907198-e08756dedf3f?auto=format&fit=crop&w=1800&q=80")) center / cover no-repeat;
}
.hero-prestige { align-items: center; min-height: min(84vh, 800px); }

.prestige-frame {
  max-width: 46rem;
  padding: clamp(1.75rem, 4vw, 3rem);
  background: rgba(10, 16, 28, .42);
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: var(--r-lg);
  backdrop-filter: blur(10px);
}

/* Modern variant: light, split hero */
.hero-modern {
  min-height: auto;
  padding: clamp(2.5rem, 5vw, 4rem) 0 1rem;
  color: var(--ink);
  background: none;
}
.hero-modern::after { display: none; }
.hero-modern h1 { color: var(--ink); }
.hero-modern p { color: var(--ink-soft); }
.modern-grid {
  display: grid;
  grid-template-columns: 1.02fr .98fr;
  gap: clamp(1.5rem, 3vw, 2.75rem);
  align-items: center;
}
.modern-media {
  position: relative;
  min-height: 440px;
  background-size: cover;
  background-position: center;
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
}
.modern-media::after {
  position: absolute;
  inset: auto 1.25rem 1.25rem auto;
  width: 40%;
  height: 26%;
  border: 2px solid color-mix(in srgb, var(--accent) 78%, transparent);
  border-radius: var(--r-md);
  content: "";
}

/* Statistics band ---------------------------------------------------------- */

.stats-grid {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  width: min(clamp(1200px, 88vw, 1520px), calc(100% - var(--gutter) * 2));
  margin: -2.75rem auto 0;
  overflow: hidden;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
}
.stats-grid .stat { padding: 1.6rem 1rem; background: var(--surface); text-align: center; }
.stats-grid .stat strong {
  display: block;
  margin-bottom: .15rem;
  color: var(--brand);
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 3vw, 2.5rem);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.stats-grid .stat span { color: var(--muted); font-size: .85rem; font-weight: 600; letter-spacing: .02em; }

/* Sections ----------------------------------------------------------------- */

.section, .page-body { padding: var(--section-y) 0; }
.section.muted { background: var(--surface-soft); border-block: 1px solid var(--line); }
.section-head { max-width: 42rem; margin-bottom: 2rem; }
.section-head p { margin-top: .5rem; }

/* Shared card surface. Borders carry the structure; shadow only on lift. */
.panel,
.split-cards article,
.card-grid article,
.message-list article,
.person-card,
.campus-panels article,
.academic-columns article,
.people-card,
.news-item,
.event-card,
.steps li {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-xs);
}

.split-cards article,
.card-grid article,
.message-list article,
.campus-panels article,
.academic-columns article,
.people-card,
.news-item,
.event-card {
  transition: transform .2s var(--ease), box-shadow .2s var(--ease), border-color .2s var(--ease);
}
.card-grid article:hover,
.people-card:hover,
.news-item:hover,
.event-card:hover,
.campus-panels article:hover,
.academic-columns article:hover {
  transform: translateY(-2px);
  border-color: var(--line-strong);
  box-shadow: var(--shadow-md);
}

.panel { padding: clamp(1.4rem, 2.5vw, 2rem); }

.campus-panels, .academic-columns, .prestige-row, .split-cards, .card-grid, .contact-layout {
  display: grid;
  gap: 1.25rem;
}
.campus-panels, .academic-columns, .prestige-row, .split-cards {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  width: min(clamp(1200px, 88vw, 1520px), calc(100% - var(--gutter) * 2));
  margin: 2rem auto;
}

/* These blocks self-centre when used at page level (home variants). Inside a
   .shell they must not centre again, or they indent away from the body text. */
.shell .campus-panels,
.shell .academic-columns,
.shell .prestige-row,
.shell .split-cards,
.shell .cta-band,
.shell .stats-grid {
  width: 100%;
  margin-inline: 0;
}
.campus-panels article,
.academic-columns article,
.prestige-row > *,
.split-cards article,
.card-grid article,
.message-list article {
  padding: clamp(1.25rem, 2vw, 1.75rem);
}
.split-cards .wide { grid-column: 1 / -1; }

/* Headings inside a card are subheads, not section titles. */
.split-cards article h2,
.campus-panels article h2,
.academic-columns article h2,
.panel h2,
.message-list article h2 {
  font-size: clamp(1.2rem, 1.7vw, 1.45rem);
}

.card-grid { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.card-grid article h3 { margin-bottom: .4rem; }
.card-grid img {
  width: 100%;
  height: 190px;
  margin-bottom: 1rem;
  object-fit: cover;
  border-radius: var(--r-sm);
  background: var(--surface-soft);
}

.prestige-row blockquote {
  margin: 0;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 2.4vw, 1.85rem);
  line-height: 1.35;
}
.prestige-row cite {
  display: block;
  margin-top: 1rem;
  color: var(--muted);
  font-family: var(--font-body);
  font-style: normal;
  font-weight: 650;
}

/* Call-to-action band ------------------------------------------------------ */

.cta-band {
  width: min(clamp(1200px, 88vw, 1520px), calc(100% - var(--gutter) * 2));
  margin: 1rem auto 2.5rem;
  padding: clamp(2rem, 4vw, 3.25rem);
  overflow: hidden;
  color: #fff;
  background:
    radial-gradient(circle at 88% 0%, color-mix(in srgb, var(--accent) 34%, transparent), transparent 46%),
    linear-gradient(135deg, var(--brand), color-mix(in srgb, var(--brand) 72%, #ffffff));
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
}
.cta-band h2 { color: #fff; }
.cta-band p { color: rgba(255, 255, 255, .88); }

/* Inner page header -------------------------------------------------------- */

.page-shell { display: block; min-height: 55vh; }
.page-hero {
  padding: clamp(2.25rem, 4vw, 3.25rem) 0 clamp(1.5rem, 2.5vw, 2.25rem);
  background: linear-gradient(180deg, var(--surface-soft), var(--canvas));
  border-bottom: 1px solid var(--line);
}
/* An inner page is a document, not a landing page: its title sits a step below
   the homepage hero so the two never compete. */
.page-hero h1 { font-size: clamp(2rem, 3.6vw, 3rem); }
.page-hero .lede { margin-top: .5rem; }
.page-body { padding: clamp(2.25rem, 4vw, 3.25rem) 0 var(--section-y); }
.page-body > * + * { margin-top: 2rem; }

/* Admission steps ---------------------------------------------------------- */

.steps ol { margin: 0; padding: 0; list-style: none; display: grid; gap: .75rem; }
.steps li {
  display: grid;
  gap: .3rem;
  padding: 1.1rem 1.25rem;
  counter-increment: step;
}
.steps ol { counter-reset: step; }
.steps li strong { color: var(--brand); font-weight: 700; }
.steps li strong::before {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.55rem;
  height: 1.55rem;
  margin-right: .6rem;
  color: var(--brand);
  background: color-mix(in srgb, var(--brand) 9%, transparent);
  border-radius: 50%;
  font-size: .8rem;
  font-variant-numeric: tabular-nums;
  content: counter(step);
}
.steps li span { color: var(--ink-soft); }

/* Gallery ------------------------------------------------------------------ */

.gallery-block { margin-bottom: 2.5rem; }
/* A gallery that lines up, without cutting anybody out of the picture.
   Two wrong answers were tried before this one. Filling a fixed box cropped every photograph —
   a tall one lost its top and bottom, a group photograph lost the people at the edges. Letting
   each photograph keep its own height lined nothing up: the rows ended at six different places
   and it read as a mistake.

   So: every cell the same size, and the photograph fitted inside its cell rather than cropped
   to fill it. The rows line up, nothing is cut off, and the space a photograph does not fill
   is the page's own colour, so there is no box or frame around it — the picture is all you
   see. Whatever an administrator uploads, in whatever shape, it lands square with the rest. */
.gallery-grid {
  display: grid;
  /* Four across on a full-width screen, so the cells stay a sensible shape rather than
     stretching wide and shallow. Fewer as the screen narrows, down to one on a phone. */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
  gap: 1.25rem;
}
@media (min-width: 1024px) { .gallery-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 700px) and (max-width: 1023px) { .gallery-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.gallery-grid figure {
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-md);
}
.gallery-grid img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  transition: transform .4s var(--ease);
}
.gallery-grid figure:hover img { transform: scale(1.045); }
/* A film has no natural height in a column, so it keeps the shape films come in. */
.gallery-grid iframe,
.gallery-grid video { display: block; width: 100%; aspect-ratio: 16 / 9; border: 0; }
.gallery-grid figcaption {
  padding: .7rem .9rem;
  color: var(--muted);
  font-size: .88rem;
}

/* Mandatory disclosure table ----------------------------------------------- */

.disclosure-table {
  overflow: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-xs);
}
.disclosure-table table { width: 100%; border-collapse: collapse; }
.disclosure-table th, .disclosure-table td {
  padding: .95rem 1.15rem;
  border-bottom: 1px solid var(--line);
  text-align: left;
}
.disclosure-table th {
  color: var(--brand);
  background: var(--surface-soft);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  white-space: nowrap;
}
.disclosure-table tbody tr:last-child td { border-bottom: 0; }
.disclosure-table tbody tr:hover { background: var(--surface-soft); }
.disclosure-table a { color: var(--brand); font-weight: 650; text-decoration: underline; text-underline-offset: 2px; }

/* Leadership messages ------------------------------------------------------ */

.message-list { display: grid; gap: 1rem; }
.message-list article { display: grid; gap: .35rem; border-left: 3px solid var(--accent); }
.message-list blockquote {
  margin: .5rem 0 0;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 1.9vw, 1.45rem);
  line-height: 1.45;
}

/* Committee ---------------------------------------------------------------- */

.person-card { padding: 1.35rem; text-align: center; }
.person-card h3 { margin-bottom: .2rem; }
.person-card p { color: var(--muted); font-size: .92rem; }

/* Contact ------------------------------------------------------------------ */

.contact-layout { grid-template-columns: 1.05fr .95fr; align-items: start; }
.contact-meta {
  display: grid;
  margin: 0 0 1.75rem;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}
.contact-meta li {
  padding: .8rem .1rem;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--line);
  font-size: .95rem;
}
.contact-meta li strong { color: var(--ink); font-weight: 650; }

.contact-form { display: grid; grid-template-columns: 1fr 1fr; gap: .85rem; margin-top: 1.25rem; }
.contact-form label { display: grid; gap: .35rem; color: var(--ink); font-size: .88rem; font-weight: 650; }
.contact-form .full, .contact-form button { grid-column: 1 / -1; }
.contact-form input, .contact-form textarea {
  padding: .8rem .95rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-xs);
  font: inherit;
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}
.contact-form input:focus, .contact-form textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 14%, transparent);
}
.contact-form textarea { resize: vertical; }

.map {
  width: 100%;
  min-height: 440px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-xs);
}

.notice {
  margin-bottom: 1.25rem;
  padding: .9rem 1.1rem;
  color: #0f6340;
  background: #eaf7f0;
  border: 1px solid #b9e3cd;
  border-radius: var(--r-xs);
  font-size: .93rem;
}
.validation-summary { margin-bottom: .85rem; color: #a3232f; font-size: .9rem; }
.validation-summary ul { margin: 0; padding-left: 1.1rem; }
.validation-summary:empty, .validation-summary-valid { display: none; }

/* News --------------------------------------------------------------------- */

.article-image {
  width: 100%;
  max-height: 420px;
  margin-bottom: 1.75rem;
  object-fit: cover;
  border-radius: var(--r-md);
}

.news-list { display: grid; gap: 1rem; }
.news-item {
  display: grid;
  grid-template-columns: minmax(0, 270px) 1fr;
  gap: 1.5rem;
  align-items: start;
  padding: 1.35rem;
}
.news-item--text { grid-template-columns: 1fr; }
.news-item img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; border-radius: var(--r-sm); }
.news-item.is-featured { border-color: color-mix(in srgb, var(--accent) 55%, transparent); }
.news-item h2 { margin: .4rem 0 .5rem; font-size: clamp(1.15rem, 1.8vw, 1.4rem); }
.news-item h2 a:hover { color: var(--brand); }
.news-item__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem;
  margin: 0;
  color: var(--faint);
  font-size: .82rem;
}
.news-item__actions { display: flex; flex-wrap: wrap; gap: 1.1rem; margin-top: .75rem; }
.news-item__actions .text-link { margin-top: 0; }

/* Events ------------------------------------------------------------------- */

.event-list { display: grid; gap: .85rem; margin-bottom: 2.5rem; }
.event-card { display: grid; grid-template-columns: 76px 1fr; gap: 1.25rem; align-items: start; padding: 1.25rem 1.4rem; }
.event-card__date {
  display: grid;
  padding: .65rem 0;
  color: #fff;
  background: var(--brand);
  border-radius: var(--r-sm);
  line-height: 1.15;
  text-align: center;
}
.event-card__date strong { font-size: 1.5rem; font-variant-numeric: tabular-nums; }
.event-card__date span { font-size: .67rem; letter-spacing: .05em; text-transform: uppercase; opacity: .85; }
.event-card h3 { margin: 0 0 .3rem; }
.event-card h3 a:hover { color: var(--brand); }
.event-card__meta { margin: 0 0 .5rem; color: var(--muted); font-size: .88rem; }
.event-list.is-past .event-card { opacity: .68; }
.event-list.is-past .event-card__date { background: var(--faint); }

/* Faculty ------------------------------------------------------------------ */

.people-block { margin-bottom: 2.75rem; }
.people-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 1rem; }
.people-card { padding: 1.5rem 1.25rem; text-align: center; }
.people-card img, .people-card__initial {
  width: 88px;
  height: 88px;
  margin: 0 auto 1rem;
  object-fit: cover;
  border-radius: 50%;
}
.people-card__initial {
  display: grid;
  place-items: center;
  color: var(--brand);
  background: color-mix(in srgb, var(--brand) 8%, transparent);
  font-family: var(--font-display);
  font-size: 1.85rem;
  font-weight: 600;
  letter-spacing: .02em;
}
.people-card h3 { margin: 0 0 .15rem; font-size: 1.05rem; }
.people-card__role { margin: 0 0 .5rem; color: var(--brand); font-size: .88rem; font-weight: 650; }
.people-card__meta { margin: 0 0 .25rem; color: var(--muted); font-size: .82rem; }
.people-card__contact { margin: .75rem 0 0; font-size: .82rem; word-break: break-word; }
.people-card__contact a { color: var(--brand); text-decoration: underline; text-underline-offset: 2px; }

/* Footer ------------------------------------------------------------------- */

.site-footer {
  margin-top: var(--section-y);
  padding: clamp(2.5rem, 5vw, 3.5rem) 0 2.25rem;
  color: rgba(255, 255, 255, .84);
  background:
    radial-gradient(circle at 6% 0%, color-mix(in srgb, var(--accent) 18%, transparent), transparent 38%),
    linear-gradient(158deg, color-mix(in srgb, var(--brand) 88%, #000), var(--brand));
}
.site-footer a { color: #fff; }
.site-footer a:hover { color: var(--accent); }
.footer-grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 2rem; align-items: start; }
.footer-grid strong {
  display: block;
  margin-bottom: .6rem;
  color: #fff;
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 600;
}
.footer-grid p { margin-bottom: .4rem; color: rgba(255, 255, 255, .78); font-size: .92rem; line-height: 1.6; }
.social-links { display: flex; flex-wrap: wrap; gap: .9rem; }
.social-links a { font-size: .9rem; font-weight: 600; }

/* Variant accents ---------------------------------------------------------- */

body.variant-academic .eyebrow { color: color-mix(in srgb, var(--accent) 80%, #fff); }

/* Responsive --------------------------------------------------------------- */

@media (max-width: 1000px) {
  .nav-toggle { display: inline-flex; }
  .site-header .shell { flex-wrap: wrap; min-height: 68px; padding-block: .8rem; }
  .site-header nav {
    display: none;
    flex-direction: column;
    align-items: stretch;
    width: 100%;
    padding-bottom: .5rem;
  }
  .site-header nav.is-open { display: flex; }
  .site-header nav a { padding: .7rem .25rem; border-bottom: 1px solid var(--line); }
  .site-header nav a::after { display: none; }

  .modern-grid, .campus-panels, .academic-columns, .prestige-row,
  .split-cards, .contact-layout, .contact-form, .footer-grid {
    grid-template-columns: 1fr;
  }
  .stats-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: -1.5rem; }
  .modern-media { min-height: 300px; }
}

@media (max-width: 720px) {
  body { font-size: 1rem; }
  .hero { min-height: min(72vh, 560px); padding: 5rem 0 3.5rem; }
  .news-item, .event-card { grid-template-columns: 1fr; }
  .event-card__date { width: 76px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
  .card-grid article:hover, .people-card:hover, .news-item:hover, .event-card:hover { transform: none; }
  /* Reveals are never applied when motion is reduced, but guard anyway so content
     can never be left invisible. */
  .reveal { opacity: 1 !important; transform: none !important; }
}

@media print {
  body { background: #fff; }
}

.programme-list {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin: .85rem 0 0;
  padding: 0;
  list-style: none;
}
.programme-list li {
  padding: .2rem .6rem;
  color: var(--ink-soft);
  background: var(--surface-soft);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: .78rem;
  font-weight: 600;
}

/* ===========================================================================
   Motion, press feedback and scroll affordances.
   Every effect here is additive: with JavaScript disabled nothing is hidden and
   nothing moves — the reveal classes are only ever applied by site.js.
   =========================================================================== */

/* The hidden state must NOT transition: the class is applied by script after the
   element has painted, so a transition here would fade content out and back in. */
.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: none;
  will-change: opacity, transform;
}
.reveal.is-revealed {
  opacity: 1;
  transform: none;
  transition:
    opacity .55s var(--ease) var(--reveal-delay, 0ms),
    transform .55s var(--ease) var(--reveal-delay, 0ms);
}

/* Images fade in rather than snapping, which hides decode jank on slow connections. */
img { opacity: 1; }
.card-grid img, .news-item img, .people-card img, .gallery-grid img, .article-image, .modern-media {
  transition: opacity .4s var(--ease);
}
.card-grid img:not(.is-loaded), .news-item img:not(.is-loaded),
.people-card img:not(.is-loaded), .gallery-grid img:not(.is-loaded),
.article-image:not(.is-loaded) { opacity: 0; }

/* Press feedback ----------------------------------------------------------- */

.btn:active, .contact-form button:active, .nav-toggle:active { transform: translateY(1px) scale(.985); }
.nav-toggle { transition: transform .12s var(--ease), border-color .16s var(--ease), background .16s var(--ease); }
.nav-toggle:hover { border-color: var(--brand); }

.card-grid article:active, .people-card:active, .news-item:active, .event-card:active { transform: translateY(0); }

/* Header condenses once the page scrolls, giving content more room ---------- */

.site-header { transition: box-shadow .25s var(--ease), background .25s var(--ease); }
.site-header.is-scrolled {
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
/* The bar keeps its height and the crest keeps its size and its place, whatever the page is
   doing. Shrinking both on scroll moved the crest and the name a few pixels down and left the
   moment a visitor touched the wheel, and a school's crest is the one thing on the page that
   should sit still. Scrolling changes the bar's weight — its ground and its shadow — and
   nothing that has a position. */

/* Back to top -------------------------------------------------------------- */

.to-top {
  position: fixed;
  right: clamp(1rem, 3vw, 2rem);
  bottom: clamp(1rem, 3vw, 2rem);
  z-index: 40;
  display: grid;
  width: 46px;
  height: 46px;
  place-items: center;
  color: #fff;
  background: var(--brand);
  border: 0;
  border-radius: 50%;
  box-shadow: var(--shadow-md);
  opacity: 0;
  transform: translateY(12px) scale(.9);
  pointer-events: none;
  cursor: pointer;
  transition: opacity .25s var(--ease), transform .25s var(--ease), background .2s var(--ease);
}
.to-top.is-visible { opacity: 1; transform: none; pointer-events: auto; }
.to-top:hover { background: color-mix(in srgb, var(--brand) 86%, #000); }
.to-top:active { transform: scale(.94); }
.to-top svg { width: 20px; height: 20px; }

/* Mobile navigation drawer -------------------------------------------------- */

body.nav-locked { overflow: hidden; }

@media (max-width: 1000px) {
  .site-header nav {
    gap: 0;
    max-height: min(70vh, 26rem);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .site-header nav.is-open { animation: nav-in .22s var(--ease); }
  .site-header nav a.is-active { color: var(--brand); background: var(--surface-soft); }
}

@keyframes nav-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}

/* Additional breakpoints ---------------------------------------------------- */

@media (max-width: 1280px) {
  .site-header nav a { padding-inline: .55rem; font-size: .89rem; }
}

@media (max-width: 880px) {
  .stats-grid { border-radius: var(--r-md); }
  .people-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  .gallery-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: .75rem; }
  .cta-band { padding: 1.75rem; }
}

@media (max-width: 560px) {
  :root { --section-y: 2.5rem; }
  .stats-grid { grid-template-columns: 1fr 1fr; }
  .stats-grid .stat { padding: 1.15rem .75rem; }
  .cta-row { flex-direction: column; align-items: stretch; }
  .cta-row .btn { width: 100%; }
  .people-grid { grid-template-columns: 1fr 1fr; }
  .people-card { padding: 1.1rem .85rem; }
  .people-card img, .people-card__initial { width: 68px; height: 68px; }
  .disclosure-table th, .disclosure-table td { padding: .75rem .8rem; font-size: .88rem; }
  .to-top { width: 42px; height: 42px; }
}

/* Touch devices get no hover lift — it sticks after tapping ------------------ */

@media (hover: none) {
  .card-grid article:hover, .people-card:hover, .news-item:hover,
  .event-card:hover, .campus-panels article:hover, .academic-columns article:hover {
    transform: none;
    box-shadow: var(--shadow-xs);
  }
  .gallery-grid figure:hover img { transform: none; }
}

/* Hero slideshow -----------------------------------------------------------
   The school's own pictures, filling the hero. The scrim is repeated over the
   images so headings stay legible whatever photograph is showing. */
.hero-slides { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.hero-slides__item {
  position: absolute;
  inset: 0;
  background-position: center;
  background-size: cover;
  opacity: 0;
  transform: scale(1.04);
  transition: opacity .9s ease, transform 7s ease;
}
.hero-slides__item.is-active { opacity: 1; transform: scale(1); }
/* The same shade as the hero's own, because the slides cover the hero's background and would
   otherwise leave the copy standing on bare photograph. Confined to the bottom for the same
   reason: the picture is the point of the section. */
.hero-slides::after {
  position: absolute;
  inset: 0;
  content: "";
  background: linear-gradient(to top, rgba(7, 16, 33, .72) 0%, rgba(7, 16, 33, .34) 22%, transparent 58%);
}
/* Direct children only: with a space this also matched the arrows and dots inside the
   slideshow, turning them relative and dropping them into the top-left corner. */
.hero > *:not(.hero-slides) { position: relative; z-index: 2; }
.hero-slides__nav {
  position: absolute;
  top: 50%;
  z-index: 3;
  display: grid;
  width: 46px;
  height: 46px;
  margin-top: -23px;
  padding: 0;
  color: #fff;
  background: rgba(9, 18, 36, .42);
  border: 1px solid rgba(255, 255, 255, .35);
  border-radius: 50%;
  cursor: pointer;
  place-items: center;
  transition: background .18s ease, transform .18s ease;
}
.hero-slides__nav:hover { background: rgba(9, 18, 36, .72); transform: scale(1.06); }
.hero-slides__nav svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; }
.hero-slides__nav--prev { left: clamp(10px, 3vw, 28px); }
.hero-slides__nav--next { right: clamp(10px, 3vw, 28px); }
.hero-slides__dots {
  position: absolute;
  bottom: 22px;
  left: 50%;
  z-index: 3;
  display: flex;
  gap: 8px;
  transform: translateX(-50%);
}
.hero-slides__dots button {
  width: 26px;
  height: 4px;
  padding: 0;
  background: rgba(255, 255, 255, .45);
  border: 0;
  border-radius: 3px;
  cursor: pointer;
  transition: background .2s ease, width .2s ease;
}
.hero-slides__dots button.is-active { width: 40px; background: #fff; }

@media (prefers-reduced-motion: reduce) {
  .hero-slides__item { transition: opacity .01ms; transform: none; }
}

/* The header settles into a compact bar once the page is scrolled, so the school's name stays
   present without taking a fifth of the screen. Motion is skipped for anyone who asks for less. */
.site-header {
  transition: box-shadow .28s ease, background-color .28s ease;
}
.site-header.is-condensed {
  background: var(--surface);
  box-shadow: 0 10px 30px -22px rgba(9, 18, 36, .7);
}

@media (prefers-reduced-motion: reduce) {
  .site-header { transition: none; }
}

/* ===========================================================================
   Scrolling notice strip, opening popup and per-section reveal styles.
   Everything here is additive: with the settings off, or with JavaScript
   disabled, the page is exactly what it was before.
   =========================================================================== */

/* --- Notice strip that moves ---------------------------------------------- */

.notice-bar--scrolling { overflow: hidden; padding-block: .6rem; }
.notice-marquee {
  display: flex;
  width: max-content;
  animation: notice-scroll var(--notice-speed, 26s) linear infinite;
}
/* One set is the school's notices end to end. The track holds as many identical sets as it
   takes to outrun the screen, and travels exactly one set's width — so the loop closes with no
   gap and no visible repeat, whatever the notice says. */
.notice-marquee__set {
  display: flex;
  gap: 3.5rem;
  padding-right: 3.5rem;
  font-weight: 600;
  letter-spacing: .005em;
  white-space: nowrap;
}
.notice-marquee__set > span { display: block; }
/* Reading stops while the visitor is trying to read it. */
.notice-bar--scrolling:hover .notice-marquee,
.notice-bar--scrolling:focus-within .notice-marquee { animation-play-state: paused; }

@keyframes notice-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(calc(var(--notice-set-width, 50%) * -1)); }
}

/* --- Opening popup -------------------------------------------------------- */

.site-popup {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: clamp(.75rem, 3vw, 2rem);
}
.site-popup[hidden] { display: none; }
.site-popup__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(8, 17, 31, .72);
  backdrop-filter: blur(2px);
}
.site-popup__panel {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  width: min(960px, 100%);
  max-height: 90vh;
  overflow: auto;
  border-radius: 14px;
  background: #fff;
  box-shadow: 0 30px 80px rgba(8, 17, 31, .45);
  animation: popup-in .32s var(--ease, cubic-bezier(.22, .61, .36, 1)) both;
}
@keyframes popup-in {
  from { opacity: 0; transform: translateY(18px) scale(.98); }
  to { opacity: 1; transform: none; }
}
.site-popup__close {
  position: absolute;
  top: .5rem;
  right: .6rem;
  z-index: 2;
  width: 38px;
  height: 38px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, .92);
  color: #08111f;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(8, 17, 31, .25);
}
.site-popup__close:hover { background: #fff; }
.site-popup__poster { flex: 1 1 320px; min-width: 0; }
.site-popup__poster img { display: block; width: 100%; height: auto; }
.site-popup__form {
  flex: 1 1 300px;
  min-width: 0;
  padding: clamp(1.25rem, 3vw, 2rem);
}
.site-popup__form h2 { margin: 0 0 .25rem; font-size: 1.3rem; }
.site-popup__form h3 { margin: 0 0 1rem; font-size: 1rem; color: var(--brand); }
.site-popup__form label { display: block; margin-bottom: .7rem; }
.site-popup__form label span { display: block; margin-bottom: .25rem; font-size: .82rem; font-weight: 600; }
.site-popup__form input,
.site-popup__form textarea {
  width: 100%;
  padding: .55rem .7rem;
  border: 1px solid #d7dde6;
  border-radius: 8px;
  font: inherit;
}
.site-popup__form button {
  width: 100%;
  padding: .7rem 1rem;
  border: 0;
  border-radius: 8px;
  background: var(--brand);
  color: #fff;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.site-popup__status {
  margin: 0 0 .9rem;
  padding: .6rem .75rem;
  border-radius: 8px;
  background: #eef7ee;
  color: #205c2a;
  font-size: .9rem;
}
/* A poster on its own should not be stretched to a form's width. */

body.popup-open { overflow: hidden; }

/* --- Per-section reveal styles -------------------------------------------- */

[data-animate="none"].reveal,
[data-animate="none"] .reveal { opacity: 1 !important; transform: none !important; }

[data-animate="fade"].reveal { transform: none; }
[data-animate="zoom"].reveal { transform: scale(.965); }
[data-animate="slide-left"].reveal { transform: translateX(-28px); }
[data-animate="slide-right"].reveal { transform: translateX(28px); }
[data-animate="fade-down"].reveal { transform: translateY(-18px); }

@media (prefers-reduced-motion: reduce) {
  /* Standing still, one set is enough and the copies would only read as stutter. */
  .notice-marquee { animation: none; width: 100%; justify-content: center; }
  .notice-marquee__set[aria-hidden="true"] { display: none; }
  .notice-marquee__set { padding-right: 0; }
  .site-popup__panel { animation: none; }
}

/* ===========================================================================
   Lightbox, reading progress and the extra entrance styles.
   =========================================================================== */

.is-zoomable { cursor: zoom-in; }
.is-zoomable:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 210;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(.25rem, 2vw, 1rem);
  padding: clamp(.75rem, 4vw, 2.5rem);
  background: rgba(6, 12, 22, .93);
  animation: lightbox-in .22s ease both;
}
.lightbox[hidden] { display: none; }
@keyframes lightbox-in { from { opacity: 0; } to { opacity: 1; } }

.lightbox__stage {
  display: grid;
  place-items: center;
  gap: .75rem;
  margin: 0;
  min-width: 0;
  max-height: 100%;
}
.lightbox__stage img {
  max-width: 100%;
  max-height: 78vh;
  border-radius: 8px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, .6);
  animation: lightbox-zoom .28s var(--ease, cubic-bezier(.22, .61, .36, 1)) both;
}
@keyframes lightbox-zoom {
  from { opacity: 0; transform: scale(.94); }
  to { opacity: 1; transform: none; }
}
.lightbox__stage figcaption {
  max-width: 68ch;
  color: rgba(255, 255, 255, .88);
  font-size: .95rem;
  text-align: center;
}
.lightbox__stage figcaption[hidden] { display: none; }

.lightbox__close,
.lightbox__nav {
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, .12);
  color: #fff;
  cursor: pointer;
  transition: background .18s ease, transform .18s ease;
}
.lightbox__close:hover,
.lightbox__nav:hover { background: rgba(255, 255, 255, .26); transform: scale(1.06); }
.lightbox__close:focus-visible,
.lightbox__nav:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.lightbox__close {
  position: absolute;
  top: clamp(.5rem, 2vw, 1.25rem);
  right: clamp(.5rem, 2vw, 1.25rem);
  width: 44px;
  height: 44px;
  font-size: 1.8rem;
  line-height: 1;
}
.lightbox__nav {
  width: clamp(38px, 6vw, 54px);
  height: clamp(38px, 6vw, 54px);
  font-size: 2rem;
  line-height: 1;
}
.lightbox--single .lightbox__nav { visibility: hidden; }

/* --- Reading progress ------------------------------------------------------ */

.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 220;
  width: 100%;
  height: 3px;
  background: linear-gradient(90deg, var(--brand), var(--accent));
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}

/* --- Two more ways for a section to arrive --------------------------------- */

[data-animate="blur"].reveal { transform: none; filter: blur(9px); }
[data-animate="blur"].reveal.is-revealed { filter: none; transition: opacity .6s var(--ease), filter .6s var(--ease); }

[data-animate="rise"].reveal { transform: translateY(40px) scale(.97); }

@media (prefers-reduced-motion: reduce) {
  .lightbox, .lightbox__stage img { animation: none; }
  .scroll-progress { display: none; }
}

@media print {
  .lightbox, .scroll-progress, .site-popup { display: none !important; }
}

/* ===========================================================================
   Section backdrops — a photograph, a drawn pattern, or both.
   The layers below are inert until the home page emits a rule naming a section;
   nothing here applies to a section that was never given a backdrop.
   =========================================================================== */

[data-section] { position: relative; }

/* Behind the section's own content, never over it. */
[data-section]::before,
[data-section]::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-position: center;
  background-repeat: no-repeat;
  opacity: 0;
}
[data-section]::before { background-size: cover; }
[data-section]::after { background-size: 22px 22px; color: var(--ink); }
/* Lifts a section's own content above the background layer drawn in ::before. The hero's
   slideshow is not content — it is the background — and it is positioned to fill the hero, so
   turning it relative collapsed it to nothing and no template with banner pictures ever showed
   them. It keeps its own positioning. */
[data-section] > *:not(.hero-slides) { position: relative; z-index: 1; }

/* --- Popup posters that take turns ---------------------------------------- */

.site-popup__poster { position: relative; }
.site-popup__slide {
  margin: 0;
  opacity: 0;
  transition: opacity .45s var(--ease, ease);
}
.site-popup__slide:not(:first-child) { position: absolute; inset: 0; }
.site-popup__slide.is-active { opacity: 1; }
.site-popup__slide img { display: block; width: 100%; height: 100%; object-fit: cover; }

.site-popup__dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: .7rem;
  display: flex;
  justify-content: center;
  gap: .4rem;
}
.site-popup__dots button {
  width: 8px;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, .5);
  cursor: pointer;
  transition: background .2s ease, transform .2s ease;
}
.site-popup__dots button.is-active { background: #fff; transform: scale(1.35); }
.site-popup__dots button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .site-popup__slide { transition: none; }
}

/* --- Moving backdrops ------------------------------------------------------
   Drawn with gradients, so they cost no request and no image. The section's
   own text always sits above them, and a visitor who asked for less motion —
   or a school that switched animations off — gets the still version. */

@keyframes backdrop-drift {
  from { background-position: 0 0; }
  to { background-position: 260px 260px; }
}
@keyframes backdrop-rise {
  from { background-position: 0 0, 0 0, 0 0; }
  to { background-position: 0 -540px, 0 -700px, 0 -440px; }
}
@keyframes backdrop-sweep {
  0%, 100% { background-position: 200% 0; }
  50% { background-position: -100% 0; }
}
@keyframes backdrop-twinkle {
  0%, 100% { opacity: .05; }
  50% { opacity: .13; }
}

body[data-scroll-animations="off"] [data-section]::after { animation: none !important; }

@media (prefers-reduced-motion: reduce) {
  [data-section]::after { animation: none !important; }
}

/* ===========================================================================
   Button and card treatments a school picks in the console.
   Each is a class on <body>, so a design's own look is what shows until one
   is chosen — nothing here applies by default.
   =========================================================================== */

/* --- Button shape --------------------------------------------------------- */

.btn-shape-rounded .btn, .btn-shape-rounded .contact-form button,
.btn-shape-rounded .site-popup__form button { border-radius: 10px; }
.btn-shape-pill .btn, .btn-shape-pill .contact-form button,
.btn-shape-pill .site-popup__form button { border-radius: 999px; }
.btn-shape-square .btn, .btn-shape-square .contact-form button,
.btn-shape-square .site-popup__form button { border-radius: 0; }

/* --- Button style --------------------------------------------------------- */

.btn-style-solid .btn { color: #fff; background: var(--brand); }

.btn-style-outline .btn {
  color: var(--brand);
  background: transparent;
  border-color: currentColor;
  box-shadow: none;
}

.btn-style-soft .btn {
  color: var(--brand);
  background: color-mix(in srgb, var(--brand) 14%, transparent);
  border-color: transparent;
  box-shadow: none;
}

.btn-style-gradient .btn {
  color: #fff;
  background: linear-gradient(120deg, var(--brand), var(--accent));
  border-color: transparent;
}

/* --- Button hover --------------------------------------------------------- */

/* Each replaces the default lift rather than stacking on it, or two effects fight. */
.btn-hover-lift .btn:hover { transform: translateY(-3px); filter: none; }

.btn-hover-fill .btn {
  position: relative;
  overflow: hidden;
  z-index: 0;
}
.btn-hover-fill .btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--brand);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform .32s var(--ease);
}
.btn-hover-fill .btn:hover { color: #fff; transform: none; filter: none; }
.btn-hover-fill .btn:hover::before { transform: scaleX(1); }

.btn-hover-glow .btn:hover {
  transform: none;
  filter: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent),
              0 10px 26px color-mix(in srgb, var(--accent) 35%, transparent);
}

/* A sweep of light crossing the button, rather than the button moving. */
.btn-hover-slide .btn { position: relative; overflow: hidden; }
.btn-hover-slide .btn::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -60%;
  width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .55), transparent);
  transform: skewX(-18deg);
  transition: left .5s var(--ease);
}
.btn-hover-slide .btn:hover { transform: none; filter: none; }
.btn-hover-slide .btn:hover::after { left: 120%; }

/* --- Card and tile hover -------------------------------------------------- */

.card-hover-lift :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure):hover {
  transform: translateY(-6px);
  box-shadow: var(--shadow-md);
}
.card-hover-zoom :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure):hover {
  transform: scale(1.025);
}
.card-hover-glow :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure):hover {
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 55%, transparent),
              0 14px 34px color-mix(in srgb, var(--brand) 22%, transparent);
}
.card-hover-tilt :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure) {
  transform-style: preserve-3d;
}
.card-hover-tilt :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure):hover {
  transform: perspective(700px) rotateX(3deg) rotateY(-3deg) translateY(-4px);
}

/* A pointer that cannot hover has nothing to show, and a visitor who asked for
   less motion should not be moved at. */
@media (hover: none), (prefers-reduced-motion: reduce) {
  .btn-hover-lift .btn:hover,
  .btn-hover-fill .btn:hover,
  .btn-hover-glow .btn:hover,
  .btn-hover-slide .btn:hover { transform: none; box-shadow: var(--shadow-xs); }
  .btn-hover-fill .btn:hover::before { transform: scaleX(0); }
  .btn-hover-slide .btn:hover::after { left: -60%; }
  .card-hover-lift :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure):hover,
  .card-hover-zoom :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure):hover,
  .card-hover-tilt :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure):hover {
    transform: none;
  }
}

/* --- Notice strip treatments ---------------------------------------------- */

.notice-bar--gradient { background: linear-gradient(100deg, var(--brand), var(--accent)); }
.notice-bar--dark { background: #10192b; }
.notice-bar--outline {
  color: var(--brand);
  background: transparent;
  border-block: 1px solid currentColor;
}

/* --- Buttons take the school's own colour --------------------------------- */

.btn-style-solid .btn { background: var(--btn-color, var(--brand)); }
.btn-style-outline .btn { color: var(--btn-color, var(--brand)); }
.btn-style-soft .btn {
  color: var(--btn-color, var(--brand));
  background: color-mix(in srgb, var(--btn-color, var(--brand)) 14%, transparent);
}
.btn-style-gradient .btn {
  background: linear-gradient(120deg, var(--btn-color, var(--brand)), var(--accent));
}
.btn-hover-fill .btn::before { background: var(--btn-color, var(--brand)); }

/* A colour chosen for the buttons applies even when no style was picked. */
body:not([class*="btn-style-"]) .btn { background: var(--btn-color, var(--accent)); }

/* A section's own hover beats the site-wide one: it is on the section, so it is nearer. */
[data-section].card-hover-lift :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure):hover {
  transform: translateY(-6px);
}
[data-section].card-hover-zoom :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure):hover {
  transform: scale(1.025);
}
[data-section].card-hover-tilt :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure):hover {
  transform: perspective(700px) rotateX(3deg) rotateY(-3deg) translateY(-4px);
}

/* One copy crossing the strip: it begins off the right edge, so there is a gap behind it. */
.notice-marquee--single { padding-left: 100%; }
.notice-marquee--single .notice-marquee__set { padding-right: 0; }

/* ===========================================================================
   Hover colour, and the treatments for pictures and links.
   Everything below reads --hover-color, which falls back to the accent when
   the school has not chosen one.
   =========================================================================== */

.btn-hover-glow .btn:hover {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--hover-color, var(--accent)) 45%, transparent),
              0 10px 26px color-mix(in srgb, var(--hover-color, var(--accent)) 35%, transparent);
}
.card-hover-glow :is(.people-card, .news-item, .event-card, .card-grid > article, .gallery-grid figure):hover {
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--hover-color, var(--accent)) 55%, transparent),
              0 14px 34px color-mix(in srgb, var(--brand) 22%, transparent);
}

/* --- Pictures ------------------------------------------------------------- */

/* The wrapper has to clip, or a zooming picture spills over the card's corner. */
.img-hover-zoom :is(.card-grid > article, .news-item, .people-card, .event-card, .gallery-grid figure) {
  overflow: hidden;
}
.img-hover-zoom :is(.card-grid > article, .news-item, .people-card, .event-card, .gallery-grid figure) img {
  transition: transform .45s var(--ease);
}
.img-hover-zoom :is(.card-grid > article, .news-item, .people-card, .event-card, .gallery-grid figure):hover img {
  transform: scale(1.06);
}

.img-hover-lift :is(.card-grid > article, .news-item, .people-card, .event-card, .gallery-grid figure) img {
  transition: transform .35s var(--ease), box-shadow .35s var(--ease);
}
.img-hover-lift :is(.card-grid > article, .news-item, .people-card, .event-card, .gallery-grid figure):hover img {
  transform: translateY(-5px);
  box-shadow: 0 12px 28px rgba(8, 17, 31, .22);
}

.img-hover-tint :is(.card-grid > article, .news-item, .people-card, .event-card, .gallery-grid figure) img {
  transition: filter .35s var(--ease);
}
.img-hover-tint :is(.card-grid > article, .news-item, .people-card, .event-card, .gallery-grid figure):hover img {
  filter: saturate(1.25) contrast(1.05);
}

/* --- Links in body copy --------------------------------------------------- */

/* Never the navigation or a button: those already say what they are, and a second
   treatment on them reads as a mistake. */
.link-hover-underline :is(.section-body, .page-body, .prose) a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}
.link-hover-color :is(.section-body, .page-body, .prose) a {
  transition: color .2s var(--ease);
}
.link-hover-color :is(.section-body, .page-body, .prose) a:hover {
  color: var(--hover-color, var(--accent));
}

@media (hover: none), (prefers-reduced-motion: reduce) {
  .img-hover-zoom :is(.card-grid > article, .news-item, .people-card, .event-card, .gallery-grid figure):hover img,
  .img-hover-lift :is(.card-grid > article, .news-item, .people-card, .event-card, .gallery-grid figure):hover img {
    transform: none;
  }
}

/* Arrows on the poster, so a visitor can move through them rather than only wait. */
.site-popup__arrow {
  position: absolute;
  top: 50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  transform: translateY(-50%);
  border: 0;
  border-radius: 50%;
  background: rgba(8, 17, 31, .45);
  color: #fff;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  transition: background .18s ease;
}
.site-popup__arrow:hover { background: rgba(8, 17, 31, .7); }
.site-popup__arrow:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.site-popup__arrow--prev { left: .5rem; }
.site-popup__arrow--next { right: .5rem; }

/* ===========================================================================
   Design identities.

   Until now every variant shared one typeface pairing, one card shape and one
   spacing rhythm, so five templates were the same page with the blocks in a
   different order — which is exactly how they read. Type comes from the
   layout, which loads only the families the chosen design needs; the rest is
   here. Each block changes shape, density and colour weighting, not just hue.
   =========================================================================== */

/* --- Modern: sharp, tight, high contrast ---------------------------------- */

.variant-modern {
  --r-xs: 0;
  --r-sm: 0;
  --r-md: 0;
}
.variant-modern h1,
.variant-modern h2 { letter-spacing: -.03em; text-transform: uppercase; }
.variant-modern .eyebrow { letter-spacing: .22em; }
.variant-modern .btn { text-transform: uppercase; letter-spacing: .06em; font-weight: 700; }
.variant-modern :is(.card-grid > article, .news-item, .people-card, .event-card) {
  border-left: 3px solid var(--brand);
}

/* --- Campus: soft, warm, generous ----------------------------------------- */

.variant-campus {
  --r-xs: 1rem;
  --r-sm: 1.5rem;
  --r-md: 2rem;
  --surface: #fffdf9;
}
.variant-campus h1,
.variant-campus h2 { letter-spacing: -.01em; }
.variant-campus .btn { border-radius: 999px; padding-inline: 1.8rem; }
.variant-campus :is(.card-grid > article, .news-item, .people-card, .event-card) {
  border: 0;
  box-shadow: 0 10px 30px rgba(60, 40, 20, .09);
}
.variant-campus img { border-radius: 1rem; }

/* --- Academic: bookish, restrained, ruled --------------------------------- */

.variant-academic {
  --r-xs: 2px;
  --r-sm: 2px;
  --r-md: 2px;
}
.variant-academic h1,
.variant-academic h2 { font-weight: 400; letter-spacing: 0; }
.variant-academic .section-head { border-bottom: 1px solid var(--line); padding-bottom: .9rem; }
.variant-academic :is(.card-grid > article, .news-item, .people-card, .event-card) {
  border: 1px solid var(--line);
  box-shadow: none;
  background: transparent;
}
.variant-academic .btn { box-shadow: none; }

/* --- Prestige: formal, spaced, gold --------------------------------------- */

.variant-prestige {
  --r-xs: 0;
  --r-sm: 0;
  --ink: #1a1712;
}
.variant-prestige h1,
.variant-prestige h2 { font-weight: 500; letter-spacing: .01em; }
.variant-prestige .eyebrow {
  letter-spacing: .34em;
  text-transform: uppercase;
  color: var(--accent);
}
.variant-prestige .section-head h2::after {
  content: "";
  display: block;
  width: 54px;
  height: 2px;
  margin: .9rem auto 0;
  background: var(--accent);
}
.variant-prestige .section-head { text-align: center; }
.variant-prestige .btn { border-radius: 0; letter-spacing: .1em; text-transform: uppercase; }

/* --- Bulletin: dense, utilitarian, notice-led ----------------------------- */

.variant-bulletin {
  --r-xs: 3px;
  --r-sm: 3px;
  --r-md: 3px;
  --shell: 1240px;
  font-size: 15px;
}
.variant-bulletin h1 { letter-spacing: .01em; text-transform: uppercase; }
.variant-bulletin h2 { font-size: clamp(1.3rem, 2.2vw, 1.7rem); text-transform: uppercase; letter-spacing: .02em; }
.variant-bulletin .section { padding-block: clamp(1.75rem, 3vw, 2.5rem); }
.variant-bulletin .btn { border-radius: 3px; text-transform: uppercase; letter-spacing: .05em; }

.bulletin-pillars > .shell {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1px;
  background: var(--line);
  border-block: 1px solid var(--line);
}
.bulletin-pillars article { padding: 1.2rem 1.1rem; background: var(--surface); }
.bulletin-pillars h3 { margin: 0 0 .3rem; font-size: .98rem; color: var(--brand); }
.bulletin-pillars p { margin: 0; color: var(--muted); font-size: .88rem; }

.bulletin-welcome {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  padding-block: clamp(2rem, 4vw, 3rem);
}
@media (max-width: 860px) { .bulletin-welcome { grid-template-columns: 1fr; } }

.bulletin-news { border: 1px solid var(--line); border-radius: 3px; padding: 1.1rem 1.2rem; }
.bulletin-news h3 {
  margin: 0 0 .8rem;
  padding-bottom: .6rem;
  border-bottom: 2px solid var(--brand);
  font-size: .95rem;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.bulletin-news ul { margin: 0; padding: 0; list-style: none; }
.bulletin-news li { padding: .7rem 0; border-bottom: 1px dashed var(--line); }
.bulletin-news li:last-child { border-bottom: 0; }
.bulletin-news li a { font-weight: 600; }
.bulletin-news li p { margin: .2rem 0 .25rem; color: var(--muted); font-size: .85rem; }
.bulletin-news time { color: var(--accent); font-size: .78rem; font-weight: 600; }

.bulletin-figures { background: var(--brand); color: #fff; }
.bulletin-figures > .shell {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  padding-block: 1.4rem;
  text-align: center;
}
.bulletin-figures strong { display: block; font-family: var(--font-display); font-size: 1.9rem; }
.bulletin-figures span { font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; opacity: .85; }

.bulletin-cta { background: var(--sunken, #f1f4f8); }
.bulletin-cta > .shell {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1.6rem;
}
.bulletin-cta h2 { margin: 0 0 .25rem; }
.bulletin-cta p { margin: 0; color: var(--muted); }

/* --- Atrium: spacious, editorial ------------------------------------------ */

.variant-atrium {
  --r-xs: 0;
  --r-sm: 0;
}
.variant-atrium h1 { font-size: clamp(2.6rem, 6vw, 5rem); font-weight: 300; letter-spacing: -.02em; }
.variant-atrium h2 { font-size: clamp(1.8rem, 3.4vw, 2.9rem); font-weight: 300; }
.variant-atrium .eyebrow { letter-spacing: .28em; font-weight: 400; }
.variant-atrium .btn { border-radius: 0; font-weight: 400; letter-spacing: .08em; }

.atrium-statement { padding-block: clamp(4rem, 10vw, 8rem); text-align: center; }
.atrium-statement .section-body { max-width: 62ch; margin-inline: auto; font-size: 1.08rem; }

.atrium-message > .shell {
  display: grid;
  grid-template-columns: minmax(0, 380px) minmax(0, 1fr);
  align-items: center;
  gap: clamp(2rem, 6vw, 5rem);
  padding-block: clamp(3rem, 8vw, 6rem);
}
@media (max-width: 820px) { .atrium-message > .shell { grid-template-columns: 1fr; } }
.atrium-message figure { margin: 0; }
.atrium-message img { width: 100%; height: auto; display: block; }
.atrium-message blockquote {
  margin: .5rem 0 1.2rem;
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 2.4vw, 1.85rem);
  font-weight: 300;
  line-height: 1.45;
}
.atrium-message__by strong { display: block; }
.atrium-message__by span { color: var(--muted); font-size: .9rem; }

.atrium-pillars { padding-block: clamp(3rem, 7vw, 6rem); }
.atrium-pillars ol { margin: 2rem 0 0; padding: 0; list-style: none; counter-reset: pillar; }
.atrium-pillars li {
  counter-increment: pillar;
  display: grid;
  grid-template-columns: 4rem minmax(0, 1fr);
  gap: 1.5rem;
  padding: clamp(1.5rem, 3vw, 2.25rem) 0;
  border-top: 1px solid var(--line);
}
.atrium-pillars li::before {
  content: counter(pillar, decimal-leading-zero);
  font-family: var(--font-display);
  font-size: 1.6rem;
  color: var(--accent);
}
/* The number is the first grid item and takes the narrow column; without saying so, the
   heading lands beside it and the description falls to the next row's narrow column, where it
   sets one word per line and the section runs for several screens. Both belong in the wide
   column, under one another. */
.atrium-pillars li > * { grid-column: 2; }
.atrium-pillars li::before { grid-column: 1; grid-row: 1; }
.atrium-pillars li { row-gap: .4rem; }

.atrium-pillars h3 { margin: 0 0 .4rem; font-family: var(--font-display); font-size: 1.4rem; font-weight: 500; }
.atrium-pillars p { margin: 0; max-width: 60ch; color: var(--muted); }

.atrium-wall { padding-block: clamp(3rem, 7vw, 6rem) 0; }
.atrium-wall__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 2px;
  margin-top: 2rem;
}
.atrium-wall figure { margin: 0; overflow: hidden; }
.atrium-wall img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: contain; }

/* ===========================================================================
   Sections a school actually needs, and no design had.
   Tables stay tables — a timetable read as cards is four screens of scrolling
   and cannot be searched with the browser's own find.
   =========================================================================== */

.table-scroll { overflow-x: auto; }
.table-scroll table {
  width: 100%;
  border-collapse: collapse;
  font-size: .94rem;
}
.table-scroll :is(th, td) {
  padding: .7rem .85rem;
  text-align: left;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.table-scroll thead th {
  background: color-mix(in srgb, var(--brand) 8%, transparent);
  color: var(--brand);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.table-scroll tbody th { font-weight: 600; }
.table-scroll tbody tr:last-child :is(th, td) { border-bottom: 0; }
.table-note { margin-top: .8rem; color: var(--muted); font-size: .86rem; }

/* Timings and staff read as columns of the same kind of thing, so the digits line up. */
.timings-table td,
.staff-table td:first-child { font-variant-numeric: tabular-nums; }
.staff-table td:first-child { width: 3.5rem; color: var(--muted); }

.downloads-table td:last-child { text-align: right; white-space: nowrap; }
.btn-sm { padding: .4rem .9rem; font-size: .82rem; }

/* --- Crest ---------------------------------------------------------------- */

.crest-section {
  display: grid;
  grid-template-columns: minmax(0, 220px) minmax(0, 1fr);
  align-items: start;
  gap: clamp(1.5rem, 5vw, 4rem);
}
@media (max-width: 760px) { .crest-section { grid-template-columns: 1fr; } }
.crest-section__mark { margin: 0; }
.crest-section__mark img { width: 100%; height: auto; }
.crest-section .motto {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-style: italic;
  color: var(--brand);
}
.crest-meanings { margin: 0; display: grid; gap: .9rem; }
.crest-meanings > div {
  display: grid;
  grid-template-columns: minmax(0, 9rem) minmax(0, 1fr);
  gap: 1rem;
  padding-bottom: .9rem;
  border-bottom: 1px solid var(--line);
}
@media (max-width: 560px) { .crest-meanings > div { grid-template-columns: 1fr; gap: .2rem; } }
.crest-meanings dt { font-weight: 700; color: var(--accent); }
.crest-meanings dd { margin: 0; color: var(--muted); }

/* --- Alumni --------------------------------------------------------------- */

.alumni-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1.5rem;
}
.alumni-grid article { text-align: center; }
.alumni-grid img {
  width: 128px;
  height: 128px;
  margin: 0 auto .8rem;
  display: block;
  border-radius: 50%;
  object-fit: cover;
}
.alumni-grid h3 { margin: 0 0 .2rem; font-size: 1.02rem; }
.alumni-role { margin: 0; color: var(--muted); font-size: .88rem; }
.alumni-batch { margin: .25rem 0 0; color: var(--accent); font-size: .8rem; font-weight: 600; }

/* --- Facilities ----------------------------------------------------------- */

.facility-list { display: grid; gap: 1.25rem; }
.facility-list article {
  display: grid;
  grid-template-columns: minmax(0, 260px) minmax(0, 1fr);
  gap: 1.5rem;
  align-items: center;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--line);
}
.facility-list article:last-child { border-bottom: 0; padding-bottom: 0; }
.facility-list article:only-child,
.facility-list article:not(:has(img)) { grid-template-columns: 1fr; }
@media (max-width: 700px) { .facility-list article { grid-template-columns: 1fr; } }
.facility-list img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--r-sm); }
.facility-list h3 { margin: 0 0 .35rem; }
.facility-list p { margin: 0; color: var(--muted); }

/* --- Founder and history -------------------------------------------------- */

.founder-section {
  display: grid;
  grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
  gap: clamp(1.5rem, 5vw, 4rem);
  align-items: start;
}
@media (max-width: 820px) { .founder-section { grid-template-columns: 1fr; } }
.founder-section__who img { width: 100%; height: auto; border-radius: var(--r-sm); margin-bottom: 1rem; }
.founder-name { margin: 0; font-weight: 700; }
.founder-dates { margin: .1rem 0 1rem; color: var(--muted); font-size: .9rem; }

.founder-timeline { margin: 0; padding: 0; list-style: none; }
.founder-timeline li {
  display: grid;
  grid-template-columns: minmax(0, 5.5rem) minmax(0, 1fr);
  gap: 1.25rem;
  padding: 0 0 1.4rem 0;
  position: relative;
}
.founder-timeline li::before {
  content: "";
  position: absolute;
  left: 5.5rem;
  top: .55rem;
  bottom: -.4rem;
  width: 1px;
  background: var(--line);
}
.founder-timeline li:last-child { padding-bottom: 0; }
.founder-timeline li:last-child::before { display: none; }
.founder-timeline time {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.founder-timeline h3 { margin: 0 0 .2rem; font-size: 1.02rem; }
.founder-timeline p { margin: 0; color: var(--muted); }

/* ===========================================================================
   Where the designs actually diverge.

   Changing the hero and a handful of owned sections was not enough: roughly
   twenty-three of every page's twenty-five sections fall through to one
   renderer, and the header, top strip and footer are shared outright. So the
   page had the same shape in every design and only the typeface moved.

   The markup is consistent, so the shape can be rearranged from here — where
   the heading sits, how a card is drawn, how the navigation stacks, how much
   air a section gets. That is what makes one design unlike another.
   =========================================================================== */

/* --- Bulletin: a directory. Heading left, content right, rules everywhere. -- */

.variant-bulletin .site-header .shell {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  grid-template-areas: "brand brand" "nav nav";
  gap: 0;
  min-height: 0;
  padding-block: .6rem 0;
}
@media (min-width: 900px) {
  .variant-bulletin .site-header .shell { grid-template-areas: "brand nav"; align-items: center; padding-block: .5rem; }
}
.variant-bulletin .brand { grid-area: brand; }
.variant-bulletin .site-header nav {
  grid-area: nav;
  justify-content: flex-start;
  gap: 0;
  margin-top: .5rem;
  border-top: 1px solid var(--line);
  overflow-x: auto;
}
@media (min-width: 900px) {
  .variant-bulletin .site-header nav { justify-content: flex-end; margin-top: 0; border-top: 0; }
}
.variant-bulletin .site-header nav a {
  padding: .7rem .85rem;
  border-right: 1px solid var(--line);
  font-size: .82rem;
  text-transform: uppercase;
  letter-spacing: .03em;
  white-space: nowrap;
}
.variant-bulletin .site-header nav a::after { display: none; }
.variant-bulletin .site-header nav a.is-active { background: var(--brand); color: #fff; }

/* Heading in a left column, the section's content beside it. */
.variant-bulletin .section {
  display: grid;
  grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: 1.5rem 2.5rem;
  align-items: start;
  border-top: 2px solid var(--line);
}
.variant-bulletin .section > * { grid-column: 2; }
.variant-bulletin .section-head { grid-column: 1; grid-row: 1 / span 20; text-align: left; }

/* The same fault, and the same fix. */
@media (max-width: 860px) {
  .variant-bulletin .section { grid-template-columns: 1fr; }
  .variant-bulletin .section > *,
  .variant-bulletin .section-head { grid-column: 1; }
  .variant-bulletin .section-head { grid-row: auto; }
}
.variant-bulletin .section-head h2 { font-size: 1.2rem; }
.variant-bulletin .section-head p { font-size: .86rem; }

.variant-bulletin .card-grid { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 1px; background: var(--line); }
.variant-bulletin .card-grid > article {
  padding: .95rem 1rem;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: var(--surface);
}
.variant-bulletin .card-grid h3 { font-size: .95rem; }
.variant-bulletin .card-grid p { font-size: .85rem; }

.variant-bulletin .site-footer .footer-grid { gap: 1.25rem 2rem; }
.variant-bulletin .site-footer strong { text-transform: uppercase; letter-spacing: .06em; font-size: .82rem; }

/* --- Atrium: air. Centred heads, hairline cards, one column of reading. ---- */

.variant-atrium .site-header .shell {
  display: grid;
  justify-items: center;
  gap: .6rem;
  padding-block: 1.4rem;
  min-height: 0;
}
.variant-atrium .brand { flex-direction: column; text-align: center; gap: .4rem; }
.variant-atrium .site-header nav { justify-content: center; flex-wrap: wrap; gap: .2rem 1.4rem; }
.variant-atrium .site-header nav a { font-size: .82rem; letter-spacing: .12em; text-transform: uppercase; }

.variant-atrium .section { padding-block: clamp(3.5rem, 8vw, 7rem); }
.variant-atrium .section-head { text-align: center; margin-bottom: 2.5rem; }
.variant-atrium .section-head p { max-width: 58ch; margin-inline: auto; }
.variant-atrium .section-body { max-width: 64ch; margin-inline: auto; }

.variant-atrium .card-grid { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 3rem 3.5rem; }
.variant-atrium .card-grid > article {
  padding: 0;
  border: 0;
  border-top: 1px solid var(--line);
  padding-top: 1.4rem;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
}
.variant-atrium .card-grid h3 { font-family: var(--font-display); font-size: 1.35rem; font-weight: 500; }

.variant-atrium .site-footer { text-align: center; }
.variant-atrium .site-footer .footer-grid { justify-items: center; gap: 2rem; }

/* --- Modern: left-ruled, two up, nothing centred ---------------------------- */

.variant-modern .site-header { border-bottom-width: 3px; border-bottom-color: var(--brand); }
.variant-modern .site-header nav a { text-transform: uppercase; font-size: .8rem; letter-spacing: .06em; }
.variant-modern .section-head { text-align: left; border-left: 4px solid var(--brand); padding-left: 1rem; }
.variant-modern .card-grid { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }

/* --- Campus: rounded, three up, centred heads ------------------------------- */

.variant-campus .site-header nav a { border-radius: 999px; padding-inline: .9rem; }
.variant-campus .site-header nav a.is-active { background: color-mix(in srgb, var(--brand) 12%, transparent); }
.variant-campus .site-header nav a::after { display: none; }
.variant-campus .section-head { text-align: center; }
.variant-campus .section-head p { max-width: 56ch; margin-inline: auto; }
.variant-campus .card-grid { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.75rem; }

/* --- Academic: ruled columns, dense, nothing rounded ------------------------ */

.variant-academic .site-header nav a { font-size: .84rem; font-variant: small-caps; letter-spacing: .04em; }
.variant-academic .section {
  display: grid;
  grid-template-columns: minmax(0, 13rem) minmax(0, 1fr);
  gap: 1rem 2.5rem;
  align-items: start;
}
.variant-academic .section > * { grid-column: 2; }
.variant-academic .section-head { grid-column: 1; grid-row: 1 / span 20; border-bottom: 0; border-right: 1px solid var(--line); padding-right: 1.5rem; }

/* On a phone the two columns become one. Setting the template alone was not enough: every
   child is pinned to column 2, and a child pinned to a column the template does not have makes
   the browser invent one — so the section stayed two columns wide and ran 136px off the side of
   a 390px screen. The children have to come back to column 1 with it. */
@media (max-width: 820px) {
  .variant-academic .section { grid-template-columns: 1fr; }
  .variant-academic .section > *,
  .variant-academic .section-head { grid-column: 1; }
  .variant-academic .section-head {
    grid-row: auto;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    padding-right: 0;
  }
}
.variant-academic .card-grid { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1px; background: var(--line); }
.variant-academic .card-grid > article { background: var(--surface); padding: 1rem; border: 0; }

/* --- Prestige: centred, spaced, a rule under every heading ------------------ */

.variant-prestige .site-header .shell { justify-content: center; gap: 2rem; }
.variant-prestige .site-header nav { justify-content: center; }
.variant-prestige .site-header nav a { letter-spacing: .14em; text-transform: uppercase; font-size: .76rem; }
.variant-prestige .card-grid { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 2.5rem; }
.variant-prestige .card-grid > article {
  border: 0;
  border-top: 2px solid var(--accent);
  border-radius: 0;
  box-shadow: none;
  background: transparent;
  padding: 1.2rem 0 0;
  text-align: center;
}
.variant-prestige .site-footer { text-align: center; }

/* --- Notice label, and contact in the header ------------------------------- */

.notice-bar__tag {
  /* The notice scrolls past underneath. Both are in normal flow and the marquee comes second,
     so without this it paints over the label and the two read as one smeared line. */
  position: relative;
  z-index: 1;
  flex: none;
  align-self: stretch;
  display: inline-flex;
  align-items: center;
  padding: 0 1rem;
  background: var(--accent);
  color: #fff;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.notice-bar--scrolling { display: flex; align-items: stretch; padding-block: 0; }
.notice-bar--scrolling .notice-marquee { align-self: center; padding-block: .6rem; }
@media (prefers-reduced-motion: reduce) {
  /* Standing still the marquee is sized to the whole bar, which with a tag beside it would
     centre the notice against a width the tag has already taken part of. It takes what is
     left instead. */
  .notice-bar--scrolling .notice-marquee { flex: 1 1 auto; width: auto; min-width: 0; }
}

.header-contact { display: none; align-items: center; }
.header-contact a { display: flex; align-items: center; gap: .6rem; color: var(--ink); }
.header-contact .icon { width: 26px; height: 26px; color: var(--accent); }
.header-contact small {
  display: block;
  color: var(--muted);
  font-size: .72rem;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.header-contact strong { display: block; font-size: .92rem; white-space: nowrap; }
/* A phone number broken across two lines stops being a phone number, so the block keeps its
   own width and the row gives way around it rather than through it. */
.header-contact a { flex: none; }

.header-cta { flex: none; }
@media (max-width: 700px) { .header-cta { display: none; } }

/* Bulletin puts the contact blocks and the call to action on the brand row, with the
   navigation on its own bar underneath — the way both reference sites are built. */
@media (min-width: 1080px) {
  .variant-bulletin .site-header .shell {
    grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto auto;
    grid-template-areas: "brand contact contact cta" "nav nav nav nav";
    gap: .75rem 1.5rem;
    align-items: center;
  }
  .variant-bulletin .header-contact { grid-area: contact; justify-self: end; }
  .variant-bulletin .header-cta { grid-area: cta; }
  .variant-bulletin .site-header nav {
    grid-area: nav;
    justify-content: center;
    margin-top: .35rem;
    border-top: 0;
    background: var(--brand);
    border-radius: 999px;
  }
  .variant-bulletin .site-header nav a { color: #fff; border-right: 0; }
  .variant-bulletin .site-header nav a:hover { color: var(--accent); }
  .variant-bulletin .site-header nav a.is-active { background: transparent; color: var(--accent); }
}

/* Atrium's own arrangement: the brand at the left, contact and the call to action at the
   right, and the navigation centred on a rule below — the same three rows as Bulletin, read
   quietly rather than boxed. The two stay apart in weight and colour, not in structure,
   because that is how the sites they follow are actually built. */
@media (min-width: 1080px) {
  .variant-atrium .site-header .shell {
    grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto;
    grid-template-areas: "brand contact cta" "nav nav nav";
    justify-items: stretch;
    align-items: center;
    gap: .9rem 2rem;
  }
  .variant-atrium .brand { grid-area: brand; flex-direction: row; text-align: left; gap: .9rem; }
  .variant-atrium .header-contact { grid-area: contact; justify-self: end; gap: 2.25rem; }
  .variant-atrium .header-cta { grid-area: cta; }
  .variant-atrium .site-header nav {
    grid-area: nav;
    border-top: 1px solid var(--line);
    padding-top: .7rem;
  }
}
/* No panel behind the icons here, and the labels carry the weight instead. */
.variant-atrium .header-contact .icon { width: 22px; height: 22px; color: var(--brand); }
.variant-atrium .header-contact small { letter-spacing: .14em; }
.variant-atrium .header-contact strong { font-weight: 600; }

/* --- The opening poster, shown whole ---------------------------------------
   A school designs an admissions poster as a whole picture: the dates are at
   the bottom and the phone number is in a corner, so cropping it to fill a box
   is how the useful part gets cut off. The frame takes the poster's own shape
   and the picture is fitted inside it, never trimmed.

   640x480 is the size the reference site opens at, and it is what the frame
   falls back to until the first poster reports its own proportions. */

/* The frame's own size is measured against the poster and the window, in script; until that
   runs it is 640x480, the size the reference site opens at. The transition is so a change of
   poster shape reads as the next poster arriving rather than as the popup breaking. */
.site-popup__poster {
  flex: 0 0 auto;
  width: min(640px, 100%);
  max-width: 100%;
  aspect-ratio: 4 / 3;
  background: #eef0f3;
  transition: width .35s var(--ease, ease), height .35s var(--ease, ease);
}
/* Once script has measured it, the box has both dimensions and the fallback shape must not
   fight them. */
.site-popup__poster[style*="height"] { aspect-ratio: auto; }
.site-popup__slide img { object-fit: contain; }
/* A poster alone: the panel is whatever the poster measured, so a tall narrow poster is not
   left sitting against a white margin the width of a landscape one. With an enquiry form
   beside it the panel takes a settled width instead, because a form that changes width as the
   posters change shape is unreadable. */
.site-popup__panel:not(:has(.site-popup__form)) { width: auto; max-width: min(1040px, 100%); }
.site-popup__panel { align-items: stretch; }
.site-popup__panel:has(.site-popup__form) { width: min(1040px, 100%); max-width: 100%; }
.site-popup__form { flex: 1 1 340px; align-self: center; }

@media (max-width: 720px) {
  /* Stacked, the poster keeps its measured shape and the form follows underneath. */
  .site-popup__panel { flex-direction: column; align-items: center; }
}

/* --- Banners that carry their own text -------------------------------------
   A school's banner is often a finished poster: headline, dates and a button
   already drawn into the picture. Laying our heading across it gives the frame
   two headlines, and the scrim that keeps ours readable dims theirs. Here the
   picture is the whole hero — shown entire, nothing over it, nothing cut off. */

body.hero-plain .hero {
  min-height: 0;
  padding: 0;
  color: inherit;
  background: #0b1220;
}
body.hero-plain .hero::after { display: none; }
body.hero-plain .hero-copy,
body.hero-plain .hero > .shell { display: none; }

/* Full width, and as tall as the picture's own proportions make it — measured in script,
   because a max-height on a box with an aspect-ratio is transferred back into a max-width and
   narrows the hero instead of shortening it. 16:9 until that measurement lands. */
body.hero-plain .hero > .hero-slides {
  position: relative;
  inset: auto;
  width: 100%;
  aspect-ratio: 16 / 9;
}
body.hero-plain .hero > .hero-slides[style*="height"] { aspect-ratio: auto; }
body.hero-plain .hero-slides::after { display: none; }
/* Cover, not contain: the box is already the picture's shape, so there is nothing for cover to
   crop and no bare strip left where contain would have stopped short. */
body.hero-plain .hero-slides__item {
  background-size: cover;
  background-repeat: no-repeat;
  transform: none;
  transition: opacity .9s ease;
}
body.hero-plain .hero-slides__item.is-active { transform: none; }

/* --- Centred section heads sit centred -------------------------------------
   The head is capped at 42rem so a heading and its intro do not run the full
   width of a wide screen. In the designs that centre their headings, that cap
   left the box pinned to the left margin with its text centred inside it — so
   the heading read as off to one side of the content beneath it. Centring the
   text means centring the box it is in. */

.variant-atrium .section-head,
.variant-campus .section-head,
.variant-prestige .section-head { margin-inline: auto; }

/* --- Room for the header's four parts ---------------------------------------
   Brand, contact, button and menu need about 1160px between them before the
   name starts being trimmed to fit. Below that the contact blocks step aside —
   the phone and email are still one line up, on the strip — rather than every
   part of the header being squeezed at once. */

@media (max-width: 1239px) {
  .header-contact { display: none; }
}
@media (min-width: 1240px) {
  .header-contact { display: flex; gap: 1.75rem; }
  .variant-atrium .header-contact { gap: 1.75rem; }
}

/* --- Ways to reach the school ----------------------------------------------
   The contact page listed an address, a phone number and an email as three
   unlabelled lines of text. Each row now says what it is and links to the
   thing it names, so a parent on a phone taps the number instead of copying
   it out. */

.contact-meta li {
  display: grid;
  grid-template-columns: auto minmax(6.5rem, auto) minmax(0, 1fr);
  align-items: baseline;
  gap: 0 .75rem;
}
/* Rows with no icon still line their label up with the ones that have one. */
.contact-meta li > .contact-meta__label:first-child { grid-column: 2; }
.contact-meta .icon { width: 18px; height: 18px; color: var(--accent); align-self: center; }
.contact-meta__label {
  color: var(--muted);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
}
@media (max-width: 560px) {
  .contact-meta li { grid-template-columns: auto minmax(0, 1fr); }
  .contact-meta li > * { grid-column: 2; }
  .contact-meta .icon { grid-column: 1; grid-row: 1 / span 2; }
}

.contact-social { display: grid; gap: .5rem; margin-bottom: 1.75rem; }

/* Icons in the strip and the footer, words nowhere — one set of shapes, so a school that adds
   an account gets it in both places. */
.social-links a { display: inline-flex; }
.social-links .icon { width: 20px; height: 20px; }
.site-footer .social-links { margin-top: .9rem; gap: 1rem; }
.site-footer .social-links .icon { width: 22px; height: 22px; }

/* --- The crest, and the name beside it --------------------------------------
   The crest sits on a disc of the header's own colour and hangs below the bar
   into whatever is under it, so the header and the banner read as one piece
   rather than two stacked bands with a line between them.

   It hangs by a transform, not a margin, so the bar's height is untouched: a
   bigger crest was the ask, a deeper header that pushes the whole page down
   was not. And because nothing in the header resizes on scroll, the crest is
   in the same place on a page halfway read as on one just opened. */

.brand img { position: relative; z-index: 2; }

/* A crest on a disc, a crest in a panel, or the artwork on its own — the school chooses. The
   first two carry the header's colour down over the banner; "original" leaves the drawing
   exactly as it was made, which is right for a crest that already has its own outline. */
.logo-rounded .brand img,
.logo-square .brand img {
  padding: .7rem;
  background: var(--surface);
  box-shadow: 0 14px 30px -14px rgba(8, 17, 31, .5);
}
.logo-rounded .brand img { border-radius: 50%; }
.logo-square .brand img { border-radius: var(--r-sm, 10px); }
@media (max-width: 700px) {
  .logo-rounded .brand img,
  .logo-square .brand img { box-shadow: none; padding: 0; border-radius: 0; }
}
@media (max-width: 700px) {
  /* Too little header to hang from, and a crest over the banner on a phone covers the banner. */
  .brand img { transform: none; }
}

/* The name reads as the school's, in its own colour, with whatever follows it — the campus,
   the city, the board — set under the name's right edge rather than under its left. */
.brand strong { color: var(--brand); }
.brand small { text-align: right; }

@media (prefers-reduced-motion: reduce) {
  .brand img { transition: none; }
}

/* The menu rows that sit under the brand have to begin to the right of the crest, or the crest
   hangs straight through the first item. The header measures the crest and publishes its width;
   Atrium's menu is centred, so it is held off on both sides and stays centred. */
@media (min-width: 760px) {
  /* The pill begins to the right of the crest rather than running underneath it: padding moved
     the links but left the coloured shape passing behind the crest, which is what made the two
     look like they had collided. Atrium's menu has no shape behind it and is centred, so it is
     held off on both sides instead. */
  .variant-bulletin .site-header nav { margin-left: var(--crest-space, 0px); }
  .variant-atrium .site-header nav { padding-inline: var(--crest-space, 0px); }
}

/* --- Video ------------------------------------------------------------------
   A poster and a play button. The player itself is built only when somebody
   asks for it, so a page with three films on it is not three third-party pages
   loading before anyone has pressed anything. */

.video-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1.5rem;
  margin-top: 2rem;
}
.video-grid--single { grid-template-columns: minmax(0, min(880px, 100%)); justify-content: center; }

.video-card { margin: 0; }
.video-card video,
.video-card iframe,
.video-card__play {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: var(--r-md, 14px);
  background: #0b1220;
  overflow: hidden;
}
.video-card__play { position: relative; padding: 0; cursor: pointer; }
.video-card__play img { width: 100%; height: 100%; object-fit: cover; }
.video-card__icon {
  position: absolute;
  inset: 50% auto auto 50%;
  translate: -50% -50%;
  display: grid;
  place-items: center;
  width: 68px;
  height: 68px;
  background: var(--accent);
  border-radius: 50%;
  box-shadow: 0 12px 30px -10px rgba(8, 17, 31, .6);
  transition: transform .2s var(--ease, ease);
}
.video-card__icon svg { width: 30px; height: 30px; fill: #fff; }
.video-card__play:hover .video-card__icon,
.video-card__play:focus-visible .video-card__icon { transform: scale(1.08); }
.video-card figcaption { margin-top: .75rem; color: var(--ink-soft); font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  .video-card__icon { transition: none; }
}

/* The contact block on the home page: details on one side, the map on the other. */
.contact-block {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: start;
}
.contact-block .map { width: 100%; min-height: 320px; border: 0; border-radius: var(--r-md, 14px); }
@media (max-width: 820px) { .contact-block { grid-template-columns: 1fr; } }

/* Facts the board requires on the disclosure page, above the document table. */
.disclosure-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2.5rem;
  margin: 0 0 1.5rem;
  padding: 1rem 1.25rem;
  list-style: none;
  background: var(--surface-2, #f4f5f7);
  border-radius: var(--r-md, 14px);
}
.disclosure-facts span {
  display: block;
  color: var(--muted);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.disclosure-table td small { display: block; margin-top: .2rem; font-size: .82rem; }

/* --- The gallery page ------------------------------------------------------
   Its heading and each album's name sit over a grid of pictures that is
   centred on the page, so ranging them left leaves the words hanging off to
   one side of everything they belong to. */

/* The gallery used to be centred by a rule written for the gallery alone, so it disagreed with
   every other page and neither half could be changed. The choice below governs all of them. */
.gallery-block > .section-head { margin-inline: auto; }

/* A table cannot be made narrower than its content, so on a phone it has to scroll inside its
   own box rather than pushing the whole page sideways with it. */
.staff-table, .timings-table, .downloads-table, .disclosure-table table { min-width: 100%; }
.table-scroll,
.staff-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
@media (max-width: 700px) {
  .staff-table, .timings-table, .downloads-table { display: block; overflow-x: auto; max-width: 100%; }
}

/* --- How large an inner page's heading is set -------------------------------
   A school's own choice. Each stays a clamp rather than one fixed size, so a
   heading that fits a desktop still fits a phone: the choice moves the whole
   range, it does not pin the heading to a number. */

.title-small  .page-hero h1 { font-size: clamp(1.5rem,  2.4vw, 2rem); }
.title-medium .page-hero h1 { font-size: clamp(1.75rem, 3vw,   2.5rem); }
.title-large  .page-hero h1 { font-size: clamp(2rem,    3.6vw, 3rem); }
.title-xlarge .page-hero h1 { font-size: clamp(2.4rem,  4.8vw, 4rem); }

/* A page the school has written itself. It gets the width of the band and no styling of its
   own beyond what the page already provides, because anything imposed here would fight the
   markup they wrote. Wide things inside it scroll rather than pushing the page sideways. */
.custom-html { max-width: none; }
.custom-html img { max-width: 100%; height: auto; }
.custom-html table { display: block; overflow-x: auto; max-width: 100%; }
.custom-html iframe { max-width: 100%; }

/* --- Which way page headings are ranged ------------------------------------
   One choice for the whole site, so About, Gallery, Messages and the rest
   agree with each other. The eyebrow is a flex row and the intro line has a
   reading measure, so both need telling as well as the text itself. */

.title-align-left   .page-hero, .title-align-left   .gallery-block > .section-head { text-align: left; }
.title-align-center .page-hero, .title-align-center .gallery-block > .section-head { text-align: center; }
.title-align-right  .page-hero, .title-align-right  .gallery-block > .section-head { text-align: right; }

.title-align-center .page-hero .eyebrow { justify-content: center; }
.title-align-right  .page-hero .eyebrow { justify-content: flex-end; }

.title-align-center .page-hero .lede { margin-inline: auto; }
.title-align-right  .page-hero .lede { margin-inline: auto 0; }
