main#main.landing {
  padding: 0;
  max-width: none;
}
.shell-content--landing { overflow-x: clip; }

/* On the landing page the hero runs full-bleed behind the rail, so the rail
   needs its own scrim. 0.92 white over a worst-case pure-black frame
   composites to ~rgb(235,235,235); --bms-blue-700 on that is 9.2:1, so the
   nav text stays above 4.5:1 whatever the video is doing underneath. */
/* No backdrop blur, by decision (2026-09-18). It sat over the playing hero
   video, so the browser had to re-blur a strip of moving footage on every
   frame, and on every frame of every scroll — the landing page froze Edge on
   Sean's PC mid-scroll. Under 0.92 white the blur was barely visible anyway.
   The scrim stays on a pseudo-element: a backdrop-filter or transform on
   .rail itself would make it a containing block for the fixed drawer and
   clip the drawer to the rail's width. */
.is-landing .rail { background: transparent; }
.is-landing .rail::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: rgba(255, 255, 255, 0.92);
}

/* ── 2.1 Hero ──────────────────────────────────────────────────── */
.hero {
  position: relative;
  /* Deliberately short of full height: the next section peeks in at the
     bottom so it's obvious the page continues. A fixed 5rem rather than a
     percentage keeps that reveal the same on every screen size. */
  height: calc(100svh - 5rem);
  min-height: 32rem;
  margin-left: calc(var(--rail-w-collapsed) * -1);
  /* not 100vw — that ignores the scrollbar and causes horizontal overflow */
  width: calc(100% + var(--rail-w-collapsed));
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  isolation: isolate;
}
/* The still and the video occupy exactly the same box, and neither takes
   part in layout — .hero sets its own height — so swapping one for the other
   cannot move anything on the page. The still sits one layer further back and
   simply stays there: where the video plays it is covered, and where it does
   not the photograph is already in place. */
.hero-video,
.hero-still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero-video { z-index: -2; }
.hero-still { z-index: -3; }
/* Lightened a step so the footage shows through more (Sean, 2026-09-22):
   0.78 / 0.55 / 0.25 before. Measured, not eyeballed — over every second of
   BMS-Landing.mp4, under every line of the title and the standfirst, at a
   phone, an iPad both ways up, a laptop and a desktop, taking the brightest
   2% of the footage behind each line. The worst the title reaches is 3.1:1
   (desktop, where it sits highest in the frame; large text needs 3:1), and
   the standfirst 4.3:1 on a laptop, where it is large text, and 5.1:1 on a
   phone, where it is not and needs 4.5:1. One step lighter than this
   (0.62 / 0.38 / 0.10) fails on desktop, laptop and phone.
   This rests on this footage. A new landing video needs measuring again: the
   old values held 8.8:1 even over a pure white frame; these do not. */
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to top,
    rgba(18, 24, 31, 0.70) 0%,
    rgba(18, 24, 31, 0.45) 45%,
    rgba(18, 24, 31, 0.15) 100%
  );
}
.hero-inner {
  padding: var(--sp-16) var(--sp-8) var(--sp-24) calc(var(--rail-w-collapsed) + var(--sp-8));
  max-width: 60rem;
  color: var(--paper);
}
.hero-title {
  color: var(--paper);
  font-size: var(--step-4);
  letter-spacing: -0.02em;
  text-wrap: balance;
}
.hero-standfirst {
  margin-top: var(--sp-4);
  font-size: var(--step-1);
  color: var(--paper);
  max-width: 34ch;
}

/* Circled down arrow — the scroll cue. Centred with the `translate`
   property, not `transform`, so the idle bob below can own transform. */
.hero-scroll {
  position: absolute;
  bottom: var(--sp-8);
  left: 50%;
  translate: -50% 0;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--paper);
  color: var(--bms-blue-700);
  box-shadow: 0 4px 18px -6px rgba(18, 24, 31, 0.5);
  transition: background 0.3s var(--ease), color 0.3s var(--ease);
  animation: hero-scroll-bob 2.4s var(--ease) infinite;
}
.hero-scroll svg { width: 22px; height: 22px; }
.hero-scroll:hover { background: var(--bms-blue-700); color: var(--paper); }
@keyframes hero-scroll-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(5px); }
}
/* The scroll prompt is bold at every width (Sean, 2026-09-19). On a phone or
   tablet it is the only prompt there is: the circled arrow below is hidden,
   since a thumb scrolls anyway and the circle sat over the footage for
   nothing. */
