/* Explore pages — the eight showcases reached from the landing page's card
   rail. Loaded only for body.is-explore, and last, so the body-level overrides
   below outrank the same-specificity rules page.css sets on
   body:not(.is-landing).

   These pages deliberately break the interior-page mould: no page top, no
   prose column, no section index. What they keep is the rail, the footer, the
   tokens and the type — so a visitor never leaves the site's own voice, they
   have just arrived somewhere that is showing rather than telling. */

body.is-explore {
  /* Unhurried on purpose. A showcase is looked at rather than read, and these
     sections are large — at an interior page's pace they had finished arriving
     before the eye reached them, so nothing read as movement at all. Longer,
     further, and further apart, so each beat is seen. Every value here is off
     under prefers-reduced-motion. */
  --t-reveal: 1500ms;
  /* Photographs arrive more slowly than the words beside them. At the same
     duration a big image reads as snapping into place while the text is still
     easing — a wipe or a fade over a large area needs longer to register as
     movement at all. */
  --t-reveal-media: calc(var(--t-reveal) * 1.75);
  /* The wipe on media that is scrolled past, which is a different job from the
     opening portrait's fade. --t-reveal-media is 2.6s: fine for the portrait,
     which is on screen at load and never travels, but a full-bleed break is
     725px tall and a reader passes it in well under a second. At 2.6s they
     scrolled by a blank band and the photograph finished arriving behind
     them. */
  --t-reveal-wipe: 900ms;
  --reveal-stagger: 200ms;
  --reveal-offset: 0ms;
  --reveal-rise: 48px;

  /* The page column. Wider than the 72rem interior measure — these pages are
     laid out in two columns of media and this one has to hold both. */
  --ex-shell: min(80rem, 100% - var(--sp-12));
  /* Half what it was. Everything that sits between sections is expressed as a
     fraction of this, so the photo-break pull and the rest came down with it
     and the relationships held. Both Explore pages take it. */
  --ex-space: clamp(2.375rem, 4.5vw, 4.125rem);
}

/* Half again on the space between sections, for a page whose sections are
   short enough to want the air. Wide screens only: on a phone the sections are
   stacked and already long, and more gap between them is just more scrolling.
   Set with `exploreClass: is-roomy` in the page's frontmatter — it overrides
   the token for everything inside main, which is every section. */
@media (min-width: 1024px) and (hover: hover), (min-width: 1024px) and (pointer: fine), (min-width: 1024px) and (pointer: none), (min-width: 1401px) {
  .explore.is-roomy { --ex-space: clamp(3.5625rem, 6.75vw, 6.1875rem); }
}

/* The opening's photo-and-prose block can also stand as its own section, as it
   does on /therapy/. The section's padding is then the space above it, so its
   own margin would double up. */
.exsection .exoffer-body { margin-top: 0; }

/* A section on the page ground directly below a full-bleed coloured band.
   The band's own bottom padding is coloured, so it reads as part of the band
   rather than as space — which left 97px of light above the content and 194px
   below it, the latter being two ordinary sections' padding back to back.
   Doubling the top puts both sides on the page's normal rhythm.
   Not for a --raised section: that is a band in its own right and butts up
   against the one above it on purpose. */
.exswitch + .exsection:not(.exsection--raised),
.exring + .exsection:not(.exsection--raised),
.exvideos + .exsection:not(.exsection--raised) {
  padding-top: calc(var(--ex-space) * 2);
}

/* Everything on the first screenful reveals together on load, and starts
   immediately — it arrives underneath the intro curtain and is all but settled
   by the time the sheet wipes off it.

   No offset here, deliberately. The curtain runs on the stylesheet's clock and
   the reveal runs on reveal.js's, and the two drift apart on a slow load. Any
   hold at all put the content behind the sheet rather than under it: at 620ms
   the page measured blank for half a second after the curtain cleared, and
   cutting it to 300ms only moved that by 200. Starting at zero means the
   content can never be later than the sheet, whatever the load does.

   Anything further down the page is reached by scrolling, where the full
   --t-reveal is seen from the first frame. That is where the movement reads. */
body.is-explore .is-initial { --reveal-offset: 0ms; }

/* page.css caps and pads main for the prose column; a showcase runs edge to
   edge and each section sets its own width. #main is an id, so this has to
   carry one too in order to outrank it. */
main#main.explore {
  max-width: none;
  padding: 0;
  margin-inline: 0;
  /* The "right" reveal variant starts its elements a reveal-rise and a half to
     the right of where they belong, which at 320px is 36px past the edge of
     the screen — a horizontal scrollbar until they arrive. clip, not hidden:
     hidden would make this a scroll container and the sticky spotlight and
     team photograph would stop sticking. */
  overflow-x: clip;
}

.ex-shell {
  width: var(--ex-shell);
  margin-inline: auto;
}

/* ── Reveal variants ──────────────────────────────────
   assets/js/reveal.js drives these like every other reveal on the site; the
   attribute only changes which direction the element arrives from. Scoped with
   :not(.is-revealed) so the shared revealed state still wins — both selectors
   would otherwise weigh the same and the later file would take it. */
.js .reveal[data-reveal="left"]:not(.is-revealed) {
  transform: translateX(calc(var(--reveal-rise) * -1.75));
}
.js .reveal[data-reveal="right"]:not(.is-revealed) {
  transform: translateX(calc(var(--reveal-rise) * 1.75));
}
/* Photographs wipe rather than fade. Fading a full-bleed image reads as the
   page still loading; wiping reads as deliberate.

   The wipe runs upward, from the foot of the frame to its head. Downward is
   the same direction the photograph is already travelling as the page scrolls
   under it, so the two cancel and the image reads as having been there all
   along. Against the scroll, the edge moves visibly. */
.js .reveal[data-reveal="clip"]:not(.is-revealed) {
  opacity: 1;
  transform: none;
  clip-path: inset(100% 0 0 0);
}
/* The open state has to be a shape, not the absence of one. Without this rule
   clip-path fell back to `none`, and a basic-shape cannot interpolate to a
   keyword — so every photograph jumped straight to its end state while the
   text beside it eased in, which read as the images having no animation at
   all. inset(0 0 0 0) is the same shape at full height, so it tweens. */
.js .reveal[data-reveal="clip"].is-revealed { clip-path: inset(0 0 0 0); }
.js.reveal-ready .reveal[data-reveal="clip"] {
  transition: clip-path var(--t-reveal-wipe) var(--ease-reveal);
  transition-delay: calc(var(--reveal-offset) + var(--reveal-i, 0) * var(--reveal-stagger));
}
/* Figures that fade and rise rather than wipe take the same longer duration —
   the opening portrait is the one that matters. */
.js.reveal-ready figure.reveal { transition-duration: var(--t-reveal-media); }

/* ── Type ─────────────────────────────────────────────
   A size above the interior pages: --step-5 for the page's name, --step-4 for
   each section. Balanced and tightly tracked, as the display sizes are
   everywhere else on the site. */