.hero-standfirst-cue { font-weight: 800; }
@media (max-width: 1023px), (hover: none) and (pointer: coarse) and (max-width: 1400px) {
  .hero-scroll { display: none; }
}

/* ── 2.2 Headteacher's welcome ─────────────────────────────────── */
/* Matches the hero's height so the two read as a pair, with the content
   centred in the taller space rather than stranded at the top. */
.welcome {
  position: relative;
  min-height: calc(100svh - 5rem);
  display: flex;
  align-items: center;
  padding: var(--sp-24) var(--sp-6);
  background: var(--bms-blue-100); /* matches the news band */
  overflow: hidden; /* crops the watermark at the section edges */
}

/* Oversized tower mark as a watermark, deliberately cropped by the section
   edges and tilted, filling what was otherwise dead space. The artwork is
   black with the white keyed out, so opacity alone sets the tone: 0.11
   matches the reference watermark, which measures just 11% darker than the
   page it sits on. Purely decorative, so it lives on a pseudo-element and
   never reaches the accessibility tree. */
.welcome::before {
  content: "";
  position: absolute;
  z-index: 0;
  top: 50%;
  /* Offset chosen so the mark's centre sits a further 10% closer to the
     section's centre, while still reading as left-weighted. */
  left: -8.65%;
  /* --wm-shift is driven by assets/js/parallax.js. It defaults to 0, so
     with no JS (or under reduced motion) the mark simply sits still. */
  translate: 0 calc(-50% + var(--wm-shift, 0px));
  /* A third smaller than the doubled size. Still large enough that only a
     fragment of the tower shows, which is the point. */
  width: min(69.5rem, 83vw);
  aspect-ratio: 800 / 1200;
  background: url("../img/bms-watermark.webp") center / contain no-repeat;
  transform: rotate(-12deg);
  /* 10% lighter again (0.10 -> 0.09). The ceiling here is 0.105: the
     attribution line uses --ink-muted, and above that a stroke behind it
     drops the contrast under AA. Lighter is the safe direction. */
  opacity: 0.09;
  pointer-events: none;
}
.welcome-inner {
  position: relative; /* sits above the watermark */
  z-index: 1;
  display: grid;
  gap: var(--sp-12);
  align-items: center;
  width: 100%;
  max-width: 84rem;
  margin: 0 auto;
}
@media (min-width: 900px) {
  /* media is the FIRST column, so it takes the larger share */
  .welcome-inner { grid-template-columns: 1.25fr 1fr; gap: var(--sp-16); }
}

/* Poster + play button. The real video only exists in the modal. */
.welcome-play {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: none;
  background: var(--bms-blue-900);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
}
.welcome-poster {
  display: block;
  width: 100%;
  /* height:auto is required — the img's height attribute is a presentational
     hint, and with width also definite it would win over aspect-ratio,
     rendering the poster at its full natural height. */
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  transition: transform 0.6s var(--ease-item), filter 0.4s var(--ease);
}
.welcome-play-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: clamp(4.5rem, 9vw, 7rem);
  height: auto;
  pointer-events: none;
  filter: drop-shadow(0 4px 18px rgba(18, 24, 31, 0.55));
  transition: scale 0.4s var(--ease-item);
}
.welcome-play:hover .welcome-poster,
.welcome-play:focus-visible .welcome-poster { transform: scale(1.03); filter: brightness(0.9); }
.welcome-play:hover .welcome-play-icon,
.welcome-play:focus-visible .welcome-play-icon { scale: 1.06; }

.welcome-heading { font-size: var(--step-3); }
.welcome-copy > * + * { margin-top: var(--sp-6); }
.welcome-copy p { max-width: var(--measure); font-size: var(--step-1); line-height: 1.55; }
.welcome-attribution {
  font-size: var(--step-0) !important;
  font-weight: 600;
  color: var(--ink-muted);
}

/* ── Video lightbox ────────────────────────────────────────────── */
.vmodal {
  position: fixed;
  inset: 0;
  z-index: 300; /* above the rail (160) and its drawer (200) */
  display: grid;
  place-items: center;
}
/* display:grid above would otherwise defeat the hidden attribute */
.vmodal[hidden] { display: none; }
.vmodal-scrim {
  position: absolute;
  inset: 0;
  background: rgba(18, 24, 31, 0.84);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}
.vmodal-frame {
  position: relative;
  width: min(92vw, 1600px);
  max-height: 86dvh;
}
.vmodal-video {
  display: block;
  width: 100%;
  height: auto;
  max-height: 86dvh;
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: var(--radius);
}
.vmodal-close {
  position: absolute;
  top: var(--sp-4);
  right: var(--sp-4);
  z-index: 2;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  padding: 0;
  background: var(--ink);
  color: var(--paper);
  border: 2px solid rgba(255, 255, 255, 0.55);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background 0.3s var(--ease), border-color 0.3s var(--ease);
}
.vmodal-close:hover { background: var(--bms-blue-700); border-color: var(--paper); }
.vmodal-close svg { width: 26px; height: 26px; }

/* Fills the screen on phones */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) and (max-width: 1400px) {
  .vmodal-frame { width: 100vw; max-height: 100dvh; }
  .vmodal-video { max-height: 100dvh; border-radius: 0; }
  .vmodal-close { top: var(--sp-3); right: var(--sp-3); }
}

/* ── 2.3 Pinned horizontal rail ────────────────────────────────── */
/* The band walks through --hrail-tone-1..8 as the cards pass; JS sets the
   interpolated colour inline. This declaration is the resting/no-JS value. */
.hrail { background: var(--hrail-tone-1); }
.hrail-viewport {
  height: 100svh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* A full-width bar holding both the heading and the counter. It stays on
   the darkest tone while the band below it ramps, which draws a clean line
   under the headteacher's welcome and leaves the shifting colour to carry
   the eye down towards the news band. It also means neither piece of text ever
   depends on the ramping colour behind it — both hold 15.9:1 throughout.
   Vertical padding totals the same 48px as before, so card height is
   unchanged. */
.hrail-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  padding: var(--sp-6);
  background: var(--hrail-tone-1);
}
/* Same size as the card titles, at both breakpoints */
.hrail-heading {
  font-size: var(--step-3);
  color: var(--paper);
}
.hrail-progress {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.88);
}
.js .hrail-progress-nojs { display: none; }
html:not(.js) .hrail-progress-js { display: none; }

/* Dot counter — mobile only, below the cards. Desktop keeps the text
   readout in the head bar. */
.hrail-dots { display: none; }
html:not(.js) .hrail-dots { display: none !important; }

/* Native scroller by default — keyboard scrollable, swipeable, no JS needed */
/* Generous breathing room: cards sit well inside the band rather than
   running to its edges, with a gap roughly a third of the card width.
   Selector is .hrail .hrail-track deliberately — base.css resets
   ul[role="list"] padding to 0, and that beats a lone class selector. */
/* The left inset centres the first card while the track sits at x:0 — i.e.
   at the moment the pin engages — so it never ends up jammed against the
   viewport edge on the way in. It scrolls away as the track translates. */
.hrail .hrail-track {
  --hrail-lead: max(var(--sp-8), calc((100% - var(--hrail-card-w)) / 2));
  flex: 1;
  display: flex;
  gap: var(--hrail-gap);
  margin: 0;
  padding: var(--sp-12) var(--sp-8) var(--sp-16) var(--hrail-lead);
  scroll-padding-left: var(--hrail-lead);
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  /* The dots (mobile) and the "N of 8" readout (desktop) already show
     position, so the native scrollbar is redundant — and it was cutting
     across the dots. Scrolling is untouched: swipe, wheel and keyboard all
     still work, and the track keeps its tabindex. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.hrail .hrail-track::-webkit-scrollbar { display: none; }
.hrail-card {
  flex: 0 0 auto;
  width: var(--hrail-card-w);
  scroll-snap-align: start;
}
/* GSAP takes over the horizontal movement when pinned, so the native
   scroller stands down. Removing this class restores it completely. */
.hrail.is-pinned .hrail-track {
  overflow: visible;
  scroll-snap-type: none;
  will-change: transform;
}
.hrail-card-link {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  height: 100%;
  padding: var(--sp-8);
  border-radius: var(--radius);
  overflow: hidden;
  text-decoration: none;
  color: var(--paper);
  background: var(--bms-blue-700);
}
/* The card is a fixed width but its height follows the viewport, so the
   crop direction changes between screens, and cover handles both. Any
   height it has to lose comes off the bottom, never the top: centred, a
   short window (a laptop, or a tablet on its side) squared the card off and
   shaved the head off the cyclist on Awards (Sean, 2026-09-21). The foot of
   each photograph is under the title and its scrim anyway. A width it has to
   lose still comes off both sides equally. */
.hrail-card-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 0;
  border-radius: var(--radius);
  background: var(--bms-blue-700); /* holds the shape before the photo loads */
}
/* Two of the eight have no crop of their own, and where the card is squat —
   a tablet or a phone on its side, a short computer window — the 2:3 original
   loses what the picture is of. About Us: the group in the garden stands low
   in the frame and was left under the title, so its crop comes from further
   down. Diversity and Inclusion: an empty stretch of window above her head,
   and her face down where the title crosses it. Measured against the card at
   those shapes (Sean, 2026-09-22). Where the card is taller than the
   photograph's own shape the crop is sideways and these do nothing, so a
   computer's tall cards are untouched. */