.explore h1 {
  margin-bottom: clamp(var(--sp-8), 5vw, var(--sp-16));
  font-size: var(--step-5);
  line-height: 0.98;
  letter-spacing: -0.03em;
  text-wrap: balance;
}
.explore h2 {
  font-size: var(--step-4);
  line-height: 1.05;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

/* Every figure on these pages sizes its own box and asks the photograph to
   cover it. <picture> sits in between and would break that chain — a
   percentage height against it resolves to auto — so it is taken out of the
   layout and the img becomes the figure's own child. */
.explore picture { display: contents; }

/* Photographs are square-cut everywhere on these pages — the same near-square
   discipline as --radius, taken to its end. Nothing on the site is a bubble.
   No margin reset for figure here: base.css zeroes every margin already, and a
   rule at this weight would outrank the centring on .exbreak below. */
.explore figure img,
.explore .keepex-media img,
.explore video { border-radius: 0; }

/* ── Opening: title, lead, two photographs ──────────── */
.exoffer { padding: 0 0 var(--ex-space); }

/* ── The wash behind the opening ──────────────────────
   A slow drift of pale blue under the first screenful, so the section is not
   dead still. Opt-in per page with data-wash on the section, like the tilt —
   About Us has it and nothing else does yet.

   Where they sit matters more than how strong they are. The first attempt put
   both centres behind content — one under the heading, one under the
   photograph — so only their faded edges ever showed, and no amount of colour
   would have fixed that. They are in open ground now: below the lead on the
   left, above the photograph on the right. If the opening's layout changes,
   check these again.

   Two soft blooms, both mixed from --bms-blue-100, the tint the site already
   bands sections with; against --page-ground they are a couple of percent
   apart, which is all this wants to be. They move by transform only, so the
   compositor handles it and no frame repaints a gradient the size of the
   screen.

   overflow: hidden is what keeps the blooms inside the section. Safe here:
   nothing in the opening is sticky or pinned — .exswitch-spot and
   .exteam-photo, the only sticky things on these pages, are in later
   sections. isolation keeps the layers off anything else's stacking. */
/* Desktop only, like the rest of this treatment. A phone's opening is one
   column with the photograph under the heading, so there is little open
   ground for a bloom to be seen in — and it is the screen least able to
   spare the compositing. */
@media (min-width: 1024px) and (hover: hover), (min-width: 1024px) and (pointer: fine), (min-width: 1024px) and (pointer: none), (min-width: 1401px) {
  [data-wash] {
    position: relative;
    overflow: hidden;
    isolation: isolate;
  }
  [data-wash]::before,
  [data-wash]::after {
    content: "";
    position: absolute;
    /* The element overhangs the section by 15% a side, so it spans 130% of it.
       That matters for every percentage below: a gradient position of 34.6% is
       34.6% of THIS box, which lands at 30% of the section. Getting that wrong
       is what made the first attempt invisible — 22% here put a bloom at 2% of
       the section, mostly past its left edge. section% = (0.15 + f) / 1.3. */
    inset: -15%;
    z-index: -1;
    pointer-events: none;
    will-change: transform;
  }
  [data-wash]::before {
    background:
      radial-gradient(58% 60% at 30.0% 80.8%,
        color-mix(in srgb, var(--bms-blue-100) 70%, var(--bms-blue-500)) 0%,
        color-mix(in srgb, var(--bms-blue-100) 70%, var(--bms-blue-500)) 28%,
        transparent 80%);
    animation: exwash-a var(--t-wash-a) ease-in-out infinite alternate;
  }
  [data-wash]::after {
    background:
      radial-gradient(60% 56% at 70.0% 17.7%,
        color-mix(in srgb, var(--bms-blue-100) 74%, var(--bms-blue-500)) 0%,
        color-mix(in srgb, var(--bms-blue-100) 74%, var(--bms-blue-500)) 26%,
        transparent 82%);
    animation: exwash-b var(--t-wash-b) ease-in-out infinite alternate;
  }
  /* 8% of the element is 10.4% of the section, against a 15% overhang each side
     — 4.6% of margin before an edge of a bloom could drift into view. That
     overhang is the ceiling on how far this can travel: past about 11% the
     blooms start showing their own edges. */
  @keyframes exwash-a {
    from { transform: translate3d(-8%, -6%, 0) scale(1); }
    to   { transform: translate3d(8%, 6%, 0) scale(1.18); }
  }
  @keyframes exwash-b {
    from { transform: translate3d(8%, 6%, 0) scale(1.14); }
    to   { transform: translate3d(-8%, -6%, 0) scale(1); }
  }
  
}

/* The first screenful. Holds the viewport on its own so nothing from below
   shows above the fold and the page opens on one idea, with the scroll cue at
   its foot saying there is more.

   It is an inner wrapper rather than the section itself because an opening can
   carry more than one screen's worth: /therapy/ has a second block of
   photograph and prose inside the same section, and if the section held the
   min-height the cue would land 1500px down, below the fold it exists to point
   past.

   A grid, not flex centring: the content takes the first row and the cue the
   second, which pins the cue to the bottom without fighting the centring above
   it. svh, not vh — vh ignores a mobile browser's collapsing toolbar and the
   cue would sit under it. */
.exoffer-screen {
  display: grid;
  grid-template-rows: 1fr auto;
  align-items: center;
  min-height: calc(100svh - var(--topbar-h));
  padding: clamp(var(--sp-12), 5vw, var(--sp-16)) 0 var(--sp-6);
}
@media (max-width: 700px) {
  /* Ten pixels closer to the bar above it. The title is the largest thing on
     the site and on a phone it was sitting further down than it needed to. */
  .exoffer-screen { padding-top: calc(clamp(var(--sp-12), 5vw, var(--sp-16)) - 10px); }
  /* And another 36px (10px, four steps of 5px, then 3px twice, Sean, 2026-09-19) — as an offset, not more padding:
     the screen centres its content, so trimming padding moves it by only part
     of the amount. An offset moves it exactly, and moves nothing else. */
  .exoffer-screen > .exoffer-top { position: relative; top: -36px; }
}
/* Phone widths only, those two. They were set by eye on a phone, where the
   section also carries --sp-12 of its own above the screen (the 700px block
   further down) and the pull-up comes out of that. A tablet has no such
   padding, so the same 46px took the title up against the top bar, and on
   five of the eight pages a few pixels under it (measured 2026-09-21). Above
   700px the title keeps the screen's ordinary padding, which sets it about
   as far below the bar as it sits on a phone. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) and (max-width: 1400px) {
  /* The tinted gap under the opening photograph, cut to a quarter (Sean,
     2026-09-19): 73px to 37px, then to about 18px on a phone. Most of what is
     left is the space the screen keeps under the photograph, so the section
     adds only a sliver of its own. */
  .exoffer { padding-bottom: calc(var(--sp-1) * 1.5); }
}
@media (min-width: 1024px) and (hover: hover), (min-width: 1024px) and (pointer: fine), (min-width: 1024px) and (pointer: none), (min-width: 1401px) {
  /* The rail is a side column at this width, not a bar above the content. */
  .exoffer-screen { min-height: 100svh; }
}

.exoffer-top {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(21rem, 0.85fr);
  gap: clamp(var(--sp-12), 8vw, 7.5rem);
  align-items: center;
}
/* The opening carries one heading, at the largest size on the site. Its own
   bottom margin is dropped here: the lead below sets the gap, and two
   collapsing margins made it a size larger than the rest of the page. */
/* min-content is the width of the longest word, and min-width outranks
   max-width, so a title that is one long word takes the room it needs and
   leans into the gutter beside it rather than being broken across two lines.
   "Safeguarding" set at --step-5 wants 636px; the heading column has been
   592px since the opening photograph was enlarged, and overflow-wrap then
   split it and stranded the "g" on a line of its own, with its descender
   clipped by the 0.98 line-height. A multi-word title is unaffected: its
   longest word is short, so 14ch still governs where it wraps. */
.exoffer-heading h1 {
  max-width: 14ch;
  min-width: min-content;
  margin-bottom: 0;
}
/* An optional second paragraph under the lead — the lead states what the
   school is, this carries the detail, so it steps back to body size. */
.exoffer-note {
  max-width: 44ch;
  margin-top: var(--sp-6);
  color: var(--ink-muted);
}
.exoffer-lead {
  max-width: 31ch;
  margin-top: clamp(var(--sp-8), 4vw, var(--sp-12));
  font-size: clamp(1.24rem, 2.1vw, 1.72rem);
  font-weight: 500;
  line-height: 1.5;
}

/* No height of its own: it hugs the photograph, so its background can never
   show as a band under it. The navy is only what shows while the image loads. */
.exoffer-portrait { background: var(--bms-blue-900); }
/* The photograph governs the figure's height, and max-height governs the
   photograph. Left to itself it draws at its own 2:3 — 739px in a 493px column
   — which pushed the opening past the fold it is meant to fill. height:auto
   rather than 100%: a percentage against a figure of auto height resolves to
   auto anyway, and the two disagreeing by a rounding step left a band of the
   figure's navy showing beneath the image. */
.exoffer-portrait img {
  width: 100%;
  height: auto;
  max-height: 62svh;
  object-fit: cover;
}

/* ── The opening photograph, enlarged and drifting ────
   Opt-in per page with data-portrait-float on the figure, so this is on About
   Us and nowhere else until it is wanted elsewhere. Adding the attribute is
   the whole of the rollout.

   Desktop only. Both dimensions grow by about 15%, which takes two changes,
   not one: the height is governed by the 62svh cap and the width by the
   column, and raising only the cap would make the photograph taller rather
   than bigger. :has() puts the column change on the grid from the same
   attribute, so the markup says it once. */
@media (min-width: 1024px) and (hover: hover), (min-width: 1024px) and (pointer: fine), (min-width: 1024px) and (pointer: none), (min-width: 1401px) {
  .exoffer-top:has([data-portrait-float]) {
    /* min-content, not 0, as the heading column's floor: the title is set at
       the largest size on the site and one of them — "Safeguarding" — is a
       single twelve-letter word. Enlarging the photograph took this column
       from 670px to 592px, and between 1101 and 1280 the fr split left it
       around 450px, which the word overran by up to 42px straight across the
       photograph. Floored at its own content the column takes what the word
       needs and the picture gives up those few pixels, only at the widths
       where it matters. */
    grid-template-columns: minmax(min-content, 1.02fr) minmax(21rem, 0.98fr);
  }
  [data-portrait-float] {
    /* No navy behind it. A photograph that moves uncovers whatever is under
       the edge it moves away from, and .exoffer-portrait's navy was what
       showed there. Left transparent, what shows instead is the page's own
       ground, which is what surrounds the photograph anyway, so the movement
       reads as movement rather than as a gap opening. */
    background: none;
  }
  [data-portrait-float] img {
    max-height: 71svh;
    /* A small circle, travelled continuously rather than a line travelled
       back and forth. Percentages of the photograph, so the drift stays
       proportional — but a circle in pixels needs a different percentage per
       axis, because the photograph is taller than it is wide: a 7px radius is
       1.23% across and 1.10% down. Eight points rather than four, or the path
       is a diamond; linear rather than an ease, or it hesitates at each one.
       Normal direction, not alternate: a circle has nowhere to reverse. */
    animation: exportrait-float var(--t-portrait-float) linear infinite;
    will-change: transform;
  }
  @keyframes exportrait-float {
    0% { transform: translate3d(0.000%, -1.095%, 0); }
    12.5% { transform: translate3d(0.870%, -0.775%, 0); }
    25% { transform: translate3d(1.230%, 0.000%, 0); }
    37.5% { transform: translate3d(0.870%, 0.775%, 0); }
    50% { transform: translate3d(0.000%, 1.095%, 0); }
    62.5% { transform: translate3d(-0.870%, 0.775%, 0); }
    75% { transform: translate3d(-1.230%, 0.000%, 0); }
    87.5% { transform: translate3d(-0.870%, -0.775%, 0); }
    100% { transform: translate3d(-0.000%, -1.095%, 0); }
  }
  
}

.exoffer-body {
  display: grid;
  grid-template-columns: minmax(22rem, 0.82fr) minmax(0, 1.18fr);
  gap: clamp(var(--sp-12), 7vw, var(--sp-24));
  align-items: stretch;
  margin-top: clamp(var(--sp-16), 9vw, 7.75rem);
}
/* Stretches to whatever the column of copy beside it comes to, so the two
   columns finish level however long the text runs. height rather than
   min-height: the grid area gives it a definite size to resolve against, which
   is what the photograph inside needs in order to cover it. */
.exoffer-figure { height: 100%; overflow: hidden; }
.exoffer-figure img { width: 100%; height: 100%; object-fit: cover; }

.exoffer-copy {
  max-width: 70ch;
  color: var(--ink-muted);
}
.exoffer-copy p + p { margin-top: var(--sp-6); }

/* The scroll cue. Same mark and same bob as the landing page's .hero-scroll,
   but in normal flow at the foot of the opening rather than laid over a video,
   so it needs its own room rather than an absolute position. */
.scrollcue {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  margin: clamp(var(--sp-8), 4vw, var(--sp-12)) auto var(--sp-6);
  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: scrollcue-bob 2.4s var(--ease) infinite;
}
.scrollcue svg { width: 22px; height: 22px; }
.scrollcue:hover { background: var(--bms-blue-700); color: var(--paper); }
@keyframes scrollcue-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(5px); }
}

/* ── Switcher band ───────────────────────────────────
   partials/explore-switcher.njk. Navy, as the footer and the page tops are —
   the site has one dark ground and this is it. */
.exswitch {
  padding: clamp(var(--sp-12), 5vw, var(--sp-16)) 0;
  background: var(--bms-blue-900);
  /* Twelve links on a ground the default ring cannot be seen against — see
     tokens.css. Inherited by every focusable thing inside the band. */
  --focus-ring: var(--paper);
}
.exswitch-heading {
  margin-bottom: clamp(var(--sp-8), 3vw, var(--sp-12));
  color: var(--paper);
}

.exswitch-body {
  display: grid;
  grid-template-columns: minmax(18.75rem, 0.72fr) minmax(0, 1.28fr);
  gap: clamp(var(--sp-8), 5vw, var(--sp-16));
  align-items: start;
}

/* The spotlight. Follows the list down as it is scrolled, so the panel stays
   beside whichever row is being pointed at. */
.exswitch-spot {
  position: sticky;
  top: var(--sp-8);
  display: flex;
  height: 34rem;
  flex-direction: column;
  /* Not space-between. That pinned the caption to the panel's foot, so its top
     edge moved with its own height: a two-line name started right under the
     icon and a one-line one started lower down. Stacked from the top instead,
     every name begins at the same place — and so does the action under it,
     since the name's box is a fixed two lines whatever it holds. */
  justify-content: flex-start;
  padding: clamp(var(--sp-6), 2.7vw, var(--sp-8));
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--exswitch-tone);
  color: var(--ink);
  text-decoration: none;
  transition: background-color 0.4s var(--ease);
}
.exswitch-art {
  display: block;
  width: min(82%, 20rem);
  aspect-ratio: 1;
  align-self: center;
  overflow: hidden;
  background: var(--paper);
}
/* contain, not cover: the artwork is a drawing rather than a photograph, and
   cover was shaving the dome and the tail off the top and bottom of every
   one of them. Each icon already carries its own margin. */
.exswitch-art img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition: opacity 0.18s var(--ease), transform 0.48s var(--ease), filter 0.25s var(--ease);
}
.exswitch-spot.is-changing .exswitch-art img {
  opacity: 0;
  filter: blur(6px);
  transform: scale(0.92) rotate(-2deg);
}

/* The name takes the first line and the action the second, right-aligned
   under it. The names run from four characters to twenty, and at display size
   the longest cannot sit beside the action and fit the panel; rather than let
   short ones pair up and long ones wrap — which moved the action between two
   heights as the panel swapped — the action always takes its own line. */
.exswitch-caption {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--sp-6);
  align-items: flex-end;
  margin-top: var(--sp-6);
}
/* Two lines' worth of height held open whatever the name is, so the panel does
   not jog as the names swap. min-width:0 lets it shrink to its longest word
   instead of pushing the action out through the panel's clipped edge. */
.exswitch-name {
  display: flex;
  /* A whole row to itself, so the action always sits under it rather than
     beside the short names and under the long ones. With the name's box a
     fixed two lines, that puts every part of the caption in the same place
     for all twelve of them — nothing moves as the panel swaps. */
  flex: 1 1 100%;
  min-width: 0;
  min-height: 1.96em;
  /* Top of the box, for the same reason: bottom-aligned, a single line sat at
     the foot of a two-line box and started lower than a name that filled it. */
  align-items: flex-start;
  max-width: 13ch;
  font-size: clamp(1.7rem, 3vw, 2.8rem);
  font-weight: 800;
  line-height: 0.98;
  letter-spacing: -0.03em;
  text-wrap: balance;
}
.exswitch-action {
  display: flex;
  flex: none;
  gap: var(--sp-2);
  align-items: center;
  margin-inline-start: auto;
  padding-bottom: var(--sp-1);
  font-size: var(--step--1);
  font-weight: 700;
}
.exswitch-arrow { width: 1.05rem; height: 1.05rem; }

/* A band that is held has to fill the window. Held and shorter than the
   screen, it leaves a strip below itself that the next section rides up into
   while it is pinned — the same thing the navy documents band was doing to the
   Inclusion Quality Mark on /diversity-and-inclusion/. Only at the width where
   holds happen at all; narrower than that, explore-hold.js does nothing and
   the band is its own height. */
@media (min-width: 1024px) and (hover: hover), (min-width: 1024px) and (pointer: fine), (min-width: 1024px) and (pointer: none), (min-width: 1401px) {
  .exswitch[data-hold] {
    display: grid;
    align-content: center;
    min-height: 100svh;
  }
}

/* The list. Two columns, hairline-ruled, with the bottom rule drawn under the
   last row of each column. The li is the grid item and the link fills it, so
   the list keeps its semantics without display:contents — which drops list
   items out of the accessibility tree in some browsers. */
.exswitch-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: var(--sp-8);
}
.exswitch-item { display: flex; }
.exswitch-link {
  display: grid;
  flex: 1;
  min-height: 4rem;                 /* keeps the target above 44px */
  grid-template-columns: 0.875rem minmax(0, 1fr) auto;
  gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-2) 0;
  border-top: 1px solid var(--hairline-on-dark);
  color: var(--bms-sky);
  font-weight: 600;
  line-height: 1.3;
  text-decoration: none;
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.exswitch-item:nth-last-child(-n + 2) .exswitch-link { border-bottom: 1px solid var(--hairline-on-dark); }
.exswitch-dot {
  width: 0.625rem;
  height: 0.625rem;
  border-radius: 50%;
  background: var(--exswitch-tone);
  transition: transform 0.38s var(--ease);
}
.exswitch-rowarrow {
  width: 1.15rem;
  height: 1.15rem;
  opacity: 0;
  transform: translateX(-0.375rem);
  transition: opacity 0.25s var(--ease), transform 0.38s var(--ease);
}
.exswitch-link:hover,
.exswitch-link:focus-visible,
.exswitch-link.is-active {
  border-color: rgba(255, 255, 255, 0.58);
  color: var(--paper);
}
.exswitch-link:hover .exswitch-dot,
.exswitch-link:focus-visible .exswitch-dot,
.exswitch-link.is-active .exswitch-dot { transform: scale(1.65); }
.exswitch-link:hover .exswitch-rowarrow,
.exswitch-link:focus-visible .exswitch-rowarrow,
.exswitch-link.is-active .exswitch-rowarrow {
  opacity: 1;
  transform: none;
}

/* ── Full-bleed photograph between sections ─────────── */
/* The letterbox shape sits on the image rather than on the figure, so a
   caption underneath is not clipped away by the crop. */
.exbreak {
  width: min(96.25rem, 100% - var(--sp-8));
  margin-inline: auto;
}
/* height:auto is load-bearing — the img carries a height attribute, which the
   UA stylesheet turns into a used height and which would otherwise win over
   the ratio below. */
.exbreak img {
  width: 100%;
  height: auto;
  aspect-ratio: 2.35 / 1;
  object-fit: cover;
}
/* Between two sections. It is a beat in the page rather than a chapter of its
   own, so it sits a little closer to both than their own --ex-space would put
   it. The pull lives here rather than in the neighbours' padding, so no
   section has to know what it happens to sit next to. */
.exbreak--between {
  /* Above: the section before it has no background, so a negative margin just
     closes whitespace and nothing shows. */
  margin-top: calc(var(--ex-space) * -0.25);
  /* Below: the band after it is tinted, so its top edge is visible and its own
     padding is coloured rather than light. A --sp-6 gap left the caption
     sitting almost on the colour change; a section's worth of space gives the
     photograph and its line room to finish before the next band starts. */
  padding-bottom: var(--ex-space);
}
.exbreak--between img { object-position: center 46%; }
.exbreak-caption {
  max-width: 62ch;
  margin: var(--sp-6) auto 0;
  color: var(--bms-blue-900);
  font-size: var(--step-0);
  font-weight: 700;
  line-height: 1.5;
  text-align: center;
  text-wrap: balance;
}