.hrail-card[data-card="about-our-school"] .hrail-card-media { object-position: 50% 55%; }
.hrail-card[data-card="diversity-and-inclusion"] .hrail-card-media { object-position: 50% 28%; }
.hrail-card-link::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Weighted to the bottom half, where the text zone sits */
  background: linear-gradient(
    to top,
    rgba(18, 24, 31, 0.88) 0%,
    rgba(18, 24, 31, 0.72) 30%,
    rgba(18, 24, 31, 0.28) 58%,
    rgba(18, 24, 31, 0.06) 100%
  );
  transition: opacity 0.55s var(--ease-item);
}
.hrail-card-link:hover::after { opacity: 0.94; }

/* No reserved height any more — the zone is sized by the type itself, which
   is what the reference does. Measured off card-example.png: the title alone
   occupies ~1/6 of the card, and title + sentence ~1/3 on hover. */
.hrail-card-body {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.hrail-card-title {
  display: block;
  font-size: var(--step-3);
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.02em;
  text-shadow: 0 1px 12px rgba(18, 24, 31, 0.6);
}
.hrail-card-link:hover .hrail-card-title { text-decoration: underline; }

/* Reveal-on-hover sentence. Collapsed with max-height rather than display
   so it can actually animate; the value is generous enough for a couple of
   lines and only ever clips a placeholder. */
.hrail-card-reveal {
  display: block;
  /* 10rem, not 7 — the larger sentence type needs the headroom or a
     two-line sentence gets clipped mid-reveal. */
  max-height: 10rem;
  opacity: 1;
  overflow: hidden;
  /* 4x the previous timing, as asked. --ease-item is the easeOutExpo-ish
     curve used by the nav drawer, so the whole site decelerates alike. */
  transition:
    max-height 2.88s var(--ease-item),
    opacity 2.4s var(--ease-item),
    transform 2.88s var(--ease-item);
}
.hrail-card-sentence {
  display: block;
  /* ~32px, matching the gap under the title in card-example.png (~40px on
     a 680px card). The old 16px sat the sentence too tight to the title. */
  padding-top: var(--sp-8);
  font-size: var(--step-1);
  font-weight: 400;
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.92);
  text-shadow: 0 1px 10px rgba(18, 24, 31, 0.7);
}

/* Only hide it where there is a pointer that can hover — on touch there is
   no hover, so the sentence stays visible instead of being unreachable. */
@media (hover: hover) and (pointer: fine) {
  .hrail-card-reveal {
    max-height: 0;
    opacity: 0;
    transform: translateY(1.25rem); /* longer travel to read as a slide */
  }
  .hrail-card-link:hover .hrail-card-reveal,
  .hrail-card-link:focus-visible .hrail-card-reveal {
    max-height: 10rem;
    opacity: 1;
    transform: none;
  }
}

/* ── 2.4 News band ─────────────────────────────────────────────── */
/* Deliberately a little shorter than the hero and welcome sections, which
   are both calc(100svh - 5rem) — enough to read as a step down from them
   rather than a third full-height panel. Content is centred vertically but
   stays left-aligned. */
.newsband {
  position: relative;
  /* Was 80% of a screen. The band carries a week of five days now, so it
     takes the full screen back: the squares need the depth, and the aerial
     behind them needs room to read as a photograph rather than a texture. */
  min-height: calc(100svh - 8rem);
  display: flex;
  align-items: center;
  padding: var(--sp-24) var(--sp-6);
  background: var(--bms-blue-100);
  overflow: hidden;
}
/* Faded aerial of the school across the whole band. Slightly desaturated as
   well as faint, so it recedes behind the brand colours instead of
   competing with them. The desaturation (35% toward grey) is baked into the
   image rather than applied with filter: grayscale(): a CSS filter on a layer
   that moves with the parallax is recomputed every frame of a scroll.
   2600px wide (re-cut from news-background.jpg, 3866px, on 2026-09-19): the
   band grew with the notices, and the old 1600px file was being enlarged
   about 1.65x to cover it, which read as blur.
   Deliberately taller than the section (18% overhang each side) so the
   parallax has somewhere to travel without ever exposing an edge.
   --bg-shift is driven by assets/js/parallax.js and defaults to 0. */