/* ── Team ─────────────────────────────────────────── */
.exteam { padding: var(--ex-space) 0; }
.exteam-heading { margin-bottom: clamp(var(--sp-12), 7vw, 5.125rem); }
.exteam-body {
  display: grid;
  grid-template-columns: minmax(20.625rem, 0.88fr) minmax(0, 1.12fr);
  gap: clamp(var(--sp-12), 7vw, var(--sp-24));
  align-items: start;
}
.exteam-photo {
  position: sticky;
  top: var(--sp-8);
  overflow: hidden;
  aspect-ratio: 4 / 5;
}
.exteam-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 56%;
}
.exteam-list { border-top: 1px solid var(--hairline); }
.exteam-list li {
  display: grid;
  grid-template-columns: minmax(11.25rem, 0.72fr) minmax(0, 1.28fr);
  gap: var(--sp-6);
  padding: var(--sp-4) 0;
  border-bottom: 1px solid var(--hairline);
}
.exteam-list strong { font-weight: 700; }
.exteam-list span { color: var(--ink-muted); }

/* ── Video band ───────────────────────────────────── */
/* --bms-sky rather than --bms-blue-100: that tone is 1.02:1 off the page
   ground and the band would not read as a band at all. */
.exvideos {
  padding: var(--ex-space) 0;
  background: var(--bms-sky);
}
.exvideos-intro {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(21rem, 0.85fr);
  gap: clamp(var(--sp-6), 3vw, var(--sp-12));
  align-items: start;
}
.exvideos-intro h2 { color: var(--bms-blue-900); }
.exvideos-copy {
  max-width: 70ch;
  color: var(--ink-muted);
  font-size: clamp(1.08rem, 1.5vw, 1.3rem);
}
.exvideos-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(var(--sp-6), 4vw, var(--sp-12));
  margin-top: clamp(var(--sp-8), 4vw, var(--sp-12));
}
.exvideo {
  overflow: hidden;
  background: var(--bms-blue-900);
  color: var(--paper);
}
.exvideo video {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--bms-blue-900);
}
.exvideo figcaption {
  display: flex;
  min-height: 4.75rem;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  padding: var(--sp-4) var(--sp-6);
}
.exvideo-name { font-weight: 700; }
.exvideo-meta {
  color: var(--bms-sky);
  font-size: var(--step--1);
  white-space: nowrap;
}

/* ── Keep exploring ───────────────────────────────── */
/* partials/keep-exploring.njk. Two more showcases on the left, and on the
   right the menu the visitor probably came in through. */
.keepex { padding: var(--ex-space) 0 0; }
.keepex-heading { margin-bottom: clamp(var(--sp-8), 4vw, var(--sp-12)); }
.keepex-body {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(17rem, 0.65fr);
  gap: clamp(var(--sp-8), 5vw, var(--sp-16));
  align-items: start;
}
.keepex-cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(var(--sp-4), 2vw, var(--sp-8));
}
.keepex-card { display: flex; }
.keepex-link {
  display: flex;
  width: 100%;
  flex-direction: column;
  color: inherit;
  text-decoration: none;
}
/* Near-portrait, like the cards on the landing page these come from. The
   photographs are 2:3, so a landscape crop throws away a third of every one of
   them and lands on whatever happens to be in the middle band. */
.keepex-media {
  display: block;
  overflow: hidden;
  aspect-ratio: 3 / 4;
  background: var(--paper-alt);
}
.keepex-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s var(--ease);
}
.keepex-link:hover .keepex-media img,
.keepex-link:focus-visible .keepex-media img { transform: scale(1.04); }
.keepex-text {
  display: block;
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--hairline);
}
.keepex-name {
  display: block;
  color: var(--bms-blue-700);
  font-size: var(--step-1);
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.02em;
}
.keepex-sentence {
  display: block;
  margin-top: var(--sp-1);
  color: var(--ink-muted);
  font-size: var(--step--1);
}
.keepex-arrow {
  width: 1.15rem;
  height: 1.15rem;
  margin-top: var(--sp-4);
  color: var(--ink-muted);
  transition: transform 0.34s var(--ease), color 0.2s var(--ease);
}
.keepex-link:hover .keepex-arrow,
.keepex-link:focus-visible .keepex-arrow {
  color: var(--gold-ink);
  transform: translateX(0.25rem);
}

.keepex-menu-heading {
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--gold-line);
  color: var(--bms-blue-700);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.keepex-menu-link {
  display: block;
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--hairline);
  color: var(--ink);
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1.4;
  text-decoration: none;
  transition: color 0.2s var(--ease), padding-inline-start 0.28s var(--ease);
}
.keepex-menu-link:hover,
.keepex-menu-link:focus-visible {
  color: var(--bms-blue-700);
  padding-inline-start: var(--sp-2);
}
/* The page you are already on. Marked with the gold bar the rail uses for the
   same job, not colour alone — the two inks are close enough that a reader
   could miss the difference. */
.keepex-menu-link[aria-current="page"] {
  padding-inline-start: var(--sp-3);
  border-inline-start: 2px solid var(--gold-ink);
  color: var(--bms-blue-700);
  font-weight: 800;
}
/* On a phone or tablet the section menu under Keep Exploring is set like the
   top bar's own menu (Sean, 2026-09-19): the same --step-1 at 600, the same
   leading and a row at least 44px tall, so the two read as one menu. */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) and (max-width: 1400px) {
  .keepex-menu-link {
    display: flex;
    align-items: center;
    min-height: 44px;
    font-size: var(--step-1);
    line-height: 1.25;
  }
}

/* ── Narrow ──────────────────────────────────────── */
@media (max-width: 1100px) {
  body.is-explore {
    --ex-shell: min(100% - var(--sp-8), 48.75rem);
    --ex-space: clamp(4.25rem, 10vw, 6rem);
  }

  .exoffer-top,
  .exoffer-body,
  .exswitch-body,
  .exteam-body,
  .exvideos-intro,
  .keepex-body { grid-template-columns: 1fr; }

  .exoffer-top { align-items: start; }
  .exoffer-heading h2 { max-width: 12ch; }
  .exoffer-lead { max-width: 42ch; }
  /* The shape belongs to the photograph; the figure hugs whatever the
     photograph comes to. Given the ratio itself the figure keeps that height
     even when the image beside it is cut short by its 62svh cap — which on a
     phone shorter than about 700px left a band of the figure's navy showing
     under every opening picture. */
  .exoffer-portrait { min-height: 0; height: auto; }
  .exoffer-portrait img { min-height: 0; height: auto; aspect-ratio: 16 / 11; }
  .exoffer-body { gap: var(--sp-12); }
  /* Nothing beside it to match any more, so it takes the photograph's own
     shape instead of a neighbouring column's height. */
  .exoffer-figure { height: auto; aspect-ratio: 2231 / 2652; }

  /* Sticky needs a column beside it to stick against; in one column both would
     simply hold their own place. */
  .exswitch-spot,
  .exteam-photo { position: static; }
  .exswitch-spot { height: 30rem; }
  .exteam-photo { aspect-ratio: 16 / 11; }
  .exteam-photo img { object-position: center 60%; }
  .exbreak { width: calc(100% - var(--sp-6)); }
  .exbreak img { aspect-ratio: 16 / 9; }
  .exvideos-intro h2 { max-width: 13ch; }
  .keepex-menu { margin-top: var(--sp-8); }
}

@media (max-width: 700px) {
  body.is-explore {
    --ex-shell: calc(100% - var(--sp-6));
    --ex-space: var(--sp-16);
  }

  .exoffer { padding-top: var(--sp-12); }
  .exoffer-top { gap: var(--sp-8); }
  .exoffer-lead { font-size: 1.18rem; }
  .exoffer-portrait img { aspect-ratio: 4 / 5; }

  .exswitch-list { grid-template-columns: 1fr; }
  .exswitch-item:nth-last-child(2) .exswitch-link { border-bottom: 0; }
  .exswitch-item:last-child .exswitch-link { border-bottom: 1px solid var(--hairline-on-dark); }
  /* The panel previews a row the finger is already on top of. With no hover to
     drive it on a touch screen it would just sit there showing the first
     item. */
  .exswitch-spot { display: none; }

  .exteam-list li { grid-template-columns: 1fr; gap: var(--sp-1); }
  .exteam-photo { aspect-ratio: 4 / 5; }
  .exvideos-grid { grid-template-columns: 1fr; }
  .keepex-cards { grid-template-columns: 1fr; }
}

/* A screen on its side where the opening is still one column: a phone, or
   an iPad narrower than 1100px. The 62svh cap on the opening photograph
   never bites on a screen held upright — the photograph runs out of width
   first — but on a short one it cut the photograph into a strip: eyes only
   on Safeguarding on a phone, and the top of the Diversity photograph, with
   the girl's head, gone on an iPad (Sean, 2026-09-22). Here the photograph
   keeps its 16:11 and the page scrolls to it. 16:11 rather than the phone's
   4:5 below 700px, which on a phone on its side is taller than two screens. */
@media (max-width: 1100px) and (orientation: landscape) {
  .exoffer-portrait img { max-height: none; aspect-ratio: 16 / 11; }
}

/* ── Generic Explore sections ─────────────────────────
   The About page is composed from these rather than from one-off classes, so
   the six Explore pages still to come have something to build with. */