.newsband::before {
  content: "";
  position: absolute;
  top: -18%;
  bottom: -18%;
  left: 0;
  right: 0;
  z-index: 0;
  background: url("../img/news-background.webp") center / cover no-repeat;
  translate: 0 var(--bg-shift, 0px);
  opacity: 0.12;
  pointer-events: none;
}
/* On a phone the band is tall and narrow, so only a thin upright strip of
   the aerial shows. Centred, that strip was the terraces to the right of the
   school; 35% across puts it on the main Victorian building with the
   cupola the school's logo is drawn from (Sean, 2026-09-19; tried at 28%,
   38% and 32%). */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) and (max-width: 1400px) {
  .newsband::before { background-position: 35% center; }
}
.newsband-inner {
  position: relative; /* above the backdrop */
  z-index: 1;
  width: 100%;
  max-width: 72rem;
  margin: 0 auto;
}
.newsband-heading {
  font-size: var(--step-3);
  font-weight: 800;
  letter-spacing: -0.02em;
}
/* Sits under the week, on the same left edge as the heading */
.newsband-cta { margin-top: var(--sp-12); }

/* ── The week ──────────────────────────────────────────────────
   Heading top left, the five school days beneath it, the weekly update
   underneath those. Everything is translucent and hairline-ruled: the aerial
   behind the band is the point of the band, so nothing here may become a
   solid panel sitting on top of it. No shadows, --radius corners, in keeping
   with the rest of the site. */
.cwk-range {
  margin-top: var(--sp-2);
  font-size: var(--step-0);
  color: var(--ink-muted);
}
.cwk-grid {
  margin-top: var(--sp-12);
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--sp-4);
  list-style: none;
  padding: 0;
}
.cwk-day {
  display: flex;
  flex-direction: column;
  /* A day with nothing on keeps the same square as a day with three things,
     so a quiet week reads as a quiet week and the row never goes ragged. */
  min-height: 12.5rem;
  padding: var(--sp-4);
  border: 1px solid color-mix(in srgb, var(--bms-blue-700) 18%, transparent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--paper) 46%, transparent);
}
/* Today is named by the border rather than by a fill, so the photograph
   carries on through it. */
.cwk-day[aria-current="date"] {
  border-color: var(--bms-blue-700);
  background: color-mix(in srgb, var(--paper) 72%, transparent);
}
.cwk-daylabel {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  margin: 0;
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid color-mix(in srgb, var(--bms-blue-700) 18%, transparent);
}
.cwk-dayname {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--bms-blue-900);
}
.cwk-date {
  font-size: var(--step-2);
  font-weight: 200;
  line-height: 1;
  color: var(--bms-blue-700);
}
.cwk-events {
  margin-top: var(--sp-4);
  display: grid;
  align-content: start;
  gap: var(--sp-4);
}
.cwk-event {
  margin: 0;
  font-size: var(--step--1);
  line-height: 1.45;
}
.cwk-event-kind {
  display: block;
  font-weight: 600;
  color: var(--bms-blue-700);
}

/* Five squares need a wide screen. Below that the week becomes a ruled list,
   one day per row, which is the same information in the shape a phone can
   actually hold — and it keeps working down to 320px. */
@media (max-width: 900px) {
  .cwk-grid {
    grid-template-columns: 1fr;
    gap: 0;
    margin-top: var(--sp-8);
  }
  .cwk-day {
    flex-direction: row;
    align-items: baseline;
    gap: var(--sp-6);
    min-height: 0;
    padding: var(--sp-4) var(--sp-2);
    border: 0;
    border-bottom: 1px solid color-mix(in srgb, var(--bms-blue-700) 18%, transparent);
    border-radius: 0;
    background: none;
  }
  .cwk-day[aria-current="date"] {
    background: color-mix(in srgb, var(--paper) 60%, transparent);
  }
  /* The name column is sized to "Wednesday", the longest day, so every date
     number starts where Wednesday's does (Sean, 2026-09-19) — at a fixed
     6.5rem, Wednesday overflowed and pushed its number 16px right of the
     rest. The number gets a two-digit floor so the events column starts at
     the same point on every row too. */
  .cwk-daylabel {
    flex: none;
    justify-content: flex-start;
    gap: var(--sp-3);
    padding-bottom: 0;
    border-bottom: 0;
  }
  .cwk-dayname { min-width: 5.9em; }
  .cwk-date { min-width: 1.2em; }
  .cwk-events { margin-top: 0; gap: var(--sp-3); }
}

/* ── Notices, under the week ───────────────────────────────────
   The same translucent, hairline-ruled square as the days, on the same
   five-column grid, so the band reads as one grid. Five columns whatever the
   count: four notices leave the fifth cell empty rather than stretching to
   fill it. The band grows to hold them, and the aerial behind it grows with
   it — .newsband::before covers the whole section at any height. */
.cwn-heading {
  /* Set exactly like "Current Events" above it, and given the same run of
     space: a full --sp-24 before it, the band's own section padding, and the
     week's --sp-12 between heading and squares. The band grows to take it. */
  /* 15px (0.9375rem) short of --sp-24, at Sean's request (2026-09-18). */
  margin-top: calc(var(--sp-24) - 0.9375rem);
  font-size: var(--step-3);
  font-weight: 800;
  letter-spacing: -0.02em;
}
.cwn-grid {
  margin: var(--sp-12) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--sp-4);
}
.cwn-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 9rem;
  padding: var(--sp-4);
  border: 1px solid color-mix(in srgb, var(--bms-blue-700) 18%, transparent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--paper) 46%, transparent);
  color: var(--bms-blue-900);
  text-decoration: none;
  transition: background 0.3s var(--ease), border-color 0.3s var(--ease);
}
.cwn-card:hover {
  border-color: var(--bms-blue-700);
  background: color-mix(in srgb, var(--paper) 82%, transparent);
}
.cwn-title {
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.2;
}
.cwn-arrow {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: auto;
  align-self: flex-end;
  color: var(--bms-blue-700);
  transition: transform 0.3s var(--ease);
}
.cwn-card:hover .cwn-arrow { transform: translateX(4px); }

/* Operation Encompass runs the full width under the notices, marked by a
   heavier left rule: a standing notice, not a fifth link. */
.cwn-compass {
  margin-top: var(--sp-6);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-8);
  padding: var(--sp-3) var(--sp-6);
  border: 1px solid color-mix(in srgb, var(--bms-blue-700) 18%, transparent);
  border-left: 3px solid var(--bms-blue-700);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--paper) 46%, transparent);
}
.cwn-compass-heading {
  margin: 0;
  font-size: var(--step-0);
  font-weight: 800;
  color: var(--bms-blue-900);
}
.cwn-compass-docs {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--sp-8);
  margin: 0;
  padding: 0;
  list-style: none;
}
.cwn-compass-docs a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-weight: 600;
  color: var(--bms-blue-700);
}
@media (max-width: 900px) {
  .cwn-heading { margin-top: calc(var(--sp-16) - 0.9375rem); }
  .cwn-grid { margin-top: var(--sp-8); }
  .cwn-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cwn-card { min-height: 7rem; }
}
@media (max-width: 520px) {
  .cwn-grid { grid-template-columns: 1fr; }
  .cwn-card { min-height: 0; flex-direction: row; align-items: center; justify-content: space-between; gap: var(--sp-4); }
  .cwn-arrow { margin-top: 0; align-self: center; }
}
.newsband-cta-icon {
  width: 18px;
  height: 18px;
  flex: none;
  transition: transform 0.3s var(--ease);
}
.newsband-cta:hover .newsband-cta-icon { transform: translateX(4px); }

/* ── 2.5 Instagram grid ────────────────────────────────────────── */
/* Eight posts, four across in two rows — the live site's grid, cut to eight
   and set in Instagram's 3:4 preview shape rather than squares. Nothing
   moves. Sits between Current Events and the card rail, so it pads itself
   evenly top and bottom; the inset lives on the section and the inner blocks
   are plain 72rem columns, the same box model as .newsband, so the headings
   share a left edge. */