.exsection { padding: var(--ex-space) 0; }
/* Three tenths less above and below. For a section whose copy sits beside a
   photograph the full height of it, where a section's ordinary space either
   side reads as a gap rather than as room.

   The space BELOW is given away rather than kept: the section that follows
   brings its own, and the two together came to two and a half times the space
   above. Nothing here, and the neighbour matched to the top, puts the same
   distance either side of the block. */
.exsection--tight {
  padding-top: calc(var(--ex-space) * 0.7);
  padding-bottom: 0;
}
.exsection--tight + .exteam { padding-top: calc(var(--ex-space) * 0.7); }
/* A section following a coloured band is given double the space, because the
   band's own bottom padding is coloured and reads as band rather than as room.
   That rule is three selectors deep, so taking it back needs three too. On
   desktop it does not apply at all — the pinned band is wrapped in a spacer,
   so this section is no longer its sibling — which is why the two widths below
   come out so differently. */
.exswitch + .exsection--tight:not(.exsection--raised),
.exring + .exsection--tight:not(.exsection--raised),
.exvideos + .exsection--tight:not(.exsection--raised) {
  padding-top: calc(var(--ex-space) * 0.7);
}
@media (max-width: 1023px), (hover: none) and (pointer: coarse) and (max-width: 1400px) {
  /* Halved on 2026-09-19, from 1.8 to 0.9 — 115px to 58px on a phone (Sean).
     These two only occur on /therapy/: the space above its second opening
     photograph, and the space above "Our Therapy Team". */
  .exsection--tight,
  .exswitch + .exsection--tight:not(.exsection--raised),
  .exring + .exsection--tight:not(.exsection--raised),
  .exvideos + .exsection--tight:not(.exsection--raised) {
    padding-top: calc(var(--ex-space) * 0.9);
  }
  .exsection--tight + .exteam { padding-top: calc(var(--ex-space) * 0.9); }
}

/* On a phone or tablet a heading sat directly on the block beneath it, with no
   gap at all — "Provision for the 21st Century Pupil" on /curriculum/ and the
   same pattern on /student-council/ (Sean, 2026-09-19). */
@media (max-width: 1023px), (hover: none) and (pointer: coarse) and (max-width: 1400px) {
  .ex-shell > h2 + .exsplit { margin-top: var(--sp-8); }
  /* Below the photo break the figure's own space and the next section's ran
     together — 128px on a phone. Just over half of that is taken back: the
     figure gives up its padding and the section keeps --sp-16 less --sp-2. */
  .exbreak--between { padding-bottom: 0; }
  .exbreak + .exsection { padding-top: calc(var(--sp-16) - var(--sp-2)); }
}
/* A section asked to stand as one screenful. Grid with the content centred in
   what is left, rather than padding worked out from the window, so nothing
   here has to know how tall its own contents came out.

   min-height and not height: at 200% zoom, or with a long word in a narrow
   column, the section grows instead of hiding what will not fit. Desktop only
   — a phone has no room to give a section a screen of its own, and the
   photograph beside the copy is stacked under it there anyway. */
@media (min-width: 1024px) and (hover: hover), (min-width: 1024px) and (pointer: fine), (min-width: 1024px) and (pointer: none), (min-width: 1401px) {
  .exsection--screen {
    display: grid;
    align-content: stretch;
    min-height: 100svh;
    /* --ex-space is measured in vw, so on a wide-but-shallow window it spends
       a sixth of the screen on padding. Held to a share of the height as well,
       which is the dimension actually under pressure here. */
    padding-block: clamp(var(--sp-8), 6svh, var(--ex-space));
  }
  /* Two rows: the heading across the whole shell, then everything else. A
     display heading sharing a column with the copy wraps to four lines — more
     of the screen than the list it introduces — and that on its own was what
     stopped a section like this fitting one. */
  .exsection--screen > .ex-shell {
    display: grid;
    /* min-content, not 0: if what is in the row will not fit the window, the
       section grows past one screen rather than clipping it. A screen is what
       this asks for, never what it enforces. */
    grid-template-rows: auto minmax(min-content, 1fr);
    gap: clamp(var(--sp-6), 3svh, var(--sp-12));
  }
  /* The photograph gets a height budget: 56svh is what is left of a window
     once this section's padding, its two-line heading and the gap under it are
     taken off, at every window height from about 730 up.

     Spent on the IMAGE's height, and left to work its own width out from that.
     Putting a max-height on the figure instead leaves the figure's ratio still
     asking for the height it wants, that height is the grid row's min-content,
     and the row grows to it — the photograph drawn at the right size inside a
     row a third of a screen too tall. Sizing the image and letting the figure
     shrink to fit it keeps the ratio out of the row's arithmetic, and works
     whatever shape the photograph happens to be.

     Flush left, on the same edge as the heading above it. */
  .exsection--screen .exsplit-figure {
    width: fit-content;
    max-width: 100%;
    justify-self: start;
  }
  .exsection--screen .exsplit-figure img {
    width: auto;
    max-width: 100%;
    max-height: 56svh;
  }
}
/* White, not a tint. --paper-alt is 1.03:1 off --page-ground and the band was
   invisible; white against the tinted ground is the same figure/ground the
   rail and the drawer use, and it reads immediately. */
.exsection--raised { background: var(--paper); }
/* The other way: a section on the deepest ground the site has. --hairline is
   redefined rather than overridden case by case, so a ruled component dropped
   in here draws its own rules the right way round without knowing where it is.
   --focus-ring the same: the default is 1.8:1 on this navy, which is a 2.4.11
   failure, and every ring inside inherits the replacement. */
.exsection--dark {
  background: var(--bms-blue-900);
  color: var(--paper);
  --hairline: var(--hairline-on-dark);
  --focus-ring: var(--paper);
}
.exsection--dark h2 { color: var(--paper); }
.exsection--dark .exsection-intro { color: var(--bms-sky); }
/* Sized and spaced so a heading + intro + row comes to the same height as a
   heading + three paragraphs, which is what Learning at Bensham is. The three
   sections sit at a matched height on a wide screen without any of them being
   given a fixed one. */
.exsection-intro {
  max-width: 62ch;
  margin-top: var(--sp-4);
  color: var(--ink-muted);
  font-size: var(--step-0);
}
.exsection-copy { max-width: 70ch; color: var(--ink-muted); }
.exsection-copy > * + * { margin-top: var(--sp-6); }
.exsection-copy .btn { margin-top: var(--sp-8); }

/* Ruled rows. One component for the three-across and four-across bands: the
   column count is a variable so the markup never changes. Each cell reveals on
   its own, a beat after the one before — see the faster stagger below. */
.exrow {
  display: grid;
  grid-template-columns: repeat(var(--exrow-cols, 3), minmax(0, 1fr));
  margin-top: var(--sp-6);
  border-top: 1px solid var(--hairline);
  /* Brisker than the page default: at 200ms a six-cell row was still arriving
     a second and a half after the first cell landed. */
  --reveal-stagger: 120ms;
}
/* A modifier, not an inline style: an inline --exrow-cols would outrank the
   narrow-screen rules at the foot of this file and the row would stay four
   across on a phone, at 88px a column. */
.exrow--four { --exrow-cols: 4; }
.exrow--two  { --exrow-cols: 2; }
.exrow--one  { --exrow-cols: 1; }
.exrow-cell {
  padding: var(--sp-4) var(--sp-6) var(--sp-4) 0;
  border-bottom: 1px solid var(--hairline);
}
/* Stacked in a column beside a photograph rather than spread across the page,
   so the cells sit closer together and the copy runs to a measure rather than
   the full column width. Three of them plus a display heading otherwise came
   to a viewport and a quarter. */
.exrow--one .exrow-cell { padding: var(--sp-2) 0; }
.exrow--one .exrow-title { font-size: var(--step-0); }
.exrow--one .exrow-cell p { max-width: 54ch; }
.exrow-title {
  margin-bottom: var(--sp-2);
  color: var(--bms-blue-700);
  font-size: var(--step-1);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
.exrow-cell p { color: var(--ink-muted); font-size: var(--step--1); }

/* Ruled statements. The same band as .exrow, for the case where each item is
   one sentence and there is no title to carry it — a set of aims, a set of
   commitments. A real <ul>, because that is what it is: giving each line an
   <h3> to borrow .exrow-title's weight would be calling a sentence a heading.
   Ink rather than --ink-muted and a step up in size for the same reason, since
   here the sentence is the whole of the item rather than a gloss under a
   title. */
/* A set of statements shown a few at a time, the one in the middle lit, the
   whole set stepping down every few seconds. Built from
   Explore-Pages/Student Council/scrolling-school-aims.html.

   Two states, like the newsletter shelf. What is in the markup is an ordinary
   ruled list of every statement — that is what a reader gets with the script
   missing, and it is what a screen reader gets always, because a window
   showing three of six is not a list. .is-live is added by school-aims.js once
   it has the window built, and hides the list from sight only. */
.exaims { margin-top: var(--sp-6); }
.exaims-list {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--hairline);
  --reveal-stagger: 120ms;
}
.exaims-list li {
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--hairline);
  font-size: var(--step-0);
  line-height: 1.5;
}
.exaims-window { display: none; }