.igstrip {
  padding: var(--sp-24) var(--sp-6);
  background: var(--paper);
}
.igstrip-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-2);
  padding-bottom: var(--sp-8);
  max-width: 72rem;
  margin: 0 auto;
}
/* Optical trim. The top gap also carries the heading's half-leading and the
   space above its ascenders — measured at 0.367em — so without this the
   white band above the words reads deeper than the one below the tiles. In
   em, so it holds as the fluid type resizes. */
.igstrip-heading { margin-top: -0.367em; }
/* No font-size of its own: the base h2 rule already gives --step-3, matching
   .newsband-heading exactly. */
.igstrip-link { font-weight: 600; font-size: var(--step-1); }

.igstrip-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-4);
  max-width: 72rem;
  margin: 0 auto;
  padding: 0;
  list-style: none;
}
.igstrip-tile {
  aspect-ratio: 3 / 4;
  min-height: 0; /* .placeholder's 12rem floor would break the ratio */
  overflow: hidden; /* the photograph is cropped by the tile, not the other way round */
  border-radius: var(--radius);
}
/* A real post, once the feed is supplying them. The tile is the 3:4 shape
   Instagram's own grid preview uses, and a post is rarely that — so the
   photograph fills the tile and is cropped to it. `fill` was what an <img>
   with no rule of its own inherited, which stretched every portrait and
   landscape post to fit. */
.igstrip-tile-link {
  display: block;
  width: 100%;
  height: 100%;
}
.igstrip-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Hover lift, desktop pointers only — no hover on touch, where it would
   either never fire or stick after a tap. :focus-within so it is already
   keyboard-equal once the tiles become links to their posts. */
@media (min-width: 1024px) and (hover: hover) {
  .igstrip-tile {
    transition: transform 0.35s var(--ease);
  }
  .igstrip-tile:hover,
  .igstrip-tile:focus-within {
    transform: scale(1.03);
    position: relative; /* lifts it over its neighbours while enlarged */
    z-index: 1;
  }
}
/* Two across on a phone: four rows of two keeps each preview large enough to
   make out. */
@media (max-width: 700px) {
  .igstrip-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
}

/* ── 2.6 Headteacher's quote ───────────────────────────────────── */
/* After the card rail, on white, left-aligned like everything else (it was
   centred first; Sean moved it left on 2026-09-18). No bottom padding: it is the last section, and the
   white run-up above the footer (--footer-lead) is the space below it. */
.hquote {
  padding: var(--sp-24) var(--sp-6) 0;
  background: var(--paper);
}
.hquote-inner {
  max-width: 72rem; /* the site's full column, as the sections above */
  margin: 0 auto;
}
.hquote-text > * + * { margin-top: var(--sp-4); }
.hquote-text p {
  max-width: none;
  font-size: var(--step-1);
  font-weight: 400;
  line-height: 1.35;
  color: var(--bms-blue-900);
}
.hquote-aims {
  display: grid;
  gap: var(--sp-3);
  padding: 0;
  list-style: none;
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.35;
  color: var(--bms-blue-700);
}
.hquote-text > .hquote-aims {
  /* The paragraph gap plus the gap between the aims, so the list stands a
     little apart from the sentence that introduces it. */
  margin: calc(var(--sp-4) + var(--sp-3)) 0 0;
}
.hquote-aims li::before {
  content: "\2022";
  margin-right: 0.4em;
  color: var(--bms-blue-500);
}
.hquote-cite {
  margin-top: var(--sp-6);
  /* A touch larger than the quote itself (--step-1), in the real italic. */
  font-size: calc(var(--step-1) * 1.1);
  font-style: italic;
  font-weight: 600;
  color: var(--ink-muted);
}

@media (max-width: 1023px), (hover: none) and (pointer: coarse) and (max-width: 1400px) {
  .hero {
    margin-left: 0;
    margin-top: calc(var(--topbar-h) * -1);
    width: 100%;
  }
  /* The copy sits at the foot of the footage (Sean, 2026-09-19). It used to
     stand --sp-32 up to leave the corner for the scroll arrow, which is not
     shown at this width any more. */
  .hero-inner {
    padding-left: var(--sp-6);
    padding-right: var(--sp-6);
    padding-bottom: var(--sp-8);
  }
  /* Scaled off viewport width, the watermark shrank to ~240px here and read
     as a small complete logo rather than a cropped one. Oversizing it keeps
     the same cropped, stylised effect on a narrow screen. */
  /* Stacked layout here, so there's no left column to sit behind — simply
     centred, and oversized enough to stay cropped.
     Much fainter than desktop: with the copy sitting directly over it in a
     single column, 0.09 read as clutter rather than texture. If this is
     still too busy, `display: none` here removes it on phones entirely. */
  .welcome::before {
    width: 207vw;
    left: 50%;
    translate: -50% calc(-50% + var(--wm-shift, 0px));
    opacity: 0.04;
  }

  /* ── Explore rail on mobile ──────────────────────────────────────
     The text readout drops out of the head bar (clipped, not removed —
     it's still the live region screen readers hear) and the dots take
     over below the cards. Losing it from the bar also shortens the bar,
     which is most of what lifts the cards up the viewport. */
  .hrail-progress {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  /* The section sits BELOW the fixed top bar, so a full 100svh overhangs the
     bottom of the screen by the bar's height — which was clipping the dots
     on a real phone. Subtracting the bar (plus a little breathing room)
     makes the whole section fit, and since the track is flex:1 the cards
     shrink to suit. */
  .hrail-viewport {
    height: calc(100svh - var(--topbar-h) - var(--sp-8));
  }
  /* Tuned so the space above and below the card stays even, which is what
     makes it read as centred rather than sitting low. */
  .hrail .hrail-track {
    padding-top: var(--sp-2);
    padding-bottom: var(--sp-6);
  }
  .hrail-dots {
    display: flex;
    justify-content: center;
    gap: var(--sp-3);
    margin: 0;
    /* Deeper bottom padding offsets the taller head bar, so the card stays
       roughly centred between the two. */
    padding: 0 var(--sp-6) var(--sp-16);
    list-style: none;
  }
  /* Outlined in the same white as the head type, filled in for the card
     currently in view. */
  .hrail-dot {
    width: 11px;
    height: 11px;
    border: 2px solid var(--paper);
    border-radius: 50%;
    transition: background-color 0.3s var(--ease);
  }
  .hrail-dot.is-current { background: var(--paper); }

  /* A mouse drags the row (horizontal-rail.js) — a desktop window narrowed
     into this layout, where there is no finger to swipe it (Sean,
     2026-09-22). Something to take hold of, over the cards as well as
     between them; a plain click still opens a card. No text selection,
     which a press-and-move would otherwise start in the card titles. */
  @media (hover: hover) and (pointer: fine) {
    .hrail:not(.is-pinned) .hrail-track,
    .hrail:not(.is-pinned) .hrail-card-link {
      cursor: grab;
      user-select: none;
    }
    .hrail .hrail-track.is-dragging,
    .hrail .hrail-track.is-dragging * { cursor: grabbing; }
    .hrail-card-link,
    .hrail-card-media { -webkit-user-drag: none; }
  }
  /* Snapping held off while the row follows the pointer, and until the glide
     that follows has landed on a card. */
  .hrail .hrail-track.is-gliding { scroll-snap-type: none; }

  /* A phone on its side: about 360px of screen, less the bar. Both of these
     are sized from the screen's height, and there that height is too little
     to share out (Sean, 2026-09-22). */
  @media (orientation: landscape) and (max-height: 500px) {
    /* The band had about 280px for its heading, the cards and the dots, and
       the cards came out a sliver tall with their words across the dots. It
       takes the height an upright phone gives it instead, and the page
       scrolls through it. */
    .hrail-viewport { height: 40rem; }
    /* Floored at 32rem, the hero stood taller than the screen and its title
       sat below the fold. At 18rem it fits with the next section showing
       beneath, as it does upright. */
    .hero { min-height: 18rem; }
  }
}

/* Placed last on purpose: the section rules above set the same properties
   at the same specificity, and the later rule wins. */
/* On a phone or tablet the homepage sections keep half their usual --sp-24
   above and below the content (Sean, 2026-09-19): the Headteacher's Welcome,
   Current Events, the Instagram grid and the headteacher's quote — and the
   white run-up from the quote to the awards band, which is the footer's but
   is halved here on the homepage only. The hero and the card rail are sized
   to the screen and are left alone. The welcome drops its screen-height
   floor as well, or on a phone that floor would put back the space the
   padding gave up. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) and (max-width: 1400px) {
  .welcome,
  .newsband,
  .igstrip { padding-block: var(--sp-12); }
  .welcome { min-height: 0; }
  .hquote { padding-top: var(--sp-12); }
  .is-landing .footer-zone { padding-top: calc(var(--footer-lead) / 2); }
}