.exaims.is-live .exaims-list {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
/* Three rows of it are shown; the row height is measured from the longest
   statement by the script, so the window is the same height whichever one is
   passing through it. */
.exaims.is-live .exaims-window {
  --exaims-row: 7rem;
  --exaims-step: 650ms;
  display: block;
  height: calc(var(--exaims-row) * 3);
  overflow: hidden;
}
/* Parked one row up, so there is always a statement waiting above the window
   for the step down to bring in. */
.exaims-track { transform: translateY(calc(-1 * var(--exaims-row))); }
.exaims-track.is-moving {
  transform: translateY(0);
  transition: transform var(--exaims-step) var(--ease);
}
.exaims-track.is-moving .exaims-row { transition: opacity var(--exaims-step) var(--ease); }
.exaims-row {
  display: flex;
  align-items: center;
  height: var(--exaims-row);
  padding: var(--sp-4) 0;
  font-size: var(--step-1);
  line-height: 1.45;
  /* Three rows of the longest statement is 432px at this size, and on a
     shallow window that is more than the whole band has to give once its
     heading is paid for. The type comes down rather than the count: two of
     three statements showing would be a different feature. The script measures
     the row from whatever is in force here, so this needs no counterpart in
     the JavaScript. */
  /* Half strength: 4.6:1 on --bms-blue-900, so the statements either side of
     the live one are still readable rather than decorative. */
  opacity: 0.5;
}
.exaims-row p { margin: 0; }
.exaims-row.is-active { opacity: 1; }
@media (max-height: 820px) {
  .exaims { margin-top: var(--sp-4); }
  .exaims-row {
    padding: var(--sp-3) 0;
    font-size: var(--step-0);
  }
}

/* Copy beside a photograph. --exsplit-flip puts the photograph first without
   reordering the markup, so the reading order stays the same either way. */
.exsplit {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(var(--sp-12), 6vw, var(--sp-24));
  align-items: center;
}
.exsplit--narrow { grid-template-columns: minmax(0, 1fr) minmax(0, 0.62fr); }
/* A portrait photograph beside copy. Left to itself a 2:3 original draws
   855px tall in a 570px column and pushes a screen-height section past the
   fold, so the figure takes a shape of its own and the photograph covers it.
   The ratio gives the figure a definite height, which is what the image inside
   needs before height:100% will resolve to anything. */
.exsplit--portrait .exsplit-figure {
  aspect-ratio: 4 / 5;
  max-height: 70svh;
  overflow: hidden;
}
.exsplit--portrait .exsplit-figure img { width: 100%; height: 100%; object-fit: cover; }
.exsplit-figure { overflow: hidden; }
.exsplit-figure img,
.exsplit-figure video { width: 100%; height: auto; display: block; }
/* A video in the media column keeps a 16:9 box even before its poster has
   painted, so the split does not reflow as it loads. */
.exsplit-figure video { aspect-ratio: 16 / 9; background: var(--bms-blue-900); }
.exsplit--flip .exsplit-figure { order: -1; }

/* The visit panel. Navy rather than the mockup's mid blue — the site has one
   dark ground and this is it, and white on that clears 15:1. */
.expanel {
  padding: clamp(var(--sp-8), 4vw, var(--sp-16));
  background: var(--bms-blue-900);
  color: var(--paper);
  --focus-ring: var(--paper);
}
.expanel h2 { color: var(--paper); }
.expanel p { max-width: 62ch; margin-top: var(--sp-6); color: var(--bms-sky); }
/* With a photograph beside the copy. The image column is deliberately the
   smaller of the two — it is there to warm the invitation, not to compete
   with it. */
.expanel--media {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.42fr);
  gap: clamp(var(--sp-8), 4vw, var(--sp-16));
  align-items: center;
}
/* No shape of its own: the photograph is shown whole and the panel grows to
   hold it. It used to be cropped to 3:4 here and to 16:10 below 900px — a
   landscape box for a portrait photograph, which showed two fifths of the
   frame and cut the whiteboard the picture is about. A panel a little taller
   is the cheaper price. */
.expanel-figure { overflow: hidden; }
.expanel-figure img { display: block; width: 100%; height: auto; }
.expanel-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  margin-top: var(--sp-8);
}
.expanel .btn-primary { background: var(--paper); color: var(--bms-blue-900); }
.expanel .btn-primary:hover { background: var(--bms-sky); }
.expanel .btn-secondary { border-color: var(--paper); color: var(--paper); }
.expanel .btn-secondary:hover { background: rgba(255, 255, 255, 0.12); }

/* The accordion is page.css's, unchanged. It only needs a measure here: a
   showcase column is far wider than a list of prose is comfortable to read. */
.exaccordion { max-width: 78ch; margin-top: clamp(var(--sp-8), 3vw, var(--sp-12)); }

/* ── Document ring ────────────────────────────────────
   partials/explore-ring.njk. A plain ruled list by default; the ring is a
   wide-screen layout laid over the same markup further down this file, so the
   links exist in the document once. */
.exring {
  padding: clamp(var(--sp-12), 5vw, var(--sp-16)) 0;
  background: var(--bms-blue-900);
  --focus-ring: var(--paper);
}
.exring-heading {
  margin-bottom: clamp(var(--sp-8), 3vw, var(--sp-12));
  color: var(--paper);
}
/* Held still by assets/js/explore-ring.js while the icons light up, the same
   pin the landing page's card rail uses. Until that takes over it is an
   ordinary block, so with no JavaScript, on a narrow screen, or under reduced
   motion the band simply scrolls past like any other. */
.exring.is-pinned {
  /* The pinned viewport is a whole screen on its own; the band's own padding
     on top of that would make the pinned block taller than the window and the
     ring would sit off-centre in it. */
  padding: 0;
}
.exring.is-pinned .exring-viewport {
  display: grid;
  align-items: center;
  min-height: 100svh;
}
.exring-centre { display: none; }
.exring-list { border-top: 1px solid var(--hairline-on-dark); }
.exring-link {
  display: grid;
  grid-template-columns: 2.75rem minmax(0, 1fr);
  gap: var(--sp-4);
  align-items: center;
  min-height: 4rem;                 /* keeps the target above 44px */
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--hairline-on-dark);
  color: var(--bms-sky);
  font-weight: 600;
  line-height: 1.3;
  text-decoration: none;
  transition: color 0.25s var(--ease);
}
.exring-link:hover,
.exring-link:focus-visible { color: var(--paper); }
.exring-tile {
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--paper);
  transition: background-color 0.3s var(--ease), transform 0.4s var(--ease);
}
.exring-tile img { width: 100%; height: 100%; object-fit: contain; }
.exring-meta { color: var(--bms-sky); font-weight: 400; }

@media (min-width: 900px) {
  /* The ring proper. Eight tiles on a circle, each rotated out to its own
     point and rotated back so the artwork stays upright. --ring-i is set per
     item in the markup, so the geometry is arithmetic rather than eight rules.
     A definite rem size, not a percentage: a percentage inside translate
     resolves against the tile, not the ring. */
  .exring-stage {
    --ring-size: clamp(30rem, 46vw, 40rem);
    --ring-tile: clamp(6rem, 9vw, 8rem);
    --ring-r: calc((var(--ring-size) - var(--ring-tile)) / 2);
    position: relative;
    width: var(--ring-size);
    height: var(--ring-size);
    margin-inline: auto;
  }
  /* The circle the tiles sit on. Decorative — it draws the relationship the
     positions already make. */
  .exring-stage::before {
    content: "";
    position: absolute;
    inset: calc(var(--ring-tile) / 2);
    border: 1px solid var(--hairline-on-dark);
    border-radius: 50%;
  }
  .exring-centre {
    position: absolute;
    top: 50%;
    left: 50%;
    display: block;
    width: 17rem;
    margin: 0;
    translate: -50% -50%;
    color: var(--paper);
    font-size: var(--step-1);
    font-weight: 700;
    line-height: 1.25;
    text-align: center;
    text-wrap: balance;
  }
  .exring-list { position: absolute; inset: 0; border-top: 0; }
  .exring-item {
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--ring-tile);
    height: var(--ring-tile);
    margin: calc(var(--ring-tile) / -2);
    transform:
      rotate(calc(var(--ring-i) * 45deg))
      translateY(calc(var(--ring-r) * -1))
      rotate(calc(var(--ring-i) * -45deg));
  }
  .exring-link {
    display: block;
    min-height: 0;
    width: 100%;
    height: 100%;
    padding: 0;
    border-bottom: 0;
  }
  .exring-tile {
    width: 100%;
    height: 100%;
    padding: var(--sp-3);
    border-radius: var(--radius);
    /* Slow enough that one tile is still fading out as the next comes up, so
       the light reads as travelling round the ring rather than jumping. */
    transition: background-color 0.45s var(--ease), transform 0.4s var(--ease);
  }
  .exring-link:hover .exring-tile,
  .exring-link:focus-visible .exring-tile {
    background: var(--ring-tone);
    transform: scale(1.08);
  }
  /* Whichever document was last pointed at stays picked out, so the ring never
     falls back to eight blanks and the name in the middle always belongs to
     something. The first item carries .is-current from the template, so this
     holds with no JavaScript at all; explore-ring.js only moves the class. */
  .exring-item.is-current .exring-tile {
    background: var(--ring-tone);
    transform: scale(1.08);
  }
  /* Hidden to the eye, kept for the accessible name — the ring shows the name
     in the middle, which is decoration a screen reader never reaches. */
  .exring-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* ── Documents band ───────────────────────────────────
   /diversity-and-inclusion/'s two documents, on a screen of navy they have to
   themselves. The documents stack at the left; the right half is a diagram of
   five outlined circles drawing lines into a hub that lights as each one
   lands. It carries no information — it is the band's one piece of ornament,
   which is why it is aria-hidden and why a phone never sees it.

   The band is a full 100svh on purpose. explore-hold.js holds it as it passes,
   and a held section shorter than the window leaves a strip of screen below
   itself that the next section rides up into while it is pinned — which is
   exactly what the Inclusion Quality Mark section was doing to this one. A
   band that fills the window has no strip to give away. */
.exdocs {
  display: grid;
  align-content: center;
  min-height: 100svh;
  background: var(--bms-blue-900);
  color: var(--paper);
  --focus-ring: var(--paper);
  --exdocs-icon: clamp(3.5rem, 5vw, 5.5rem);
}
.exdocs h2 { color: var(--paper); }
.exdocs-shell {
  display: grid;
  /* Even columns. The diagram was being held back by the narrower of the two
     rather than by its own size terms, so growing it meant growing the column
     with it. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(var(--sp-12), 6vw, var(--sp-24));
  align-items: center;
}
.exdocs-list {
  display: grid;
  gap: clamp(var(--sp-6), 3svh, var(--sp-8));
  margin-top: clamp(var(--sp-8), 4svh, var(--sp-12));
}
/* Icon beside the name rather than above it: the two sit one under the other
   here, and a column each would set them further apart than they belong. */
.exdocs-doc {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--sp-6);
  align-items: center;
  color: var(--paper);
  text-decoration: none;
}
.exdocs-icon {
  width: var(--exdocs-icon);
  height: var(--exdocs-icon);
  transition: transform 0.3s var(--ease);
}
.exdocs-icon img { display: block; width: 100%; height: 100%; }
.exdocs-doc:hover .exdocs-icon,
.exdocs-doc:focus-visible .exdocs-icon { transform: translateY(-4px); }
.exdocs-name { display: block; font-size: var(--step-1); font-weight: 600; }
.exdocs-doc:hover .exdocs-name,
.exdocs-doc:focus-visible .exdocs-name {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}
/* Part of the link text, as it is in a document list: the format is shown,
   the size is in the accessible name only. */
.exdocs-meta { display: block; color: var(--bms-sky); font-size: var(--step--1); }

/* ── The diagram ──────────────────────────────────────
   Five sectors of a pie chart, each filled with a mosaic of national flags.
   They arrive one at a time along their own bisector, two seconds each,
   fading up as they travel; the finished circle draws breath — a half-second
   shrink — then swells back to full size and full colour while a ripple runs
   out from its centre and off the edge. Twelve seconds, once, when the band
   is reached.

   Built from the artwork and timings in
   Explore-Pages/Diversity and Inclusion/Breakdown6-animation-12s-3d-transparent-assets:
   the five pieces are that folder's, byte for byte, and every number below is
   read off its animation.json rather than eyeballed. It ships as five images
   and a stylesheet instead of the folder's 19MB animated WebP or its 90MB of
   frames — 136KB in total, and sharp at any size.

   The ripple is the one thing not taken literally. The folder renders it as a
   displaced height field with its own lighting — 61 frames of artwork, which
   cost 0.9MB to encode even at 480px, seven times the whole diagram for a
   second and a half of ornament. It is a travelling band of light and shadow
   here instead, which the compositor draws for nothing.

   Nothing here runs a script of its own. The pieces are ordinary <img>
   elements laid out by percentage, so the stylesheet alone is already the
   finished circle; the animation only decides where each one comes from, and
   the only thing asked of the page is the reveal class that starts it. */
/* The band's own padding comes out of the height on purpose. explore-hold.js
   will not pin a section taller than the window — being locked on something
   you cannot see the whole of is the failure that pattern exists to avoid —
   and this figure is the tallest thing in the band, so a plain 86svh put the
   section six pixels over on a 900px window and quietly switched the hold
   off. Sized against the space the section actually has, it cannot. */
.exdocs-figure {
  justify-self: center;
  width: min(100%, calc(100svh - 2 * var(--ex-space)), 50rem);
  /* The ring below asks this element how wide it ended up. Safe to contain:
     the width is set here and owes nothing to the content. */
  container: plot / inline-size;
}
/* The circle and the ring of figures around it. The square is 2.864 times the
   drawn circle's radius across — the room the furthest label needs, which is
   Caribbean out on the left — so the circle takes 75.1% of it and everything
   in the ring is placed by percentage of the square. Sized off the mock-up,
   where the circle is 556px inside a 796px plot. */
.exflags-plot {
  position: relative;
  /* Type round the circle scales with it, down to the site's ordinary
     reading size and no further — below that the ring gives way to the list.
     3.23cqw lands on --step-1 at the width the mock-up was drawn at. */
  --key-size: clamp(var(--step-0), 3.23cqw, var(--step-1));
  /* The three moments the ending hangs off: the last sector lands, half a
     settle later the circle turns and the ripple leaves the centre, and a
     second after the ripple has gone the figures arrive. */
  --flags-assembled: calc(5 * var(--t-flags-fly));
  --flags-ripple: calc(5 * var(--t-flags-fly) + var(--t-flags-settle) / 2);
  --flags-keys: calc(5 * var(--t-flags-fly) + var(--t-flags-settle) / 2
                     + var(--t-flags-ripple) + var(--t-flags-hold));
}
/* The artwork's circle is 1004px across on its 1080 canvas, centred a pixel
   and a half above and to the left of the middle. The shrink's origin, the
   ripple's centre and the mask that keeps the ripple inside the rim are all
   placed off those three numbers rather than off the box. */
.exflags {
  position: absolute;
  inset: 12.44%;
  /* Shown only where the ring is; the list carries the figures otherwise, and
     a phone never fetches the five pieces. */
  display: none;
  --centre-x: 49.861%;
  --centre-y: 49.954%;
  --rim: 46.481%;
  /* So the ripple blends with the flags and with nothing behind them. */
  isolation: isolate;
  transform-origin: var(--centre-x) var(--centre-y);
}
/* Gated on .js throughout, the way the reveal is: with scripting off nothing
   ever adds .is-revealed, so an ungated paused animation would leave the
   circle stopped on its opening frame — which is now an empty box, since the
   sectors start hidden. Without the class the stylesheet alone is already the
   finished circle. */
.js .exflags {
  animation: exflags-settle var(--t-flags-settle) var(--ease-settle)
             var(--flags-assembled) both;
  animation-play-state: paused;
}
/* Full size at ten seconds, nine tenths at ten and a half, full again at
   eleven — smoothstep each way, which is the easing animation.json names. */
@keyframes exflags-settle {
  0%   { transform: scale(1); }
  50%  { transform: scale(0.9); }
  100% { transform: scale(1); }
}
/* Placed by percentage of the 1080-square the artwork was drawn on, so the
   whole thing scales with its container and the seams stay shut. */
.exflags-piece {
  position: absolute;
  left: var(--x);
  top: var(--y);
  display: block;
  width: var(--w);
  height: var(--h);
}
.js .exflags-piece {
  animation:
    exflags-land var(--t-flags-fly) var(--ease-scrim) var(--in) both,
    exflags-fade var(--t-flags-fly) var(--ease-scrim) var(--in) both,
    exflags-full calc(var(--t-flags-settle) / 2) linear var(--flags-ripple) forwards;
  /* Held out of sight until the band is reached. A twelve-second story that
     has already finished by the time the reader arrives is no story. */
  animation-play-state: paused;
}
.exdocs-figure.is-revealed .exflags,
.exdocs-figure.is-revealed .exflags-piece,
.exdocs-figure.is-revealed .exflags-crest,
.exdocs-figure.is-revealed .exflags-keys { animation-play-state: running; }
/* --fx and --fy are the offset each piece travels, as a percentage of ITS OWN
   width and height rather than the container's — which is what a translate
   percentage resolves against. animation.json gives the distance in pixels of
   the 1080 canvas along a unit bisector; these are that, divided by the
   piece's own size. */
@keyframes exflags-land {
  from { transform: translate(var(--fx), var(--fy)); }
  to   { transform: translate(0, 0); }
}
/* A tenth opaque to four fifths as it travels, on the same easing as the
   travel itself — animation.json ties the two together. The nought-width
   first step is what keeps a sector out of sight until its turn comes: a
   delayed animation filling backwards otherwise shows its opening frame for
   the whole of the wait, and all five would sit off the circle from the
   start. */
@keyframes exflags-fade {
  0%      { opacity: 0; }
  0.001%  { opacity: 0.1; }
  100%    { opacity: 0.8; }
}
/* Four fifths to full, with the expansion. Filling forwards only, so it
   leaves the fade alone until its own turn rather than pinning every sector
   at four fifths from the start. */
@keyframes exflags-full {
  from { opacity: 0.8; }
  to   { opacity: 1; }
}
.exflags-piece--europe    { --x: 44.537%; --y:  1.296%; --w: 54.074%; --h: 97.407%; --fx:  99.144%; --fy:   1.885%; --in: calc(0 * var(--t-flags-fly)); }
.exflags-piece--asia      { --x:  7.037%; --y: 47.685%; --w: 45.185%; --h: 50.833%; --fx: -71.174%; --fy:  99.271%; --in: calc(1 * var(--t-flags-fly)); }
.exflags-piece--caribbean { --x:  1.204%; --y: 33.148%; --w: 51.019%; --h: 41.481%; --fx: -98.730%; --fy:  11.259%; --in: calc(2 * var(--t-flags-fly)); }
.exflags-piece--africa    { --x:  3.611%; --y:  2.037%; --w: 48.611%; --h: 50.185%; --fx: -90.267%; --fy: -100.369%; --in: calc(3 * var(--t-flags-fly)); }
.exflags-piece--other     { --x: 39.259%; --y:  1.296%; --w: 12.963%; --h: 50.833%; --fx: -35.400%; --fy: -98.907%; --in: calc(4 * var(--t-flags-fly)); }

/* ── The ripple ───────────────────────────────────────
   A band of light and shadow running out from the centre of the finished
   circle. Two elements: the outer one clips to the rim and does the blending,
   the inner one carries the gradient and is scaled. They have to be separate
   — clip-path is applied before the transform, so a single element would
   scale its own mask and stop clipping the moment it moved.

   Scaling rather than animating the gradient's stops is deliberate: the ring
   travels and broadens together, which is what a spreading wave does, and a
   transform is composited where a gradient would be repainted every frame.

   The stops are fractions of the distance from the centre to the nearest
   edge, which is 538.5px of the 1080 canvas. The crest sits at 77.5% of that
   — 417px — so it crosses the 502px rim at scale 1.20, at 11.5 seconds, which
   is where the folder's render has its wave meet the edge; by the closing
   scale of 1.75 the whole band is outside the artwork.

   Hard light rather than overlay, which keys off the backdrop and so does
   nothing at all to the white in half these flags. This keys off the band
   itself: its crest lightens whatever it crosses and its trough darkens,
   which is how a moving highlight has to behave to be seen. The values were
   set by rendering both against the folder's frames rather than by eye. */
.exflags-wave {
  position: absolute;
  inset: 0;
  clip-path: circle(var(--rim) at var(--centre-x) var(--centre-y));
  mix-blend-mode: hard-light;
  pointer-events: none;
}
.exflags-crest {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle closest-side at var(--centre-x) var(--centre-y),
    transparent 66%,
    rgba(0, 0, 0, 0.35) 72%,
    rgba(255, 255, 255, 0.8) 77.5%,
    rgba(255, 255, 255, 0.25) 82%,
    transparent 87%);
  transform-origin: var(--centre-x) var(--centre-y);
  /* Nothing to show unless the animation is running. No scripting and no
     motion both land here: a finished circle with no wave crossing it. */
  opacity: 0;
}
.js .exflags-crest {
  will-change: transform, opacity;
  animation: exflags-ripple var(--t-flags-ripple) linear var(--flags-ripple) both;
  animation-play-state: paused;
}
/* Linear: a wave travels at a constant speed. It is a point at the centre
   when the expansion begins and it fades from a third of the way out, so that
   what reaches the rim is a trace rather than a bright ring drawn round the
   edge — the one thing the clip does not flatter. */
@keyframes exflags-ripple {
  0%   { transform: scale(0.06); opacity: 0; }
  6%   { opacity: 1; }
  35%  { opacity: 1; }
  100% { transform: scale(1.75); opacity: 0; }
}

/* ── The figures ──────────────────────────────────────
   What the circle is a chart of, which until now it never said. The five
   shares are the school's own; keep them and the artwork in step.

   They are ordinary text in an ordinary list, laid over the plot rather than
   drawn into it, so they are selectable, translatable, and read out in order
   under the title that names them.

   A ruled list is the default and the ring is the enhancement, because what
   decides between them is not the viewport but whether the circle is big
   enough to be labelled. Round a small circle, type at a readable size sits
   on the artwork: at 1024px the plot is 359px across and "Caribbean" lands
   two dozen pixels inside the rim. So the switch is a container query on the
   figure's own width, which also catches a wide but short window, where the
   plot is held down by the height it has rather than the width. */
.exflags-list {
  margin: 0;
  padding: 0;
  list-style: none;
  margin-top: var(--sp-6);
}
.exflags-title {
  margin: 0;
  font-size: calc(var(--key-size) * 1.15);
  font-weight: 800;
  line-height: 1.2;
}
.exflags-key {
  font-size: var(--key-size);
  line-height: 1.3;
  display: flex;
  justify-content: space-between;
  gap: var(--sp-6);
  padding-block: var(--sp-3);
  border-top: 1px solid rgba(255, 255, 255, 0.28);
}
.exflags-key:last-child { border-bottom: 1px solid rgba(255, 255, 255, 0.28); }

@container plot (min-width: 560px) {
  /* Stations round the circle. Four of the five sit on their own sector's
     bisector, read off animation.json; Europe's is pulled down to 40 degrees
     because its own bisector points at three o'clock, where the title goes.
     The percentages are of the plot square, so the ring holds at any size. */
  .exflags-plot { aspect-ratio: 1; }
  .exflags { display: block; }
  .exflags-keys {
    position: absolute;
    inset: 0;
  }
  .exflags-title,
  .exflags-key {
    position: absolute;
    left: var(--kx);
    top: var(--ky);
    transform: translate(-50%, -50%);
    text-align: center;
  }
  .exflags-list { margin-top: 0; }
  .exflags-key {
    display: block;
    padding-block: 0;
    border: 0;
  }
  /* Its own rule, to match the one that draws it: the ruled list closes with
     a line under the last row, and that outweighs the border reset above. */
  .exflags-key:last-child { border-bottom: 0; }
  /* The share on its own line under the name, as the mock-up has it. */
  .exflags-key span { display: block; }
  /* The tightest box that still breaks into the mock-up's four lines: "our
     staff and" is 6.18em wide and is the longest of them. */
  .exflags-title { width: 6.4em; }
  /* Set out a little further than the mock-up has it — same bearing, radius
     1.47 rather than 1.41 — because the title is a size up from the labels
     there and its bottom corner would otherwise sit on the rim. */
  .exflags-title          { --kx: 87.36%; --ky: 14.83%; }
  .exflags-key--other     { --kx: 46.93%; --ky: 10.29%; }
  .exflags-key--africa    { --kx: 23.11%; --ky: 18.99%; }
  /* Set out to 1.30 rather than the mock-up's 1.24: it is much the longest of
     the five, so at the same radius its last letters sat almost on the rim.
     It is the one label that reaches past the plot square, into the column
     gap, which has room to spare. */
  .exflags-key--caribbean { --kx:  4.60%; --ky: 52.13%; }
  .exflags-key--asia      { --kx: 28.14%; --ky: 83.17%; }
  .exflags-key--europe    { --kx: 83.42%; --ky: 77.90%; }
  /* All five and the title together, half a second after the ripple has gone —
     the circle finishes its own story before it is annotated. Backwards fill
     is what holds them at nought through the wait. */
  .js .exflags-keys {
    animation: exflags-keys var(--t-flags-keys) var(--ease-scrim) var(--flags-keys) both;
    animation-play-state: paused;
  }
}
@keyframes exflags-keys {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* This band's own column, wider than the page's 80rem, and split unevenly.
   The ring of figures needs half again the width the bare circle did, and the
   documents beside it only ever hold a heading and two links.

   Not below 1440px: "documents" is one word set at --step-4 and cannot wrap,
   so the left column has a hard floor that scales with the heading. Even
   columns clear it everywhere; 0.735 of one only clears it from about here
   up, and the circle is small enough to fit a plain half further down. */
@media (min-width: 1440px) {
  .exdocs-shell {
    --ex-shell: min(92rem, 100% - var(--sp-12));
    grid-template-columns: minmax(0, 0.735fr) minmax(0, 1fr);
  }
}

@media (max-width: 1023px), (hover: none) and (pointer: coarse) and (max-width: 1400px) {
  /* One column, and no screen to fill: what is left here is a heading, two
     documents and the figures. */
  .exdocs { min-height: 0; }
  .exdocs-shell { grid-template-columns: minmax(0, 1fr); }
  .exdocs-figure { width: 100%; }
  /* Room above only where the figures follow the documents rather than
     sitting beside them. */
  .exflags-title { margin-top: var(--sp-12); }
}

@media (max-width: 900px) {
  .expanel--media { grid-template-columns: 1fr; }
}

@media (max-width: 1100px) {
  .exsplit,
  .exsplit--narrow { grid-template-columns: 1fr; gap: var(--sp-12); }
  /* One column, so the photograph goes back above the copy it belongs to. */
  .exsplit--flip .exsplit-figure { order: 0; }
  /* Full width now, so the tall portrait shape would run off the screen. */
  .exsplit--portrait .exsplit-figure { aspect-ratio: 16 / 11; max-height: none; }
  /* A portrait whose subject stands high in the frame — About Us, What matters
     to us. The wide single-column crop taken from the middle cut the top of
     her head off; taken from a third of the way down it keeps her whole head
     with room above it (Sean, 2026-09-19). */
  .exsplit--portrait .exsplit-figure--upper img { object-position: center 33%; }
}

/* Bounded at both ends on purpose. The :not() below is a class more specific
   than the plain .exrow rule in the next block, so an open-ended max-width
   would still have been winning at 375px and the four-cell grid stayed two
   across on a phone. :not(--one) keeps a deliberate single column single. */
@media (min-width: 701px) and (max-width: 1100px) {
  .exrow:not(.exrow--one) { --exrow-cols: 2; }
}

@media (max-width: 700px) {
  .exrow { --exrow-cols: 1; }
  .exrow-cell { padding-right: 0; }
  /* Nobody needs telling to scroll on a phone. Removing the element takes its
     margins with it, so the space goes too. */
  .scrollcue { display: none; }
}
