/* ============================================================
 * GROUPED HOME V2 — bold direction
 * Bold type · colour-block sections · micro-interactions ·
 * Monzo nav (audience pill + mega-menu bento) · no flat seams.
 * Loads after colors_and_type.css + foundation.css.
 * ============================================================ */

:root {
  --hv2-ease: cubic-bezier(0.2, 0.8, 0.2, 1);

  /* ============================================================
   * TILE + PHOTO-FRAME TOKENS (4 Aug 2026)
   * From a measured brand review: the same white content card was wearing five
   * corner radii (18/20/22/24/28), the white photo frame came in two
   * thicknesses (26px and 14px), and 1px hairlines survived on tiles Rebecca
   * had twice asked to lose. Every tile and frame reads these values now, so a
   * new component cannot quietly invent a sixth radius.
   * Structural borders are NOT strokes and keep theirs on purpose: row dividers
   * (.pv3-frag__row, .pcard__price, .trust2__badges), the matrix's Included /
   * Not-included marks, controls (buttons, tabs), and the translucent white
   * edges used on photography.
   * ============================================================ */
  --tile-radius: 24px;                                 /* one radius for content cards */
  --tile-shadow: 0 8px 24px rgba(15, 20, 56, 0.08);    /* the soft lift that replaced strokes */
  --photo-frame: clamp(16px, 1.8vw, 26px);             /* chunky white frame, one thickness */
}

/* ============================================================
 * H1 TIER TABLE (4 Aug 2026)
 * The review found EIGHT different H1 sizes at 1440px (46/62/66/68/74/75/76/80)
 * with no principle behind the middle three. There are four jobs, so there are
 * four sizes. Pick a tier for a new page; do not invent a fifth.
 *
 *   DISPLAY    80px   the front door and the campaign page
 *                     -> home, martyns-law
 *   STATEMENT  76px   pages that open on one oversized sentence
 *                     -> About us (75, see note), Careers, Privacy, Terms, Carbon
 *   PAGE       62px   ordinary interior pages
 *                     -> One, Classroom, OnSite, Parent App, Parents,
 *                        Primary, Secondary, LAs & MATs, Founders Club,
 *                        Blog (was 66), Book a Demo (was 68)
 *   COMPACT    46px   utility pages and article heroes, where the H1 is a label
 *                     rather than a headline
 *                     -> Parent Help Centre, blog articles
 *
 * NOTE on About us: its H1 is .statement__line, the shared statement-band size
 * used on four other pages, which lands at 75px. Left alone deliberately —
 * forcing 76 would move the statement bands everywhere else for a 1px gain
 * nobody can see.
 * ============================================================ */

/* ============================================================
 * PHOTO RATIO STANDARD (4 Aug 2026)
 * The review measured SIXTEEN rendered photo ratios. Content photography now
 * uses three, each with a job:
 *   1:1    square   portraits and single-subject shots
 *                   -> .cornerlist__photo, .pv3-feat__photo, .ab2-quote__photo
 *   4:3    landscape  the default for photos sitting beside copy
 *                   -> .pv3-chap__photo, .parents__photo, .ab2-beat__img
 *                      (was 4/3.1), .os-way__photo (was 16/11)
 *   16:9   wide     photos that lead a card or a featured slot
 *                   -> .blogcard__img (was 16/10), .bhfeat__img (was 16/11),
 *                      .pcband__photo
 *
 * Three things are deliberately NOT in the standard:
 *   - HERO PORTRAITS sit at 1.1:1 — but both hero families (.hero2__photoframe
 *     and .pv3g__photo) already agree on it, so it is coherent as its own tier.
 *     Changing it would recompose approved heroes for no visible gain.
 *   - BACKGROUND FILLS (.hero2__photo, .band--photo__img, .trust2__photo) take
 *     their shape from the tile they fill, by design.
 *   - DEVICE MOCKUPS and UI screenshots (.pair__phone, .os-way__shot,
 *     .pst__tile, .pv3-acc__frames) are product shots, not photography.
 * ============================================================ */

body.hv2 {
  background: #fff;
  overflow-x: hidden; /* seam elements deliberately cross section edges */
}

/* Headline voice — SemiBold 600 to match the weight of the GroupEd
   logo wordmark (July 2026; was 700/800, which read heavier than the
   logo). Size + colour carry the hierarchy; big stat NUMBERS stay
   bolder via their own classes (.roi__num, .pcard__amount, etc.). */
.hv2 h1, .hv2 h2 { letter-spacing: -0.02em; line-height: 1.05; }
.hv2 h1 { font-weight: 600; }
.hv2 h2 { font-weight: 600; font-size: clamp(32px, 4vw, 54px); }
.hv2 h3 { font-weight: 600; letter-spacing: -0.01em; }

/* ------------------------------------------------------------
 * STATEMENT BAND — one typographic statement per page (Rebecca, 28 Jul:
 * "Once per page, go to 150px+ with a single sentence and nothing else
 * in the band... oversized restraint reads as confidence"). Deliberately
 * bare: no eyebrow, no CTA, no image — the size does the work. Currently
 * used once each on About Us and Careers; reuse sparingly elsewhere, not
 * on every page, or it stops reading as a moment.
 * ------------------------------------------------------------ */
.statement { background: #fff; padding: clamp(90px, 11vw, 160px) 0; }
/* Size settled at 84px / 30ch (Rebecca, 28 Jul: "can make sure the statement
   lines you add are the same size text as the one you did for the about us
   page"). This started life as a --mission variant when the base was
   152px/17ch: that base made one long flowing sentence run to five enormous
   lines with the scaffolding words shouting as loudly as the claim, so the
   mission got a smaller size and a wider measure — and that is the version
   she liked. It is now the DEFAULT, so every statement band on the site
   matches and a new one can't accidentally be added at the old size. */
.statement__line {
  max-width: 30ch;
  margin: 0;
  font-size: clamp(32px, 5.2vw, 84px);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -0.02em;
  color: var(--ge-navy, #0F1438);
}
.statement__line .mark { color: var(--ge-blue, #1B367E); }

/* ---------- STATEMENT: COPY LEFT, THE CLAIM MADE LITERAL RIGHT ----------
 * 5 Aug 2026. Rebecca asked what Monzo would do with this band; the answer she
 * picked was "give the right-hand half something". Scoped to
 * .statement--split-band so the four other statement bands on the site (About
 * us, Careers, Martyn's Law, Book a Demo) keep their single full-width line.
 *
 * The columns are 1.15fr / 0.85fr, not 1fr each: the headline needs to stay at
 * or near its 30ch measure or it drops to five lines, and the visual is a
 * narrow stack that looks stranded in a half-page column.
 * align-items: center rather than start — the visual is much shorter than the
 * headline block, and hanging it off the top left an obvious hole underneath. */
.statement--split-band .statement__split {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: clamp(32px, 5vw, 80px);
  align-items: center;
}
@media (max-width: 900px) {
  /* stacked: the visual goes under the copy, and its own column centres so it
     doesn't sit awkwardly against the left edge on a phone */
  .statement--split-band .statement__split { grid-template-columns: 1fr; gap: clamp(36px, 7vw, 52px); }
  .statement--split-band .stmtviz { margin-inline: auto; }
}

/* ---------- five become one, WORDLESS ----------
 * Second version. The first had a "Today" label, five named chips and a "One
 * login. One bill." tile; Rebecca cut every word of it — "i like how yourve
 * done 1. but get rid of all this" — and picked the wordless take. Which is the
 * right call: every one of those words already appears in the paragraph to the
 * left, so the visual was repeating the copy rather than adding to it.
 *
 * What carries the meaning now is contrast, three ways at once: five small
 * shapes against one big one, grey against full colour, and a scattered row
 * against a centred single mark. No text needed, and nothing for a screen
 * reader to read twice (the whole block stays aria-hidden).
 *
 * The tiles are rounded squares, not hexagons — a hexagon is OUR mark, and five
 * grey ones would read as five GroupEds rather than five other systems. */
.stmtviz { max-width: 380px; display: flex; flex-direction: column; align-items: center; gap: 22px; }

.stmtviz__row { display: flex; gap: 12px; }
.stmtviz__tile {
  width: 56px; height: 56px;
  border-radius: 16px;
  background: #F3F7FA;
  border: 1px solid #E6ECF4;
  display: grid;
  place-items: center;
}
/* the hollow dot is the only mark on each tile — a login with nothing behind it */
.stmtviz__tile::before {
  content: "";
  width: 12px; height: 12px;
  border-radius: 50%;
  border: 2px solid #C6CFDB;
}
/* tiny, alternating: enough that the row reads as five separate things rather
   than one tidy component, not enough to look like a mistake */
.stmtviz__tile:nth-child(odd)  { transform: rotate(-2deg); }
.stmtviz__tile:nth-child(even) { transform: rotate(2deg) translateY(3px); }

.stmtviz__arrow { color: #C6CFDB; display: block; }
.stmtviz__arrow svg { width: 26px; height: 26px; display: block; }

/* the payoff: the real mark, in full colour, at three times a tile's width */
.stmtviz__mark { width: clamp(88px, 8vw, 112px); height: auto; display: block; }

@media (max-width: 480px) {
  /* five 56px tiles + gaps need 332px; below that they shrink rather than wrap,
     because a wrapped row of 3 + 2 stops reading as "five of them" */
  .stmtviz__row { gap: 9px; }
  .stmtviz__tile { width: 46px; height: 46px; border-radius: 13px; }
}
/* Optional supporting line under a statement (Rebecca, 29 Jul: "could we add
   to it to add a bit more context on what we do and why"). Deliberately small
   and secondary-grey — the whole point of this band is one oversized line, so
   this has to read as a footnote to it, never as a second headline. Its
   measure is wider than the line above (48ch vs 30ch) so the two edges don't
   line up and it can't be mistaken for another display line. Only the home
   page uses it; the other four statement bands stay as a single line. */
/* ⚠ LEFT, not centred — 7 Aug 2026 ("can you align the paragraph").
   This rule is a leftover from when the band was centred. The second
   .statement__sub rule further down already cancels the auto margin, so the box
   lines up with the headline at 88px — but `text-align: center` survived, which
   centred the TEXT inside a 598px box under a left-aligned 30ch headline. The
   result reads as an indent plus a ragged left edge, which is what she spotted.
   The identical fault was fixed on .statement--hero (About us) on 6 Aug; this is
   the same bug on the plain band, so it is fixed at source rather than patched
   per page. Affects the home page and compare-school-mis; the three
   .statement--hero pages were already forced left in modular.css.
   text-wrap: balance stays — it evens the line lengths without centring them. */
.statement__sub {
  text-align: left;
  text-wrap: balance;
}

.statement__sub {
  max-width: 48ch;
  margin: clamp(20px, 2.2vw, 30px) 0 0;
  font-size: clamp(16px, 1.35vw, 19px);
  font-weight: 400;
  line-height: 1.55;
  letter-spacing: 0;
  color: #4A5876;
}

/* ------------------------------------------------------------
 * WHY GROUPED — oversized stat statement (Rebecca, 28 Jul: replacing the
 * photo + floating-pill "proof on the glass" layout — "its not the photo i
 * dont like the layout styling. i want to go in a new direction"). Reuses
 * the About/Careers .statement pattern (one big confident line) applied to
 * a real number, then the same four promises as a plain icon row — no
 * cards, no photo, no coloured tile backgrounds. Bright icon accents on a
 * white/clean base, matching the "fun/bright = accents on clean, not
 * coloured section washes" rule from the home bright-bands revert.
 * ------------------------------------------------------------ */
.proofstat {
  position: relative;
  margin-top: clamp(48px, 6vw, 80px);
  padding-top: clamp(8px, 1vw, 16px);
  overflow: hidden;
  /* Rebecca, 29 Jul: "can you make sure this is centralised in its
     section" — the .container this sits in is itself centred (equal
     gutters either side), but the heading/sub text was left-aligned
     inside it while the card row below fills the full container width,
     so the two halves of this block read as off-balance against each
     other. Centring the text (and giving it centred auto margins, since
     it has its own max-width narrower than the container) matches it to
     the symmetric card row below. */
  text-align: center;
}
.proofstat__eyebrow {
  position: relative;
  z-index: 1;
  display: inline-block;
  margin-bottom: 18px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: #fff;
  background: var(--ge-navy, #0F1438);
  border-radius: 999px;
  padding: 7px 15px;
}
.proofstat__line {
  position: relative;
  z-index: 1;
  margin: 0 auto 16px;
  max-width: 21ch;
  font-size: clamp(30px, 4.6vw, 64px);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--ge-navy, #0F1438);
}
/* the number keeps the bolder display weight big stats always use
   site-wide (headings are 600, but stat numbers stay 700/800 for punch) */
.proofstat__line .odo-wrap { font-weight: 800; }
.proofstat__line .mark {
  display: inline-block;
  line-height: 1;
  background: var(--ge-blue-3, #D9F4FF);
  color: var(--ge-blue, #1B367E);
  border-radius: 0.24em;
  padding: 0.08em 0.16em 0.12em;
  margin-block: 0.1em 0.04em;
}
.proofstat__sub {
  position: relative;
  z-index: 1;
  margin: 0 auto clamp(28px, 3.4vw, 44px);
  max-width: 46ch;
  color: #4A5876;
  font-size: clamp(16px, 1.5vw, 18px);
}
/* ------------------------------------------------------------
 * WHY-CARDS — Starling Bank's product-row, adapted (Rebecca, 29 Jul:
 * "especially this bit where they go through their products under their
 * hero... I dont want it to go off the screen like they have though").
 *
 * Starling's row is a horizontal snap-scroll carousel with prev/next
 * arrows that runs past the right edge of the viewport — deliberately NOT
 * copied. This is a plain CSS grid at the SAME 980px/620px breakpoints the
 * row it replaces already used, so all four cards are always fully on
 * screen and there is nothing to scroll or click through.
 * Card anatomy mirrors Starling's: small caps eyebrow, bold two-clause
 * headline, then a big icon "illustration" near the bottom (no "Learn
 * more" link — dropped per Rebecca, 29 Jul, these cards don't go anywhere).
 * Copy and icon/colour per card are unchanged from the plain row this
 * replaces — condensed to a headline, not invented.
 * ------------------------------------------------------------ */
.whycards {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(16px, 1.8vw, 24px);
}
.whycard {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 10px;
  background: #fff;
  border: 1px solid #E6ECF4;
  border-radius: 24px;
  padding: clamp(24px, 2.4vw, 32px) clamp(18px, 2vw, 24px) clamp(28px, 3vw, 36px);
  /* staggered entrance, same device .amb2__fig/.amb2__commit use on About Us */
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 420ms var(--hv2-ease), transform 420ms var(--hv2-ease);
  transition-delay: calc(var(--i, 0) * 90ms);
}
.rv.in .whycard { opacity: 1; transform: none; }
.whycard__eyebrow {
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #7C88A6;
}
.whycard__head {
  margin: 0;
  max-width: 20ch;
  font-size: clamp(17px, 1.5vw, 19px);
  font-weight: 600;
  line-height: 1.3;
  color: var(--ge-navy, #0F1438);
}
.whycard__ico {
  flex: none;
  margin-top: 6px;
  width: 68px;
  height: 68px;
  border-radius: 999px;
  display: grid;
  place-items: center;
  /* soft shadow "resting on a surface" beneath the icon, echoing Starling's
     floating-illustration-with-shadow-ellipse — a plain blurred tint, not a
     gradient fill */
  box-shadow: 0 18px 20px -14px rgba(15, 20, 56, 0.35);
}
.whycard__ico svg { width: 30px; height: 30px; }
@media (max-width: 980px) {
  .whycards { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 620px) {
  .whycards { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  .whycard { opacity: 1; transform: none; transition: none; }
}

/* ------------------------------------------------------------
 * (was: CURSOR-REACTIVE CARD GLOW)
 * Removed 3 Aug 2026 — Rebecca: "i dont like the hover glow effect we
 * have - get rid of it anywhere it is on site". It was a sky spotlight
 * that tracked the pointer across .vcard/.st-card/.pv3-ben/.ph-ben/
 * .ml-cap, driven by home-v2.js (that block is gone too).
 * position/overflow are KEPT below on purpose: they were added for the
 * glow, but overflow:hidden may now be clipping other card content to
 * the border radius, and dropping it could shift layouts on five card
 * families across the site. Cards keep their translateY lift — that was
 * always a separate rule, and she only objected to the glow.
 * ------------------------------------------------------------ */
.vcard, .st-card, .pv3-ben, .ph-ben, .ml-cap {
  position: relative;
  overflow: hidden;
}

/* ------------------------------------------------------------
 * PARENTS BAND — photo left, copy right
   Flipped against its neighbours on purpose (see the note in the HTML).
   A trio of app screens was tried here; Rebecca asked for one single photo
 * of a parent instead. The floating white stat card carries the 18,790
 * count-up.
 * Lives here, not in gsap-trial.css, because it is used on BOTH the trial
 * home page and parent-app.html (3 Aug).
 * ------------------------------------------------------------ */
/* Vertically centred in its band (Rebecca, 3 Aug: "make parents are on
   grouped everyday too section to be central to the section"). It measured
   17px of space above against 114px below.
   Cause: .band--sky deliberately carries only padding-top:1px — that 1px
   exists purely to block margin-collapse, because the OTHER sky band gets
   its top spacing from .proofstat's own margin-top. This block has no such
   margin, so it sat flush to the section's top edge.
   Fix is a margin-top mirroring the band's padding-bottom, matching how
   .proofstat does it — deliberately NOT extra padding on .band--sky, which
   is shared and would double the corners section's top gap. */
.parents {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.92fr);
  gap: clamp(32px, 5vw, 80px);
  align-items: center;
  margin-top: clamp(72px, 9vw, 130px);
}
/* the photo sits in its own stacking context so the stat card can overlap
   it without escaping the column */
.parents__media {
  position: relative;
  /* room for the stat card hanging off the bottom-left */
  padding-bottom: clamp(40px, 4vw, 64px);
}
.parents__photo {
  margin: 0;
  /* 4:3 is within a whisker of the file's native 1.339, so almost nothing
     is cropped, and it lands about as tall as the copy column beside it */
  aspect-ratio: 4 / 3;
  border-radius: 24px;
  overflow: hidden;
  background: var(--ge-bg-light, #F3F7FA);
  box-shadow: 0 14px 34px rgba(15, 20, 56, 0.14);
}
.parents__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The floating "Mr R Morgan · English" message tile that used to sit on this photo
   was removed from the markup on 5 Aug at Rebecca's request, so its rules have gone
   with it. The only remaining references are in _hero-trial.html and
   _inset-trial.html, which are snapshot copies of an older home page and are not
   linked from anywhere. The .parents__stat count-up card is untouched. */

/* On phones the photo is small enough that a card in its corner would cover a
   third of it, and the negative offsets would poke past the viewport edge. */
@media (max-width: 720px) {
}

.parents__stat {
  position: absolute;
  z-index: 2;
  left: 0;
  bottom: 0;
  background: #fff;
  border-radius: var(--tile-radius);   /* was 18px — unified 4 Aug */
  padding: clamp(12px, 1.3vw, 18px) clamp(14px, 1.5vw, 22px);
  box-shadow: 0 22px 46px rgba(15, 20, 56, 0.22);
  max-width: 78%;
}
/* big numbers keep 700/800 per the design rules — they are not headings */
.parents__num {
  margin: 0;
  font-size: clamp(30px, 3.4vw, 46px);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--ge-navy, #0F1438);
}
.parents__cap { margin: 5px 0 0; font-size: 12.5px; line-height: 1.4; color: #4A5876; }
.parents__copy { min-width: 0; }
.parents__copy h2 { margin: 0 0 14px; color: var(--ge-navy, #0F1438); max-width: 22ch; }
.parents__lede {
  margin: 0 0 clamp(20px, 2.2vw, 28px);
  max-width: 46ch;
  color: #4A5876;
  font-size: clamp(16px, 1.35vw, 18.5px);
  line-height: 1.55;
}
.parents__list {
  list-style: none;
  margin: 0 0 clamp(22px, 2.4vw, 30px);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(10px, 1.1vw, 14px);
  max-width: 44ch;
}
.parents__list li {
  position: relative;
  padding-left: 30px;
  color: var(--ge-navy, #0F1438);
  font-size: clamp(15.5px, 1.25vw, 17px);
  line-height: 1.5;
}
/* Navy disc, white glyph — the site standard (Rebecca, 5 Aug: "i noticed the navy
   bullet points with white ticks are consitent across site. ive seen some blue
   ones that need changing"). This was a deep-sky #1B88C8 disc carrying a NAVY
   glyph, because white on that sky fell under 4.5:1. Navy disc + white glyph
   clears that at 16:1 and matches every other tick list on the site. */
.parents__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.3em;
  width: 17px; height: 17px;
  border-radius: 50%;
  background: var(--ge-navy, #0F1438) center / 10px 10px no-repeat;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
}
.parents__link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--ge-blue, #1B367E);
  font-weight: 600;
  font-size: 15.5px;
  text-decoration: none;
}
.parents__link:hover { color: var(--ge-navy, #0F1438); }
.parents__link svg { width: 18px; height: 18px; transition: transform 200ms var(--hv2-ease); }
.parents__link:hover svg { transform: translateX(4px); }
@media (max-width: 900px) {
  .parents { grid-template-columns: 1fr; gap: clamp(26px, 5vw, 40px); }
  /* copy first on a phone — the headline should lead, not the screens */
  .parents__copy { order: -1; }
  .parents__copy h2, .parents__lede, .parents__list { max-width: none; }
  .parents__media { max-width: 520px; }
  .parents__stat { max-width: none; position: static; margin-top: 16px; box-shadow: 0 14px 30px rgba(15,20,56,0.16); }
}
@media (prefers-reduced-motion: reduce) {
  .parents__link svg { transition: none; }
}

/* ------------------------------------------------------------
 * PROMO RIBBON — first pixel of energy (Revolut-style)
 * ------------------------------------------------------------ */
/* PROMO RIBBON — RETIRED 5 Aug 2026. Rebecca: "lets remove the thin banner from
   the top of the pages", removed from all 18 site pages. The rules kept on
   purpose so it can be restored by pasting six lines of markup back above the
   nav; it was a 39px strip and nothing was positioned against its height (the
   nav is sticky at top:0), so nothing else needed changing.
   ⚠ If it comes back, --hero-h on body.home must go from 100svh-45px to
   100svh-84px, or the home hero will overhang the fold by 39px too much.
   ⚠ The design rules in CLAUDE.md still list the promo ribbon as a sanctioned
   component — worth striking out if it is gone for good.
   The markup was:
     <a class="rb" href="founders-club.html">
       <span class="rb__dot" aria-hidden="true"></span>
       <span><span class="rb__strong">Halve it:</span> free until your current
         contracts end &middot; then save at least 50% on what you pay today</span>
       <span class="rb__arrow" aria-hidden="true">&rarr;</span>
     </a>
   ------------------------------------------------------------ */
.rb {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 9px 16px;
  background: var(--ge-blue, #1B367E);
  color: #fff;
  font-size: 14px;
  letter-spacing: 0.01em;
  text-decoration: none;
}
.rb:hover .rb__arrow { transform: translateX(4px); }
.rb__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ge-green-3, #8ACB55);
  animation: rb-pulse 2s infinite;
  flex: none;
}
@keyframes rb-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(138,203,85,0.55); }
  70%  { box-shadow: 0 0 0 8px rgba(138,203,85,0); }
  100% { box-shadow: 0 0 0 0 rgba(138,203,85,0); }
}
.rb__strong { font-weight: 700; }
.rb__arrow { display: inline-block; transition: transform 200ms var(--hv2-ease); }

/* ------------------------------------------------------------
 * NAV — Monzo layout: logo · audience pill · dark CTA
 * ------------------------------------------------------------ */
.nv {
  position: sticky;
  top: 0;
  z-index: 60;
  /* solid chrome per design system v2 — frosted surfaces live on photography only */
  background: #fff;
  border-bottom: 1px solid rgba(15,20,56,0.06);
}
.nv__inner {
  display: flex;
  align-items: center;
  gap: var(--sp-5, 20px);
  /* no max-width cap — the nav grows full width and its side inset matches
     the hero tile's margin (clamp 10–22px) so the logo/CTA line up with the
     hero edges on big monitors instead of stopping at 1640px */
  max-width: none;
  margin: 0 auto;
  padding: 14px clamp(10px, 1.6vw, 22px);
}
.nv__logo { display: inline-flex; align-items: center; }
.nv__logo img { width: clamp(132px, 11vw, 168px); height: auto; display: block; }

/* Nav lines up with the hero card's edges (Rebecca, 29 Jul: "amend the nav bar
   to have the same margins"). Reads the same --hero-frame-w token as every
   hero card, so nav and hero can never drift apart. Scoped with :has() to
   pages that actually HAVE a hero card — utility pages (Privacy, Terms, Blog
   article, campaign pages) have no hero frame to line up with, so they keep
   the nav's own full-width gutters.
   .dm-hero__frame is in the list because Book a Demo's hero is its own navy
   card, not a photo frame (29 Jul) — it sits at --hero-frame-w too. */
body:has(.hero2__frame, .pv3g__frame, .ph-hero__frame, .arthero, .dm-hero__frame) .nv__inner {
  width: var(--hero-frame-w);
  margin-inline: auto;
  padding-inline: 0;
}

/* audience switcher pill */
.nv__switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  /* One token for the track's inset, because the sliding indicator has to match
     it exactly on all four sides. 4px -> 6px on 3 Aug ("the parent button is too
     close to the edge"); the indicator was left at 4px, which then made the gap
     round it uneven — 10px at the left against 4px top and bottom ("i dont like
     how the selected button doesnt fill the grey pill equally"). Both now read
     this variable, so they cannot drift apart again. */
  --nv-pad: 6px;
  padding: var(--nv-pad);
  border-radius: 999px;
  background: #EEF2F6;
}
/* Sliding indicator (Rebecca, 28 Jul: "the nav pill sliding between segments
   rather than snapping"). Absolutely positioned under the segment labels and
   moved with a transform by nv-pill.js's movePill() — .nv__seg no longer
   paints its own background/shadow when active, so there is only ever one
   white pill in the row, and it glides to the new segment's rect instead of
   two backgrounds swapping instantly. */
.nv__pill {
  position: absolute;
  /* left: 0 — the JS translates by the segment's offsetLeft, which ALREADY
     includes the track's padding. The old left: 4px was added on top of that, so
     the indicator sat 10px in from the left while its top and bottom insets were
     4px. Vertical inset now comes from the same --nv-pad. */
  top: var(--nv-pad, 6px);
  left: 0;
  height: calc(100% - 2 * var(--nv-pad, 6px));
  width: 0;
  border-radius: 999px;
  background: #fff;
  box-shadow: 0 2px 8px rgba(15,20,56,0.10);
  opacity: 0;
  transition: transform 380ms var(--hv2-ease), width 380ms var(--hv2-ease), opacity 160ms var(--hv2-ease);
  pointer-events: none;
}
.nv__pill.is-ready { opacity: 1; }
.nv__seg {
  position: relative;
  z-index: 1;
  appearance: none;
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: 15px;
  color: var(--ge-navy, #0F1438);
  background: transparent;
  border-radius: 999px;
  padding: 9px 18px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  transition: background 200ms var(--hv2-ease);
  white-space: nowrap;
}
.nv__seg svg { width: 15px; height: 15px; opacity: 0.75; }
.nv__seg .nv__chev { width: 12px; height: 12px; transition: transform 200ms var(--hv2-ease); }
.nv__seg.is-active { font-weight: 700; }
.nv--open .nv__seg.is-active .nv__chev { transform: rotate(180deg); }
.nv__seg:hover:not(.is-active) { background: rgba(255,255,255,0.6); }
.nv__seg:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(47,183,241,0.45); }

.nv__spacer { flex: 1; }

.nv__cta.btn {
  background: var(--ge-navy, #0F1438);
  color: #fff;
  border: none;
}
.nv__cta.btn:hover { background: var(--ge-blue, #1B367E); color: #fff; }
.nv__cta .btn__arrow { background: rgba(255,255,255,0.16); color: #fff; }

/* plain text link, next to the Founders Club pill (Rebecca, 29 Jul) */
.nv__about {
  color: var(--ge-navy, #0F1438);
  font-size: 15px;
  /* 4 Aug: was 500 — the only weight-500 in the LIVE type system, on 18 of 20
     pages. The scale is 400 body / 600 headings-and-UI / 700-800 big numbers.
     (.casc__chip further down is also 500 but .casc is dead CSS — the rejected
     cascade component, not rendered on any page.) */
  font-weight: 600;
  white-space: nowrap;
  transition: color 200ms var(--hv2-ease);
}
.nv__about:hover { color: var(--ge-blue, #1B367E); }
@media (max-width: 900px) { .nv__about { display: none; } }

/* founders club — ghost pill beside the CTA */
.nv__founders {
  display: inline-flex;
  align-items: center;
  gap: 9px;
}
@media (max-width: 900px) { .nv__founders { display: none; } }

/* hamburger (mobile) */
.nv__burger {
  display: none;
  appearance: none;
  border: 1px solid #E6ECF4;
  background: #fff;
  border-radius: 12px;
  width: 44px;
  height: 44px;
  cursor: pointer;
  place-items: center;
}
.nv__burger svg { width: 20px; height: 20px; }

/* ------------------------------------------------------------
 * MEGA-MENU — a mini-bento, not a grey list
 * ------------------------------------------------------------ */
.mm {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  background: #fff;
  border-bottom: 1px solid rgba(15,20,56,0.08);
  box-shadow: 0 32px 64px rgba(15,20,56,0.14);
  display: none;
}
.nv--open .mm { display: block; }
.mm__inner {
  max-width: 1640px;
  margin: 0 auto;
  padding: clamp(20px, 3vw, 36px) clamp(16px, 2.5vw, 32px);
  display: none; /* panels: only the active segment's grid shows */
  grid-template-columns: 1fr;   /* tiles-only panels now span the full width */
  gap: clamp(16px, 2vw, 24px);
}
.mm__inner.is-on { display: grid; }
.mm__list { display: flex; flex-direction: column; gap: 2px; }
.mm__list a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 16px;
  border-radius: 14px;
  text-decoration: none;
  color: var(--ge-navy, #0F1438);
  font-size: 16px;
  font-weight: 700;
  transition: background 160ms var(--hv2-ease);
}
.mm__list a svg { width: 15px; height: 15px; opacity: 0; transform: translateX(-4px); transition: opacity 160ms, transform 160ms var(--hv2-ease); }
.mm__list a:hover { background: #F3F7FA; }
.mm__list a:hover svg { opacity: 0.8; transform: translateX(0); }

.mm__tiles {
  display: grid;
  /* 5 -> 4 tiles when Sync was removed (29 Jul). auto-fit rather than a fixed
     count so the row always fills — a hard repeat(N) left an empty column. */
  grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
  grid-template-rows: auto auto;
  gap: 14px;
}
.mm__tile {
  border-radius: 20px;
  padding: 18px;
  min-height: 128px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 10px;
  text-decoration: none;
  color: #fff;
  position: relative;
  overflow: hidden;
  /* staggered pop-in, driven by .nv--open */
  opacity: 0;
  transform: translateY(10px);
}
.nv--open .mm__tile { animation: mm-in 360ms var(--hv2-ease) forwards; }
.nv--open .mm__tile:nth-child(1) { animation-delay: 40ms; }
.nv--open .mm__tile:nth-child(2) { animation-delay: 90ms; }
.nv--open .mm__tile:nth-child(3) { animation-delay: 140ms; }
.nv--open .mm__tile:nth-child(4) { animation-delay: 190ms; }
.nv--open .mm__tile:nth-child(5) { animation-delay: 240ms; }
/* Products panel — Rebecca, 3 Aug: "products doesn't need an animation
   in the nav." Tiles just show immediately; School types and Parents
   keep the staggered pop-in. */
[data-mm-panel-id="products"] .mm__tile { opacity: 1; transform: none; }
.nv--open [data-mm-panel-id="products"] .mm__tile { animation: none; }
@keyframes mm-in { to { opacity: 1; transform: translateY(0); } }

.mm__tile-logo { height: 34px; display: flex; }
.mm__tile-logo img { height: 100%; width: auto; }
.mm__tile-name { display: block; font-weight: 700; font-size: 16px; }
.mm__tile-sub { display: block; font-size: 13.5px; opacity: 0.85; line-height: 1.35; margin-top: 3px; }
/* Products tiles show the product LOGO (which is the wordmark), so the text
   name underneath is redundant — hide it, keep logo + one-line description */
[data-mm-panel-id="products"] .mm__tile-name { display: none; }
/* Parent & Carer uses the full logo lockup (icon + wordmark) like the other
   four, so its text name is redundant too — it inherits the hide rule above.
   The lockup is much wider than the other wordmarks (~7.6:1), so cap its
   WIDTH rather than its height or it overflows the tile. (Rebecca, 24 Jul) */
[data-mm-panel-id="products"] .mm__tile--parentapp .mm__tile-logo { height: auto; }
/* 221px wide = 34px tall at the real logo's 6.5:1 ratio, so it matches the
   other four tiles' 34px logo height exactly (Rebecca supplied proper SVGs on
   27 Jul, replacing the PNG I'd generated at a wider 7.65:1) */
[data-mm-panel-id="products"] .mm__tile--parentapp .mm__tile-logo img { height: auto; width: 100%; max-width: 221px; }
.mm__tile--photo .mm__tile-name { font-size: 17px; }
.mm__tile--one       { background: #1B88C8; }
.mm__tile--onsite    { background: var(--ge-violet-4, #614DBA); }
.mm__tile--classroom { background: #2248C0; }
.mm__tile--sync      { background: #1B88C8; }
/* Parent app is back on ONE's teal (Rebecca, 29 Jul: "revert the parent app
   page and nav tile to the same colour as the one product"). This reverses her
   24 Jul call, which moved it to pink "to stay distinct from One" — so the two
   tiles in the Products panel now read as the same product family again. Her
   call; flagged at the time of the change. */
/* deep sky, matching the rest of Parent & Carer (Rebecca, 3 Aug) — was
   One's teal, which made the same product two different colours */
/* 3 Aug: deep sky INK (#116490), not the bright #1B88C8 it had before
   ("the parent app tile in the nav needs its colour changing to match the
   parent page"). The Parent App page leads with --pp-ink everywhere you can
   see it — hero frame, logo chip, accent band — because the bright tone fails
   AA behind white text; the nav tile was the one place still showing the
   bright version, so the two read as different blues. White logo on #116490
   is 6.5:1. NB One has the same bright-vs-ink relationship and is deliberately
   left on its BRIGHT tone here — flagged to her, undecided.
   3 Aug, later: One and the app swapped palettes, so this tile is now teal ink
   #00736B and One's is the bright deep sky. */
.mm__tile--parentapp { background: #00736B; }
.mm__tiles--three { grid-template-columns: repeat(3, 1fr); }
.mm__tile--navy    { background: var(--ge-navy, #0F1438); }
.mm__tile--navy.mm__apptile { background: #00B1A7; }  /* Parents-panel app tile — matches the Products one above */
/* Parents-panel apptile FEATURED (24 Jul): richer benefit line + explicit
   "Explore the app" link since there's now a full page behind it — but
   side-by-side with the help-centre tile, same horizontal layout as the
   Products row (Rebecca: "I don't want the tiles stacked like this"). */
.mm__apptile-cta {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13.5px; font-weight: 700; color: #fff;
}
.mm__apptile-cta svg { width: 14px; height: 14px; transition: transform 200ms var(--hv2-ease); }
.mm__apptile:hover .mm__apptile-cta svg { transform: translateX(3px); }
/* ⚠ #2248C0, matching .mm__tile--classroom above — 7 Aug 2026, at her request:
   "can we make the primary school page nav tile the same colour as the classroom
   one". Was --ge-blue #1B367E, which sat too close to the navy Secondary tile
   directly beside it in the same panel.
   .mm__tile--blue is used for exactly ONE link site-wide — Primary Schools, on
   all 25 pages — so changing the class is safe and needs no new modifier. If it
   is ever reused elsewhere, give Primary its own class instead. */
.mm__tile--blue    { background: #2248C0; }
.mm__tile--skytint { background: var(--ge-blue-3, #D9F4FF); color: var(--ge-navy, #0F1438); }
/* 6 Aug 2026: "can we change LAS nav tile to the onsite violet". LAs & MATs used
   to share .mm__tile--skytint with the Parent help centre tile — this is its own
   class precisely so the help-centre tile is left alone.
   ⚠ The hero on local-authorities-and-trusts.html follows this colour (see
   .st--las .hero2--tile in school-types.css). The whole point of that hero
   treatment is that the tile you click and the page you land on match, so if this
   value ever changes, change it there too. */
.mm__tile--violet { background: var(--ge-violet-4, #614DBA); color: #fff; }
.mm__tile--skytint .mm__tile-sub { opacity: 0.72; }

/* The LAs & MATs nav tile is PINK, matching its hero (Rebecca, 29 Jul: "make
   the las tile in the nav bar the pink colour too"). Same #A24F81 as the hero.

   MUST stay scoped to the types panel: .mm__tile--skytint is also worn by the
   Parent Help Centre tile in the Parents panel, which stays pale sky. Editing
   the bare class would have recoloured both.

   It flips from a light tile to a solid one, so the sub-line goes back to the
   0.85 the other two solid tiles use (the 0.72 above is tuned for dark text on
   pale sky), and the hover rule further down — which holds skytint text navy —
   has to be overridden or the label would go navy-on-pink on hover. */
[data-mm-panel-id="types"] .mm__tile--skytint,
[data-mm-panel-id="types"] .mm__tile--skytint:hover { background: #A24F81; color: #fff; }
[data-mm-panel-id="types"] .mm__tile--skytint .mm__tile-sub { opacity: 0.85; }
.mm__tile-ico {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 10px;
  background: rgba(255,255,255,0.14);
}
.mm__tile--skytint .mm__tile-ico { background: #fff; color: var(--ge-blue, #1B367E); }
.mm__tile-ico svg { width: 18px; height: 18px; }

/* School-types tiles (Primary / Secondary / LAs): drop the small badge and let
   a LARGE icon peek in from the top-right corner, clipped by the tile, sitting
   tonally behind the text — more playful than a boxed icon (23 July 2026) */
[data-mm-panel-id="types"] .mm__tile { justify-content: flex-end; }
[data-mm-panel-id="types"] .mm__tile-ico { display: none; }   /* drop the house/book/building icons */
/* The big faint GroupEd hexagon that used to peek off each tile's top-right
   corner was REMOVED on 29 Jul ("remove ghost icons from all nav tiles").
   The Parent app tile's equivalent (.mm__apptile-wm) went at the same time,
   markup and all, so no nav tile carries a watermark now. Tiles are text only. */
[data-mm-panel-id="types"] .mm__tile > span:last-child { position: relative; z-index: 1; }
/* Parents panel — drop the small icon badges (the text carries the tiles) */
[data-mm-panel-id="parents"] .mm__tile-ico { display: none; }
/* Parents panel — two wide tiles (navy app tile + help-centre tile).
   The App Store / Google Play badges sit in the LINK COLUMN under
   'Contact support' (.mm__liststores). The badges reuse the footer's
   .fxb__store markup, restyled solid navy for the white panel. */
.mm__tiles--parents { grid-template-columns: 1fr 1fr; grid-template-rows: auto; }
/* The navy app tile's big ghost symbol was removed on 29 Jul ("remove ghost
   icons from all nav tiles") — the .mm__apptile-wm span is gone from all 20
   pages, so its rules are gone too. */
.mm__apptile > span { position: relative; z-index: 1; }
.mm__liststores { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }
/* the .mm__list a rules (navy text, hidden 15px arrow svg, space-between)
   would bleed into the badges, so re-assert the badge look at higher
   specificity */
.mm__liststores .fxb__store {
  background: var(--ge-navy, #0F1438);
  border-color: var(--ge-navy, #0F1438);
  color: #fff;
  justify-content: flex-start;
  gap: 9px;
  padding: 9px 16px 9px 12px;
  font-size: 15px;
  font-weight: 400;
}
.mm__liststores .fxb__store svg { width: 20px; height: 20px; opacity: 1; transform: none; }
.mm__liststores .fxb__store:hover { background: var(--ge-blue, #1B367E); color: #fff; }
.mm__tile--photo {
  grid-column: 1 / -1;
  background: var(--ge-navy, #0F1438);
  flex-direction: row;
  align-items: center;
  min-height: 88px;
}
.mm__tile--photo img.mm__tile-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.38;
}
.mm__tile--photo > * { position: relative; }
.mm__tile:hover { filter: brightness(1.08); }
/* keep tile text legible on hover — foundation's global a:hover recolours
   links to navy, which would turn the white tile text navy-on-navy */
.mm__tile:hover { color: #fff; }
.mm__tile--skytint:hover { color: var(--ge-navy, #0F1438); }
.mm__tile:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(47,183,241,0.45); }

/* ------------------------------------------------------------
 * HERO — full-screen photo, the school day comes alive
 * ------------------------------------------------------------ */

/* SHARED HERO GEOMETRY (Rebecca, 29 Jul: "make sure all heros follow the same
   margins as the home page"). One source of truth for every hero card on the
   site — .hero2__frame (home, About, Blog, Careers, Book a Demo, Founders,
   school types, Martyn's Law) and .pv3g__frame (the product pages).
   Before this, each frame ran to `100% - 2 * gutter` with no ceiling, so on a
   wide monitor the cards sprawled; the home hero was capped at 1500px first
   (measured against monzo.com: their card is 1440px on a 1920px window) and
   the rest are now matched to it.
   --hero-inset is the padding that holds a hero's CONTENT to
   --hero-content-cap inside the card. It has to derive from --hero-frame-w:
   the per-variant rules in hero-cutout.css / product-v3.css / martyns-v2.css
   used to compute it from the raw viewport width, which was correct only while
   the card itself was full-width — with a capped card that maths over-pads and
   squeezes the content (280px a side instead of 70px at 1920px). Deriving both
   from the same token means they cannot drift apart again. */
:root {
  --hero-gutter: clamp(10px, 1.6vw, 22px);   /* card edge -> window edge */
  --hero-cap: 1500px;                        /* widest the card ever gets */
  --hero-content-cap: 1360px;                /* widest the content inside it gets */
  --hero-frame-w: min(var(--hero-cap), calc(100% - 2 * var(--hero-gutter)));
  --hero-inset: max(0px, calc((var(--hero-frame-w) - var(--hero-content-cap)) / 2));
  /* HEIGHT, shared by every hero (Rebecca, 29 Jul: "max them to be the same as
     the home page"). This was the home page's own value; the other heroes ran
     to an uncapped max(480px, 66svh), so on a tall monitor they grew past the
     home card. Now all five variants read this one token.
     It stays a MINIMUM, so a hero whose content needs more room still grows —
     the 740px is a floor ceiling, not a clamp on the box. */
  --hero-h: max(480px, min(72svh, 740px));
}

.hero2 { padding-top: clamp(10px, 1.4vw, 18px); }
.hero2__frame {
  position: relative;
  overflow: hidden;
  width: var(--hero-frame-w);
  margin-inline: auto;
  border-radius: clamp(20px, 2.5vw, 34px);
  /* was max(580px, calc(100svh - 150px)) — every other hero2 variant
     (--cutout, --photo, --ml) already overrides this down to max(480px,
     66svh), which is why only the home page (the one hero2 that has no such
     override) filled almost the entire window: at a common 900px-tall
     browser, the old formula left just 14px of the next section visible, and
     below ~800px tall it overflowed the viewport outright (measured -102px
     at 700px). Matching everything else fixes that (Rebecca, 28 Jul: "we
     need to have content thats below the hero peaking through so people
     know to scroll"). */
  min-height: max(480px, 66svh);
  display: flex;
  align-items: flex-end;
  background: var(--ge-navy, #0F1438);
}
.hero2__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 40%;
}
.hero2__scrim {
  position: absolute;
  inset: 0;
  background: rgba(15, 20, 56, 0.46);   /* flat navy wash for text legibility — no gradient */
}
/* oversized ghost hexagon for plain-colour heroes — a big faint GroupEd mark
   bleeding off the top-right corner, matching the product-page hero
   (.pv3g__wm). Only renders where a .hero2__wm element is present. Sits at
   z-index 0, behind the copy (z-index 2). (Rebecca, 23 Jul) */
.hero2__wm {
  position: absolute;
  z-index: 0;
  top: clamp(-200px, -14vw, -90px);
  right: clamp(-220px, -14vw, -110px);
  width: clamp(500px, 70vw, 1120px);
  height: auto;
  opacity: 0.11;
  pointer-events: none;
}
.hero2__copy {
  position: relative;
  z-index: 2;
  max-width: 60rem;
  padding: clamp(28px, 4.5vw, 60px);
  padding-bottom: clamp(120px, 17vh, 175px); /* clears the hanging proof cards */
}
/* Match the product-page hero inset on every page EXCEPT the home page
   (Rebecca, 27 Jul: "make sure all web pages have the same product margins
   except for the home page"). The outer frame margin already matched
   everywhere — clamp(10px,1.6vw,22px) — but hero2's copy sat 60px in while a
   product hero sits 89px in at 1440px.
   Scoped to the --cutout and --ml variants: the home page is the only hero2
   that uses --graphic, so it is provably untouched.

   The 1360px wide-screen cap lives on the FRAME (see hero-cutout.css), NOT
   here. Putting it on this element was a real bug: the cap grows with the
   viewport, and because it shared an element with `max-width`, the padding ate
   the box — 750px of text at 1440px, 256px at 1920px and ZERO at 2560px, which
   shredded the headline one word per line on a monitor. */
/* Every hero variant's copy stack (heading, lede, CTA — and now the dots) gets
   real space between its children via flex + gap. Before this, .hero2__copy
   was plain block flow with every child at margin: 0, so on the home page's
   --graphic variant (the only one this rule didn't reach) the CTA button sat
   flush against the lede with a literal 0px gap — the "squished" button
   Rebecca flagged (28 Jul). --cutout/--ml already looked right because they
   picked up this treatment; --graphic just needed adding to the selector. */
.hero2--cutout .hero2__copy,
.hero2--ml .hero2__copy,
.hero2--graphic .hero2__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-5, 20px);
  /* Home only: drop the big reserved bottom padding (Rebecca, 29 Jul: "i dont
     like how the image in the hero has grown"). The base .hero2__copy reserves
     clamp(120px,17vh,175px) to clear the proof cards that hang over the hero —
     but on THIS page those cards now straddle the statement band below instead,
     so that space became empty and the hero read ~100px taller than before. The
     frame's height is driven by this copy block, not by its min-height, so this
     padding is the only lever that changes it. Value chosen to land the frame
     back on the height it visually had when the cards covered its bottom 84px.
     Still scoped to --graphic because martyns-law DOES still hang a proofrow
     over its hero and must keep the larger reserve. */
  padding-bottom: clamp(48px, 8vh, 88px);
}
/* the wide-screen inset is a --cutout/--ml-only look (Rebecca, 27 Jul: "make
   sure all web pages have the same product margins except for the home
   page") — kept split out so --graphic keeps its own base padding above */
.hero2--cutout .hero2__copy,
.hero2--ml .hero2__copy {
  padding-inline: clamp(20px, 3.4vw, 56px);
}
/* .hero2__h1 is a class-based twin of the h1 selector — the hero carousel
   (Rebecca, 28 Jul) needs three slide headlines in the DOM at once for the
   crossfade, but only the first/default slide may be a real <h1> (three
   <h1>s is invalid heading structure); slides 2 and 3 use a <p class="hero2__h1">
   that picks up identical styling via this selector. Needs its own
   font-weight: 600 here — .hv2 h1's weight rule only matches a real <h1>
   element, so slides 2/3 were silently falling back to the body's 400
   weight (Rebecca, 28 Jul: caught the two slides looking lighter than the
   first, brand system stays at SemiBold 600 for every heading). */
.hero2__copy h1,
.hero2__copy .hero2__h1 {
  margin: 0;
  color: #fff;
  font-size: clamp(40px, 5.6vw, 82px);
  line-height: 1.14;
  font-weight: 600;
}
.hero2__copy h1 .mark,
.hero2__copy .hero2__h1 .mark,
/* Home's final CTA heading (3 Aug, the Halve It rewrite) — .mark had no
   styling at all outside hero2__copy/.statement__line/.proofstat__line, so
   "halve it." rendered as plain white text with no pill until this was
   added. Same visual treatment as the hero mark, just a different scope. */
.final2__inner h2 .mark {
  /* inline-block; symmetric vertical padding + line-height 1 optically
     centres the word in the pill (Outfit's tall ascent otherwise leaves
     a big gap above and crowds the descender at the bottom) */
  display: inline-block;
  line-height: 1;
  background: var(--ge-blue-3, #D9F4FF);
  color: var(--ge-blue, #1B367E);
  border-radius: 0.24em;
  padding: 0.1em 0.18em 0.16em;
  /* vertical margins grow the pill's own line box, so letters on the lines
     above/below never touch the pill (Rebecca, 23 Jul — site-wide rule) */
  margin-block: 0.14em 0.08em;
}
/* ------------------------------------------------------------
 * HERO CAROUSEL — headline + lede rotate; photo/chips/CTA stay put
 * (Rebecca, 28 Jul). Slides stack on the same grid cell so the tallest
 * one sets the height — no jump as a 2-line headline swaps for a
 * 3-line one — and crossfade with a small rise, matching the product
 * switcher's panel transition. Auto-advances, pauses on hover/focus,
 * and is fully static under prefers-reduced-motion.
 * ------------------------------------------------------------ */
.hero2__slides { display: grid; }
.hero2__slide {
  grid-area: 1 / 1;
  display: flex;
  flex-direction: column;
  gap: var(--sp-5, 20px);
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  transition: opacity 420ms var(--hv2-ease), visibility 420ms, transform 420ms var(--hv2-ease);
  pointer-events: none;
}
.hero2__slide.is-on { opacity: 1; visibility: visible; transform: none; pointer-events: auto; }
/* home page, 5 Aug: the carousel is gone, so the single slide has nothing to
   crossfade with and should not animate on load. */
.hero2__slides--single .hero2__slide { transition: none; }
@media (prefers-reduced-motion: reduce) { .hero2__slide { transform: none; } }

.hero2__dots { display: flex; gap: 8px; margin-top: 2px; }
.hero2__dot {
  position: relative;                /* anchors the invisible hit area below */
  width: 9px; height: 9px; border-radius: 999px; cursor: pointer; padding: 0;
  border: none; background: rgba(255,255,255,0.4);
  transition: background 200ms var(--hv2-ease), width 200ms var(--hv2-ease);
}
.hero2__dot.is-on { background: #fff; width: 22px; }
.hero2__dot:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(47,183,241,0.55); }

.hero2__lede {
  margin: 0;
  max-width: 36rem;
  color: rgba(255,255,255,0.9);
  font-size: clamp(16px, 1.3vw, 20px);
  line-height: 1.55;
}
.hero2__cta.btn {
  background: #fff;
  color: var(--ge-navy, #0F1438);
  border: none;
  font-weight: 700;
}
.hero2__cta.btn:hover { background: #EAF1F8; color: var(--ge-navy, #0F1438); }
.hero2__cta .btn__arrow { background: var(--ge-navy, #0F1438); color: #fff; }

/* ---------- cutout variant: person + giant brand mark on navy ---------- */
.hero2--cutout .hero2__frame { background: var(--ge-navy, #0F1438); }
.hero2--cutout .hero2__scrim { display: none; }
.hero2--cutout .hero2__copy { max-width: 44rem; }
.hero2__mark {
  position: absolute;
  right: -10%;
  top: -18%;
  width: 68%;
  opacity: 0.08;
  pointer-events: none;
}
.hero2__mark img { width: 100%; height: auto; display: block; }
.hero2__cutout {
  position: absolute;
  right: clamp(12px, 4vw, 72px);
  bottom: 0;
  height: 86%;
  max-width: 54%;
  object-fit: contain;
  object-position: bottom;
  filter: drop-shadow(-24px 28px 60px rgba(0,0,0,0.4));
}
.hero2--cutout .hero2__chip--1 { right: 34%; top: 13%; }
.hero2--cutout .hero2__chip--2 { right: 4%;  top: 34%; }
.hero2--cutout .hero2__chip--3 { right: 38%; top: 66%; }

/* the living chips — a school morning happening on the photo */
.hero2__chip {
  position: absolute;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 11px;
  padding: 15px 24px;
  border-radius: 999px;
  background: var(--ge-sky, #2FB7F1);
  color: var(--ge-navy, #0F1438);
  font-size: 17px;
  font-weight: 700;
  box-shadow: 0 16px 40px rgba(15,20,56,0.35);
  opacity: 0;
  transform: translateY(16px) scale(0.96);
  animation: chip-in 560ms var(--hv2-ease) forwards;
}
.hero2__chip svg { width: 19px; height: 19px; color: var(--ge-navy, #0F1438); flex: none; }
.hero2__chip time { font-weight: 800; }
/* positioned in the clear zones around the teacher — never on her face */
.hero2__chip--1 { right: 12%; top: 14%; animation-delay: 700ms; background: var(--ge-sky, #2FB7F1); }
.hero2__chip--2 { right: 3%;  top: 42%; animation-delay: 1500ms; background: var(--ge-green-3, #8ACB55); }
.hero2__chip--3 { right: 21%; top: 70%; animation-delay: 2300ms; background: var(--ge-yellow-3, #E8BA19); }

/* MONZO-SIZED HERO (Rebecca, 29 Jul, from a monzo.com screenshot on her
   monitor): the hero should read as a contained card, "rather than spanning
   the majority of the page". The base rule's 66svh keeps GROWING on tall
   monitors (a ~1350px-tall viewport gave a ~890px hero) — Monzo's card stops
   at a fixed pixel height no matter the monitor. Same treatment: grow with
   the viewport up to a hard cap. Scoped to --graphic (home is the only
   hero2--graphic page). */
/* measured on monzo.com at 1920x1050: their card is 1440x750 (75% of the
   window wide, 71% tall, 240px white either side). 72svh capped at 740px
   lands ours on the same physical size on her monitor.
   HEIGHT only — the width cap that used to sit here is now the site-wide
   --hero-cap token above, shared by every hero. Other heroes still run to
   max(480px, 66svh) with no ceiling; only the home card is height-capped. */
.hero2--graphic .hero2__frame { min-height: var(--hero-h); }

/* ACCREDITATION TILES (Rebecca, 29 Jul): the three accreditations in white
   tiles floating on the hero photo, bottom-right — exactly where Monzo docks
   its Switch Guarantee / FSCS badges. Opaque white (frosted chrome is for
   photography-only CHIP furniture; these are logo tiles), 14px radius per the
   icon-tile scale. Hidden below 960px alongside the chips — on small screens
   the copy owns the tile. */
.hero2__badges {
  position: absolute;
  z-index: 2;
  right: clamp(16px, 2vw, 32px);
  bottom: clamp(16px, 2vw, 32px);
  display: flex;
  align-items: stretch;
  gap: 10px;
}
.hero2__badge {
  display: grid;
  place-items: center;
  padding: 10px 16px;
  background: #fff;
  border-radius: 14px;
  box-shadow: 0 8px 24px rgba(15, 20, 56, 0.18);
}
.hero2__badge img { height: clamp(34px, 3vw, 44px); width: auto; display: block; }
@media (max-width: 960px) {
  .hero2__badges { display: none; }
}
@keyframes chip-in { to { opacity: 1; transform: translateY(0) scale(1); } }


/* ------------------------------------------------------------
 * SEAM 1 — proof cards straddling hero → violet band
 * ------------------------------------------------------------ */
.band--sky {
  background: var(--ge-blue-3, #D9F4FF);
  padding-bottom: clamp(72px, 9vw, 130px);
  /* padding-top:1px (not 0) is load-bearing, not decorative — .proofstat's
     own 48-80px margin-top was collapsing straight through this section
     (no padding/border here to stop it), which pushed .band--sky's own
     rendered top edge down by that same 48-80px. That silently ate the
     .seam-curve overlap coming from .statement above it — measured a 24px
     GAP at this seam instead of the intended overlap once .seam-curve
     rolled out here (Rebecca, 29 Jul: "apply more on home screen"). Any
     non-zero padding-top blocks the collapse; 1px is visually nothing. */
  padding-top: 1px;
}
.proofrow {
  position: relative;
  z-index: 3;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 2vw, 24px);
  margin-top: clamp(-84px, -6vw, -58px);
  padding-top: 0;
}
.proof {
  display: flex;
  align-items: center;
  gap: var(--sp-4, 16px);
  background: #fff;
  border-radius: var(--tile-radius);
  box-shadow: 0 18px 44px rgba(15,20,56,0.14);
  padding: var(--sp-6, 24px);
  text-decoration: none;
  color: inherit;
  transition: transform 200ms var(--hv2-ease), box-shadow 200ms var(--hv2-ease);
}
a.proof:hover { transform: translateY(-4px); box-shadow: 0 26px 56px rgba(15,20,56,0.18); }
.proof__icon {
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 13px;
  background: var(--ge-blue-3, #D9F4FF);
  color: var(--ge-blue, #1B367E);
  display: grid;
  place-items: center;
}
.proof__icon svg { width: 22px; height: 22px; }
.proof__big {
  margin: 0;
  font-size: clamp(19px, 1.6vw, 24px);
  font-weight: 700;
  color: var(--ge-navy, #0F1438);
  line-height: 1.15;
}
.proof__sub { margin: 2px 0 0; font-size: 14.5px; color: #4A5876; }

/* ------------------------------------------------------------
 * ODOMETER — numbers that physically roll
 * ------------------------------------------------------------ */
.odo {
  display: inline-flex;
  align-items: baseline;
  font-variant-numeric: tabular-nums;
  /* headings carry -0.02em letter-spacing which squeezes the fixed-width
     overflow:hidden digit boxes and clips the last glyph (e.g. "100+" /
     "70%+"). Reset it on every odometer so digits never get cut off. */
  letter-spacing: normal;
}
.odo__digit {
  display: inline-block;
  overflow: hidden;
  height: 1.06em;
  line-height: 1.06;
}
.odo__col {
  display: flex;
  flex-direction: column;
  transform: translateY(0);
  transition: transform 1100ms var(--hv2-ease);
}
.odo__col span { height: 1.06em; }

/* ------------------------------------------------------------
 * PILLARS — bento on the violet band
 * ------------------------------------------------------------ */
.bandhead {
  display: flex;
  flex-direction: column;          /* copy sits under the heading, not beside it */
  align-items: flex-start;
  gap: 12px;
  padding-top: clamp(80px, 9vw, 140px);
  margin-bottom: clamp(44px, 5vw, 76px);
}
/* exception: when the heading is paired with a short action link (not copy),
   keep them on one line — e.g. "News and insights." + "All articles" */
.band--blog .bandhead {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
}
.bandhead h2 { margin: 0; max-width: 24ch; color: var(--ge-navy, #0F1438); }
.bandhead p { margin: 0; max-width: 52ch; color: #4A5876; font-size: 16px; }
.bandhead__more {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 15.5px; font-weight: 700;
  color: var(--ge-blue, #1B367E); text-decoration: none;
}
.bandhead__more:hover { color: var(--ge-navy, #0F1438); }
.bandhead__more svg { width: 16px; height: 16px; transition: transform 200ms var(--hv2-ease); }
.bandhead__more:hover svg { transform: translateX(4px); }
.band--navy .bandhead h2, .band--navy .bandhead p { color: #fff; }
.band--navy .bandhead p { opacity: 0.85; }

.pillars {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(16px, 2vw, 24px);
}
.pillar {
  position: relative;
  overflow: hidden;
  border-radius: 24px;
  padding: clamp(22px, 2.4vw, 32px);
  min-height: 210px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  transition: transform 200ms var(--hv2-ease), box-shadow 200ms var(--hv2-ease);
}
.pillar > * { position: relative; z-index: 1; }
/* the giant ghost icon behind each pillar (reference: watermark tiles) */
.pillar__wm {
  position: absolute;
  z-index: 0;
  right: -8%;
  bottom: -6%;                  /* tucked into the bottom-right corner, cropped by the tile */
  width: 52%;
  aspect-ratio: 1;
  color: var(--ge-navy, #0F1438);
  opacity: 0.05;
  pointer-events: none;
}
.pillar__wm svg { width: 100%; height: 100%; }
.pillar--b .pillar__wm, .pillar--c .pillar__wm { color: #fff; opacity: 0.09; }
.pillar:hover { transform: translateY(-4px); box-shadow: 0 22px 48px rgba(15,20,56,0.14); }
.pillar h3 { margin: 0; font-size: clamp(19px, 1.6vw, 23px); }
.pillar p { margin: 0; font-size: 15px; line-height: 1.5; }
.pillar--a { background: #fff; border: 1px solid #E6ECF4; color: var(--ge-navy, #0F1438); }
.pillar--a p { color: #4A5876; }
.pillar--b { background: var(--ge-navy, #0F1438); color: #fff; }
.pillar--b h3 { color: #fff; }
.pillar--b p { color: rgba(255,255,255,0.82); }
.pillar--c { background: var(--ge-blue, #1B367E); color: #fff; }
.pillar--c h3 { color: #fff; }
.pillar--c p { color: rgba(255,255,255,0.86); }
.pillar--d { background: #fff; border: 1px solid #E6ECF4; color: var(--ge-navy, #0F1438); }
.pillar--d p { color: #4A5876; }

/* SEAM 2 — wide stats card hanging out of the violet band */
.statstrip {
  position: relative;
  z-index: 3;
  margin-top: clamp(12px, 1.6vw, 20px);
  margin-bottom: clamp(-64px, -4.5vw, -44px); /* hangs into the white band below */
  background: #fff;
  border: 1px solid #E6ECF4;
  border-radius: 24px;
  box-shadow: 0 22px 52px rgba(15,20,56,0.14);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-5, 20px);
  padding: clamp(24px, 3vw, 40px);
}
.statstrip__item { text-align: left; padding: 6px 0; }
.statstrip__num {
  margin: 0;
  font-size: clamp(34px, 3.6vw, 56px);
  font-weight: 800;
  color: var(--ge-navy, #0F1438);
  line-height: 1.06;
}
.statstrip__label { margin: 6px 0 0; font-size: 15px; color: #4A5876; }

/* ------------------------------------------------------------
 * PRODUCTS — bento map on white
 * ------------------------------------------------------------ */
.band--white { background: #fff; padding-bottom: clamp(72px, 9vw, 130px); }

/* ------------------------------------------------------------
 * SEAM CURVE — Starling Bank's "peeling corner" (Rebecca, 29 Jul: "i like
 * how sections in starling bank are curved and the next sections colour
 * sits underneath it... they only seem to do it on the bottom of each
 * section. i'd like to try this on the home page").
 *
 * How it works: `.seam-curve` goes on the section IN FRONT (the one whose
 * bottom corners round off). It rounds its own bottom-left/right corners,
 * then pulls its bottom edge UP over the next section by the same amount
 * (negative margin) and sits in a higher stacking layer (position+z-index)
 * so it paints ON TOP of that next section rather than the reverse. Across
 * the flat middle of the seam nothing changes — one opaque section still
 * meets the next. Only at the two bottom corners, where `.seam-curve`'s own
 * background curves away, does the section BEHIND become visible through
 * the gap — which is the section immediately after it in the DOM, so the
 * "reveal" colour is automatically whatever comes next; nothing to keep in
 * sync by hand.
 * The section behind it just needs `.seam` too (position+z-index, no visual
 * change of its own) so it reliably sits behind the curved section even
 * though it comes later in the document (later normally paints on top;
 * this pins the stacking order explicitly instead of relying on default
 * paint order, which is what actually makes the reveal work).
 *
 * ROLLED OUT (Rebecca, 29 Jul: "yes i love it - apply more on home screen")
 * across every home-page seam EXCEPT three that already have their own
 * bespoke straddling element reaching into the same gap — stacking a second
 * overlap mechanism on top of those would fight the first one:
 *   - band--light -> band--blue: .stepscard already straddles down
 *     clamp(-96px,-7vw,-56px) into the blue band.
 *   - band--price -> band--mtx: NO LONGER EXCLUDED (29 Jul). .pricegrid used
 *     to straddle down clamp(-120px,-8vw,-70px), which is why this seam was
 *     left flat — but that straddle was removed (it left the cards with no
 *     padding beneath them), so this section now takes `.seam-curve` like the
 *     rest. Exactly one overlap mechanism per seam, as this note requires.
 *   - final2 -> footer: the footer's .cb--footer contact card already
 *     straddles UP out of the footer by clamp(-290px,-21vw,-190px).
 * Those three sections get `.seam` only (so they can still be curved INTO
 * from above) but not `.seam-curve` (so their own bottom stays flat and the
 * existing straddle is untouched).
 *
 * Every participating section carries a `--sz` custom property, set inline
 * per section in descending order down the page (12 at the top, 1 at the
 * bottom). z-index has to actually decrease monotonically all the way down
 * — a simple alternating 2/1/2/1 does not work, because every section only
 * needs to beat the ONE section directly below it, and with only two
 * values every other pair ties or inverts.
 *
 * ⚠ DO NOT add `.seam` to the section directly before a `.cb--footer`
 * contact-form footer. Found live (Rebecca, 29 Jul: "ive noticed the forms
 * on all the pages arent showing properly") on 9 pages at once: the contact
 * card (`.cb--footer .cb__card`) is plain `position: static` and straddles
 * UP out of the footer purely on DOM paint order (static siblings paint in
 * document order, so the footer's card — later in the DOM — naturally
 * painted over the section before it). `position: relative` ALONE, with
 * no z-index even needed, makes an element paint above all non-positioned
 * content around it — so giving that preceding section `.seam` (which sets
 * exactly that) made its opaque background cover the top half of the card
 * instead. It never needed `.seam` in the first place: the section curving
 * INTO it already carries its own explicit z-index, which is sufficient on
 * its own to paint above any plain static content that follows — the
 * receiving section does not also need position/z-index for that curve to
 * render. Give `.seam` to a "last section before the footer" ONLY on pages
 * with no contact-band footer (confirm first — grep the page for
 * "cb--footer").
 * ------------------------------------------------------------ */
.seam {
  position: relative;
  z-index: var(--sz);
}
/* Rebecca, 3 Aug: "i decided i dont like the round corners on sections"
   — the bottom radius is gone, so section seams are square.
   The negative margin-bottom STAYS on purpose. It is what pulls the next
   section up under this one, and every straddle measurement on the site
   (hanging proof cards, the .stepscard overlap, the crest strip) is
   calibrated against it. Removing it too would shift spacing site-wide;
   keeping it means the ONLY change is rounded corners -> square, with the
   overlap and all spacing exactly as before. */
.seam-curve {
  margin-bottom: clamp(-56px, -4vw, -28px);
}
.band--white .bandhead { padding-top: clamp(130px, 13vw, 190px); } /* room for the hanging stat strip */
/* home products section no longer has a hanging stat strip above it (the numbers
   now live in the proof cascade) — pull its heading back to a normal top padding */
#products.band--white .bandhead { padding-top: clamp(58px, 7vw, 96px); }

/* ------------------------------------------------------------
 * PROOF CASCADE — bold navy "why us" block (replaces .pillars/.statstrip)
 * promises tumble down as bright pills; the proof numbers punch through
 * ------------------------------------------------------------ */
.band--cascade {
  position: relative;
  overflow: hidden;
  background: var(--ge-navy, #0F1438);
  padding-bottom: clamp(80px, 9vw, 140px);
}
.casc { position: relative; z-index: 1; margin-top: clamp(80px, 9vw, 140px); }
.casc__hex {
  position: absolute; right: -3%; bottom: -14%;
  width: clamp(220px, 30%, 400px); opacity: 0.06; z-index: 0; pointer-events: none;
}
.casc__hex img { width: 100%; height: auto; display: block; }
.casc__head { max-width: 42ch; margin-bottom: clamp(44px, 5vw, 76px); }
.casc__eyebrow {
  display: inline-block; margin-bottom: 16px;
  font-size: 13px; font-weight: 600; letter-spacing: 0.1em;
  color: var(--ge-sky, #2FB7F1); background: rgba(47,183,241,0.14);
  border-radius: 999px; padding: 7px 15px;
}
.casc__h {
  margin: 0 0 10px; color: #fff;
  font-size: clamp(28px, 3.6vw, 46px); font-weight: 600; letter-spacing: -0.02em; line-height: 1.08;
}
.casc__sub { margin: 0; color: #B9C4DA; font-size: clamp(16px, 1.6vw, 18px); }

.casc__flow { position: relative; z-index: 1; display: flex; flex-direction: column; gap: clamp(12px, 1.5vw, 18px); }
.casc__row {
  display: flex; align-items: center; flex-wrap: wrap; gap: clamp(10px, 1.1vw, 14px);
  padding-left: calc(var(--i, 0) * clamp(0px, 3.4vw, 54px));
}
.casc__row.rv { transition-delay: calc(var(--i, 0) * 70ms); }

.casc__pill {
  display: inline-flex; align-items: center; white-space: nowrap;
  border-radius: 999px; padding: clamp(9px, 1vw, 13px) clamp(16px, 1.7vw, 24px);
  font-size: clamp(17px, 2vw, 26px); font-weight: 600; letter-spacing: -0.01em;
}
.casc__pill--white { background: #fff; color: var(--ge-navy, #0F1438); }
.casc__pill--sky   { background: var(--ge-sky, #2FB7F1); color: var(--ge-navy, #0F1438); }
.casc__pill--off   { background: var(--ge-off, #F3F7FA); color: var(--ge-navy, #0F1438); }
.casc__pill--out   { background: transparent; border: 2px solid rgba(255,255,255,0.55); color: #fff; }

.casc__chip {
  display: inline-flex; align-items: center;
  border-radius: 999px; padding: 8px 15px;
  font-size: clamp(13px, 1.1vw, 15px); font-weight: 500;
  background: rgba(255,255,255,0.10); color: #E7ECF7;
}

.casc__stat { display: inline-flex; align-items: flex-end; gap: 12px; }
.casc__num {
  color: #fff; font-weight: 800; letter-spacing: -0.04em; line-height: 0.9;
  font-size: clamp(46px, 7vw, 92px);
}
.casc__num--sky { color: var(--ge-sky, #2FB7F1); }
.casc__num .odo { line-height: 0.9; letter-spacing: -0.04em; }
.casc__unit { font-size: 0.32em; font-weight: 700; letter-spacing: 0; vertical-align: super; margin-left: 0.06em; }
.casc__lbl {
  font-size: clamp(12px, 1.1vw, 14px); text-transform: uppercase; letter-spacing: 0.08em;
  font-weight: 600; color: #8FA0C0; line-height: 1.2; padding-bottom: 0.55em;
}

@media (max-width: 720px) {
  .casc__row { padding-left: 0 !important; }
  .casc__num { font-size: clamp(42px, 13vw, 64px); }
  .casc__lbl { padding-bottom: 0.35em; }
}

/* Single centred instance of .glass__media, no copy column — used by the
   PARENTS stat section (3 Aug). */
.pp-media-wrap { display: flex; justify-content: center; margin-top: clamp(28px, 3.4vw, 44px); }

/* ------------------------------------------------------------
 * PROOF ON THE GLASS — warm photo + floating count-up numbers (Option A)
 * light band; a real school photo with proof cards breaking off its edges
 * ------------------------------------------------------------ */
.glass {
  position: relative; z-index: 1;
  margin-top: clamp(40px, 5vw, 72px);
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 4vw, 64px); align-items: center;
}
.glass__copy { min-width: 0; }
.glass__eyebrow {
  display: inline-block; margin-bottom: 16px;
  font-size: 12.5px; font-weight: 600; letter-spacing: 0.06em;
  color: #fff; background: var(--ge-navy, #0F1438); border-radius: 999px; padding: 7px 15px;
}
.glass__h {
  margin: 0 0 12px; color: var(--ge-navy, #0F1438);
  font-size: clamp(28px, 3.4vw, 46px); font-weight: 600; letter-spacing: -0.02em; line-height: 1.07;
}
.glass__hl { position: relative; color: var(--ge-navy, #0F1438); white-space: nowrap; }
.glass__hl::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0.04em;
  height: 0.16em; background: var(--ge-navy, #0F1438); border-radius: 3px;
}
.glass__sub { margin: 0 0 22px; color: #4A5876; font-size: clamp(16px, 1.6vw, 18px); }
.glass__list { list-style: none; margin: 0 0 24px; padding: 0; display: flex; flex-direction: column; gap: clamp(12px, 1.5vw, 18px); }
.glass__list li { display: flex; gap: 14px; align-items: center; }
.glass__ic { flex: 0 0 auto; width: 46px; height: 46px; border-radius: 13px; display: grid; place-items: center; }
.glass__ic svg { width: 24px; height: 24px; }
.glass__it { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.glass__it b { color: var(--ge-navy, #0F1438); font-weight: 600; font-size: clamp(16px, 1.5vw, 19px); line-height: 1.2; }
.glass__it > span { color: #4A5876; font-size: 14.5px; line-height: 1.4; }
.glass__link {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--ge-blue, #1B367E); font-weight: 600; font-size: 15.5px; text-decoration: none;
}
.glass__link:hover { color: var(--ge-navy, #0F1438); }
.glass__link svg { width: 18px; height: 18px; transition: transform 200ms var(--hv2-ease); }
.glass__link:hover svg { transform: translateX(4px); }

/* The photo used to fill its whole half at 4/5 portrait — 579x724 against a
   486px-tall copy column, overhanging 119px above AND below, which read as
   off-balance (Rebecca, 27 Jul: "im not enjoying the size of it - looks a bit
   off balance with the content on the left side"). Capping the media width and
   squaring the ratio up brings the photo to ~520x551, so it only overhangs the
   copy by ~33px each side. margin-left:auto holds it to the outer edge, which
   also opens up the middle gutter. Every floating piece (.glass__tag,
   .glass__card, .glass__hex) is positioned in % of this box, so they all scale
   with it — no repositioning needed. The <=820px block below overrides
   max-width to 430px, so mobile is unaffected. */
/* NB: width:100% is load-bearing. An auto margin (or justify-self) on a grid
   item switches off its default stretch, so the item sizes to its CONTENT —
   and this box's content is all absolutely positioned, giving a max-content
   width of 0 and collapsing the photo entirely. The explicit width resolves
   against the grid track first, then max-width caps it. */
.glass__media {
  position: relative;
  width: 100%; max-width: 520px;
  justify-self: end;
}
.glass__hex { position: absolute; bottom: -16%; right: -8%; width: clamp(420px, 82%, 760px); opacity: 0.6; z-index: 0; pointer-events: none; }
.glass__hex img { width: 100%; height: auto; display: block; }
.glass__photo {
  position: relative; z-index: 1; border-radius: 22px; overflow: hidden; aspect-ratio: 1 / 1.06;
  box-shadow: 0 26px 54px rgba(15,20,56,0.22);
}
.glass__photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 70% center; display: block; opacity: 0; transition: opacity 900ms var(--hv2-ease); }
.glass__photo img.is-active { opacity: 1; }
.glass__card {
  position: absolute; z-index: 3; left: -6%; bottom: -7%; width: 62%;
  background: #fff; border-radius: 18px; padding: 16px 18px;
  box-shadow: 0 22px 46px rgba(15,20,56,0.26);
}
.glass__num { font-size: clamp(38px, 5vw, 60px); font-weight: 800; letter-spacing: -0.03em; line-height: 1; color: var(--ge-navy, #0F1438); }
.glass__num .odo { line-height: 1; letter-spacing: -0.03em; }
.glass__unit { font-size: 0.34em; font-weight: 700; color: var(--ge-sync, #1B88C8); margin-left: 0.08em; vertical-align: super; }
.glass__cap { font-size: 12.5px; color: #4A5876; margin-top: 5px; }
.glass__bar { height: 5px; border-radius: 999px; background: #E4EBF5; margin-top: 10px; overflow: hidden; }
.glass__bar i { display: block; height: 100%; width: 0; background: var(--ge-sky, #2FB7F1); border-radius: 999px; transition: width 1000ms var(--hv2-ease); }
.glass__media.in .glass__bar i { width: 86%; }
.glass__tag {
  position: absolute; z-index: 3; display: inline-flex; align-items: center; gap: 11px;
  background: #fff; border-radius: 16px; padding: 11px 16px 11px 12px;
  box-shadow: 0 16px 34px rgba(15,20,56,0.22); white-space: nowrap;
}
.glass__tag .gt-ic { flex: 0 0 auto; width: 38px; height: 38px; border-radius: 11px; display: grid; place-items: center; }
.glass__tag .gt-ic svg { width: 21px; height: 21px; }
.glass__tag b { font-size: clamp(14px, 1.3vw, 16px); font-weight: 600; color: var(--ge-navy, #0F1438); line-height: 1.15; }
.glass__tag--a { top: 8%; left: -5%; }
.glass__tag--b { bottom: 26%; right: -5%; }

@media (max-width: 820px) {
  .glass { grid-template-columns: 1fr; gap: clamp(22px, 6vw, 38px); }
  .glass__media { max-width: 430px; width: 100%; margin: 6px auto 18px; justify-self: center; }
  .glass__card { width: 66%; left: -2%; bottom: -5%; }
  .glass__tag--a { left: 0; top: 5%; }
  .glass__tag--b { right: 0; bottom: 20%; }
  .glass__hex { top: auto; bottom: -7%; right: -8%; width: 72%; opacity: 0.5; }
}
@media (prefers-reduced-motion: reduce) {
  .glass__bar i { width: 86%; transition: none; }
  .glass__link svg { transition: none; }
  .glass__photo img { transition: none; }
}

.pshow { display: block; }

/* ---------- horizontal product menu across the full width ---------- */
.psmenu {
  display: grid;
  /* 5 -> 4 tabs when Sync was removed (29 Jul); auto-fit so it always fills */
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
  gap: clamp(12px, 1.4vw, 20px);
  margin-bottom: clamp(22px, 2.6vw, 36px);
}
.psmenu__tab {
  appearance: none;
  border: none;
  cursor: pointer;
  font: inherit;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(18px, 2vw, 30px);
  border-radius: 20px;
  opacity: 1;
  transition: background 200ms var(--hv2-ease), transform 200ms var(--hv2-ease),
              box-shadow 200ms var(--hv2-ease);
}
/* Logo height brought down from clamp(28px,2.9vw,46px) so the Parent & Carer
   lockup can sit at the same optical size as the rest (Rebecca, 28 Jul: "the
   parent and care app looks a lot smaller that the other logos").
   Why it needed BOTH levers: that lockup is 6.52:1 — far longer than One 2.44,
   Sync 2.61, OnSite 3.37 or Classroom 4.76 — so it hits the tab's inner width
   limit at 26px tall while the others were still climbing to 42px. Dropping the
   shared height to ~32px and widening only that tab lands them all within ~2px. */
.psmenu__tab img { height: clamp(24px, 2.2vw, 34px); width: auto; display: block; }

.psmenu__tab:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(47,183,241,0.55); }
.psmenu__tab.is-open {
  opacity: 1;
  filter: none;
  transform: translateY(-3px);
  box-shadow: 0 14px 30px rgba(15,20,56,0.18);
}


.psmenu__tab--one       { background: #1B88C8; }
.psmenu__tab--onsite    { background: var(--ge-violet-4, #614DBA); }
.psmenu__tab--classroom { background: #2248C0; }
.psmenu__tab--sync      { background: #1B88C8; }
/* Parent & Carer now uses the deep sky that was Sync's, freed up when Sync
   was removed (Rebecca, 3 Aug: "change it to the colour sync was"). The
   matching .pst__panel--parentapp below moves with it, or the tab and the
   board it opens would clash. */
.psmenu__tab--parentapp { background: #00B1A7; }
/* Was sized by WIDTH (height:auto; width:100%) because the Parent & Carer
   lockup is ~6.5:1 — much wider than the other four wordmarks. But filling a
   ~330px tab rendered it ~46px tall against their 34px cap, which is what
   Rebecca spotted ("this si bigger than the other logos"). Height-matched to
   them now: at the shared 34px cap the lockup is ~221px wide, which fits the
   tab fine — the same 221px the nav dropdown tile already caps it to. The
   old comment's "would burst the tab" assumed a 46px height, not 34px.
   max-width is a floor-guard for narrow tabs; height/width come from the
   shared .psmenu__tab img rule. */
.psmenu__tab--parentapp img { max-width: 100%; }

/* ---------- the product board: a SIDEWAYS SCROLL of four cards ----------
 * Rebecca, 5 Aug: "i like how monzo display their products on a sideways scroll.
 * i want to try this on the homepage for the 4 products. i want people to still
 * see the 4 product logos on top."
 *
 * Built on NATIVE CSS scroll-snap rather than a JS transform carousel, which is
 * the whole reason it feels like Monzo's: trackpad two-finger swipe, touch drag,
 * shift+wheel and keyboard all work for free, and the browser does the easing. A
 * JS carousel would have to reimplement each of those and would still not feel
 * right on a trackpad.
 *
 * The four panels used to be STACKED in one grid cell and crossfaded, which is
 * why they all had to be the same height. Now they are a row of cards, so the
 * cards set their own height and the tallest simply is the tallest.
 * Card width is 88%, so the next card always peeks in at the right edge — that
 * peek is what tells you it scrolls. Monzo's cards sit at 79% of the viewport.
 * ------------------------------------------------------------------------ */
.pst {
  position: relative;
  display: flex;
  gap: clamp(14px, 1.5vw, 24px);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* no visible scrollbar — the peeking card and the arrows say "this scrolls" */
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* BLEED TO THE VIEWPORT EDGE (5 Aug: "can we make sure the tiles go right to the
     edge"). Without this the track stopped at the container's edge, so the peeking
     card was chopped off mid-photo with a band of white page beyond it — it looked
     like a mistake rather than a card continuing off-screen. Monzo's track runs
     past the viewport edge, which is what makes the peek read as "there is more".
     Negative margin pulls the track out to the edges; the matching padding keeps
     the FIRST card aligned with the page's content, and scroll-padding makes the
     snap land on that same line rather than on the true left edge. */
  /* ⚠ Width-agnostic, and it has to be. Keying this to --page-gutter was wrong
     twice over: between 768 and 900px the home container drops to 24px padding
     while --page-gutter is still 88, so the track was pulled 64px wider than the
     page and gave the WHOLE PAGE a horizontal scrollbar on an iPad; and at 1920 the
     container's real gutter is 326px, so an 88px pull left the track well short of
     the edges.
     `50%` here resolves against the container's own content width, so
     `50% - 50vw` is exactly the distance out to the viewport edge at any width, and
     the matching padding puts the first card back on the content line. */
  /* NO BLEED. I read "make sure the tiles go right to the edge" as the viewport
     edge and pulled the track out past the page margins — which is what caused
     every alignment complaint that followed: the first card sitting on the screen
     edge, then the next card cropped hard against it. She meant the tiles should
     fill the CONTENT width. So the track stays inside .container, one card spans it
     exactly, and both sides line up with every other section on the page. There is
     no peeking card any more; the logo tabs and the arrows are the scroll cue. */
  /* room for the focus ring on a card that is snapped to the left edge */
  padding-bottom: 2px;
}
.pst::-webkit-scrollbar { display: none; }
@media (prefers-reduced-motion: reduce) { .pst { scroll-behavior: auto; } }

.pst__panel {
  /* 0 0 so the cards never shrink to fit — the container scrolls instead */
  /* one card fills the content width exactly, so it aligns on BOTH sides */
  flex: 0 0 100%;
  scroll-snap-align: start;
  /* a stray gesture can only ever land on a card, never between two */
  scroll-snap-stop: always;
  display: grid;
  /* ONE CARD (Rebecca, 5 Aug: "can we have the photo and in the whit card so its
     all together"). The photo tile and the white copy card used to be two separate
     tiles floating on a product-coloured frame with a coloured channel between
     them. Padding and gap are now zero, so the photo sits flush against the white
     copy and the whole panel reads as a single card with a photo down one side.
     ⚠ The product colour therefore shows nowhere on the card itself any more — it
     survives on the logo tab above and on the card's own button. Say the word if
     you want a thin coloured edge back for identity. */
  /* THIN COLOURED EDGE (her pick, 5 Aug). This is padding, not a border, and that
     is the whole trick: the panel's background is ALREADY the product colour
     (.pst__panel--one and friends set it), and the photo and copy now cover it
     completely — so 3px of padding lets exactly 3px of that colour show as an edge.
     No border, no per-product border-colour rules, nothing new to keep in sync if a
     product colour ever changes. The panel's overflow:hidden carries the edge round
     the corners, so it follows the 28px radius.
     Thickness: 3px, then "make it thicker" (14px), then 6 Aug 2026 "can we double
     the stroke here" — DOUBLED to 28px at 1440, scaling down to 20px on a phone.
     Note this is only the OUTER frame; the GAP between the photo and the copy stays
     at 0, which is what "all together" asked for. The coloured channel she objected
     to was that gap, not this frame — the original was 18px of both.
     ⚠ Shared component: the same board is on the HOME page and all three
     school-type pages. She asked while looking at a school-type page; doubling it
     everywhere keeps them identical, which is how it has always been. */
  padding: clamp(20px, 2vw, 28px);
  grid-template-columns: 1.2fr 1fr;           /* photo left, text + CTA right */
  gap: 0;
  align-items: stretch;
  border-radius: 28px;
  overflow: hidden;                           /* clips the photo to the corners */
}
/* .is-on is no longer visibility — every card is always visible. It survives only
   as the marker for which card is snapped, so the logo tab above can follow it and
   the odometer can re-roll. */
@media (max-width: 900px) {
  /* one card at a time on a phone, with a sliver of the next */
  .pst__panel { flex: 0 0 100%; }
}

/* NO WHITE FRAME (Rebecca, 5 Aug: "can we get the photos to fill whole tile - no
   white frame"). This tile used to carry the site-wide chunky --photo-frame border,
   which on the sideways-scroll cards read as a frame inside a frame — the card
   already has its own coloured padding around the tile.
   ⚠ This is a DELIBERATE EXCEPTION to the white-frame rule, which still holds on
   every other image tile on the site. The frame lives on those; it does not live
   here. `overflow: hidden` is what now clips the photo to the corner radius. */
.pst__screen {
  /* square: the panel is one card now and its own radius + overflow do the
     rounding, so an inner radius would show as a notch at the corners */
  border-radius: 0;
  overflow: hidden;
}

/* --- the copy tile: white card holding the pitch, points and CTA button --- */
.pst__copy {
  grid-column: 2;
  background: #fff;
  border-radius: 0;                 /* one card — see .pst__panel */
  padding: clamp(28px, 3.2vw, 52px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(16px, 2vw, 24px);
  color: var(--ge-navy, #0F1438);
}
/* ⚠ 10 Aug 2026 — Cerys asked for this pitch to break before "sign-in", so the
   second line starts with the three things OnSite does rather than trailing them
   off the first line. A forced <br>, because where a line wraps naturally depends
   on the tile width and cannot be controlled with text alone.
   Hidden below 700px: on a phone the tile is narrow enough that the text already
   wraps to several lines, and an extra forced break just strands the em dash. */
.pst__pitchbr { display: none; }
@media (min-width: 700px) { .pst__pitchbr { display: inline; } }

.pst__pitch {
  margin: 0;
  font-size: clamp(20px, 2.1vw, 31px);
  font-weight: 600;
  line-height: 1.22;
  color: var(--ge-navy, #0F1438);
}
.pst__points {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.pst__points li {
  display: flex;
  gap: 11px;
  align-items: flex-start;
  font-size: clamp(15px, 1.15vw, 16.5px);
  line-height: 1.45;
  color: var(--ge-navy, #0F1438);
}
.pst__ptick {
  flex: none;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  border-radius: 7px;
  background: var(--ge-navy, #0F1438);
  color: #fff;
  display: grid;
  place-items: center;
}
/* These panels have a solid product-colour background, so by the 3 Aug bullet
   rule ("if they are on a navy section make them white with a navy tick") their
   ticks are WHITE discs with a navy glyph — one rule for all five instead of a
   per-product shade. */
.pst__panel .pst__ptick { background: #fff; color: var(--ge-navy, #0F1438); }
.pst__ptick svg { width: 12px; height: 12px; }
/* proper CTA button — solid product-colour pill, always visible */
.pst__cta {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
  padding: 14px 26px;
  border-radius: 999px;
  background: var(--ge-navy, #0F1438);
  color: #fff;
  font-size: 15px;
  font-weight: 700;
  text-decoration: none;
  transition: background 180ms var(--hv2-ease), transform 180ms var(--hv2-ease);
}
.pst__cta svg { width: 18px; height: 18px; transition: transform 200ms var(--hv2-ease); }
.pst__cta:hover { transform: translateY(-2px); }
.pst__cta:hover svg { transform: translateX(4px); }
.pst__panel--one       .pst__cta { background: #14679B; }
.pst__panel--one       .pst__cta:hover { background: #0F5583; }
.pst__panel--onsite    .pst__cta { background: var(--ge-violet-4, #614DBA); }
.pst__panel--onsite    .pst__cta:hover { background: #4E3EA0; }
.pst__panel--classroom .pst__cta { background: #2248C0; }
.pst__panel--classroom .pst__cta:hover { background: #1B3AA0; }
.pst__panel--sync      .pst__cta { background: #14679B; }
.pst__panel--sync      .pst__cta:hover { background: #0F5583; }
/* pink family: bright --ge-pink-3 for the board, AA-safe --ge-pink-4 ink for
   the button and ticks, matching how One splits its teal */
.pst__panel--parentapp .pst__cta { background: #00756E; }
.pst__panel--parentapp .pst__cta:hover { background: #005E58; }
.pst__panel--one       { background: #1B88C8; }
.pst__panel--onsite    { background: var(--ge-violet-4, #614DBA); }
.pst__panel--classroom { background: #2248C0; }
.pst__panel--sync      { background: #1B88C8; }
/* moved to Sync's freed-up deep sky with its tab (Rebecca, 3 Aug) */
.pst__panel--parentapp { background: #00B1A7; }

/* --- the two tiles --- */
/* Square inside the merged card: .pst__panel is ONE card now, and its own 28px
   radius plus overflow:hidden do the rounding. A 16px radius on the halves showed
   as a notch at each of the card's corners. */
.pst__tile { border-radius: 0; overflow: hidden; }

/* ---------- THE PHOTO SLOT GETS A FIXED SHAPE (13 Aug 2026) ----------
 * Rebecca: "lets make the whole tile shorter. half of it is hidden when youre
 * looking at the filter options."
 *
 * ⚠ THE HEIGHT WAS BEING SET BY ONE PHOTOGRAPH, AND NOT THE ONE YOU ARE LOOKING AT.
 * The board is a flex scroller and all four panels stretch to the tallest, so the
 * whole board took its height from whichever photo was tallest at the column width.
 * That was `one-hero-photo.jpg` — 1200x1139, almost SQUARE — which at the 659px
 * column wants 626px. Measured: panel 681px tall while the tallest copy card needed
 * only 376px, so ~250px of every panel was empty space nobody chose.
 * It also explains the tight OnSite crop she raised separately: a 2.14:1 photo was
 * being squeezed into a slot shaped by a 1.05:1 one.
 *
 * 16/10 at the 659px column = 412px, which still clears the tallest copy card (376px)
 * so the photo governs the height and the panels stay even. Panel goes 681 -> ~468,
 * about a third shorter, which is what "half of it is hidden" was describing.
 *
 * ⚠ Do NOT put a min-height or a fixed px height here instead. The panels must be
 * free to grow if copy is ever added, and a px height would clip it silently.
 * ⚠ The --card and --logos variants are EXCLUDED: they hold a centred card and a
 * logo grid, not a cover photo, and forcing a ratio on them boxes their content. */
.pst__panel > .pst__screen:not(.pst__screen--card):not(.pst__screen--logos) {
  aspect-ratio: 16 / 10;
}

/* ---------- CURVED INNER SHAPE, 5 Aug 2026 ----------
 * Rebecca: "treating the photo and white card as one shape can we make the 4
 * corners curved". The panel's own 28px radius was rounding the COLOURED FRAME
 * only — the photo and the white copy card inside it stayed square, so at each
 * corner a square corner sat inside a curved one and the coloured edge looked
 * thicker on the diagonal than along the sides.
 *
 * Radius is concentric, not a guess: outer radius MINUS the frame thickness
 * (the panel's padding). That is what keeps the coloured edge a constant width
 * all the way round — any other value and the frame visibly fattens or pinches
 * at the corners. 14px at 1440, 18px on a phone, because the frame itself
 * scales down there.
 *
 * ⚠ Both tiles also carry `.pst__tile`, whose `border-radius: 0` sits LATER in
 * this file at equal specificity and would win. Hence the child combinator —
 * `.pst__panel > .pst__screen` is (0,2,0) against its (0,1,0), so this holds
 * wherever either rule ends up in the file.
 *
 * Two corners each, and which two depends on the layout: side by side above
 * 1020px, stacked below it (see the panel's grid override), so the photo owns
 * the left pair then the top pair, and the copy the mirror. Rounding all four
 * on both would put a curve in the middle of the shape, which is the opposite
 * of "one shape". */
.pst__panel { --pst-inner-r: calc(28px - clamp(10px, 1vw, 14px)); }
.pst__panel > .pst__screen { border-radius: var(--pst-inner-r) 0 0 var(--pst-inner-r); }
.pst__panel > .pst__copy   { border-radius: 0 var(--pst-inner-r) var(--pst-inner-r) 0; }
@media (max-width: 1020px) {
  /* stacked: photo on top, copy beneath */
  .pst__panel > .pst__screen { border-radius: var(--pst-inner-r) var(--pst-inner-r) 0 0; }
  .pst__panel > .pst__copy   { border-radius: 0 0 var(--pst-inner-r) var(--pst-inner-r); }
}

/* screenshot — big tile on the left. Every board uses the SAME fixed shape (uniform
   size across all four products) and the whole screenshot fits inside it, never cropped
   (contain letterboxes onto the white tile where the aspect differs). */
.pst__screen { grid-column: 1; background: #fff; align-self: stretch; display: flex; }
/* The image FILLS the tile (Rebecca, 5 Aug: "can we get the photo to fill the
   tiles"). It used to be pinned to the One screenshot's 2048×1161 shape and
   centred with margin:auto, so when the tile stretched to match the copy card
   beside it the image kept its 16:9 and left white bands above and below.
   Now it takes the tile's full height and crops instead — safe because all four
   panels carry PHOTOGRAPHS since 5 Aug; a UI screenshot would lose its edges
   this way, so if one ever comes back it needs its ratio pinned again.
   The white frame is gone (see .pst__screen above), so the photo fills the tile
   edge to edge and takes the tile's own radius rather than a smaller inner one. */
.pst__screen > img { width: 100%; height: 100%; aspect-ratio: auto; object-fit: cover; display: block; border-radius: 0; }
/* ⚠ THE ONSITE RECEPTION PHOTO NEEDS ITS OWN CROP — 6 Aug 2026.
   Rebecca: "can we move her over to the left more so the ipad is in shot."
   The source is 3000x1400 (2.14:1) and this tile is about 1.05:1, so `cover`
   only ever shows a 1470px-wide slice — 49% of the photo. Centred, that slice
   ran from x=765 to x=2235 and the sign-in kiosk (x≈2160-2805) was almost
   entirely off the right edge: you saw her, and a sliver of screen.
   Pushing object-position to 88% slides the window right to roughly x=1345-2815,
   which holds her face and the whole kiosk. She moves left in the frame, which
   is exactly what was asked.
   ⚠ Matched on the FILENAME, not a new class, so all four pages that show this
   tile (home, Primary, Secondary, LAs & Trusts) get it without touching four
   HTML files. If the photo is ever renamed, this rule stops applying silently.

   ⚠⚠ AND THAT IS EXACTLY WHAT HAPPENED — fixed 13 Aug 2026. Rebecca: "can you move
   the image to the left so we can see the ipad", which is this rule's whole job. It
   had stopped working: the file was `onsite-hero-reception-signin.jpg` when this was
   written and became `onsite-reception-signin.jpg` in the 10 Aug asset reorg, so
   `[src$=...]` quietly matched nothing and the crop fell back to centred 50%. The
   warning above called it and was still not enough to prevent it.
   ⚠ Now matched with *= on "reception-signin" — the stable middle of the name. That
   survives both a directory move and a prefix change, which is what the two renames
   this file has been through actually were. Do NOT tighten this back to $= with a
   full filename.
   ⚠ The tile is WIDER than when this was written (measured 659x428, ratio 1.54, not
   the 1.05:1 below), so `cover` now shows 72% of the photo rather than 49%. 88%
   still lands the window at roughly x=740-2900, holding her face and the whole
   kiosk — verified on screen, not assumed. */
.pst__screen > img[src*="reception-signin"] { object-position: 88% 50%; }
.pst__screen--card { display: flex; align-items: center; justify-content: center; padding: clamp(16px, 2vw, 30px); }

/* --- code-built rows (Sync card + any placeholder UI) --- */
.pst-uicard { width: 100%; display: flex; flex-direction: column; gap: 12px; }
.pst-row {
  display: flex; align-items: center; gap: 12px;
  font-size: clamp(13px, 1.1vw, 15px); color: var(--ge-navy, #0F1438);
}
.pst-row__ico { flex: none; width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center; background: #F3F7FA; }
.pst-row__ico svg { width: 17px; height: 17px; color: #1B88C8; }
.pst-row__txt { flex: 1; font-weight: 700; }
.pst-row__val { font-weight: 700; }
.pst-tick { color: var(--ge-green-4, #76A74F); display: inline-flex; align-items: center; gap: 6px; }
.pst-tick svg { width: 16px; height: 16px; }

/* Sync board — hub: the 3 external MIS flow into GroupEd (data both ways) */
.pst__screen--logos { display: block; padding: clamp(20px, 2.6vw, 42px); }
.synchub {
  position: relative; width: 100%; height: 100%;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 0.82fr);
  align-items: stretch; gap: clamp(8px, 1.6vw, 22px);
}
.synchub__wires { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; overflow: visible; }
.synchub__wire { fill: none; stroke: #1B88C8; stroke-width: 2.2; opacity: 0.5; stroke-dasharray: 5 7; }
.pshow.is-live .synchub__wire { animation: synchub-flow 1.5s linear infinite; }
.synchub__wire--rev { animation-direction: reverse; }
@keyframes synchub-flow { to { stroke-dashoffset: -48; } }
@media (prefers-reduced-motion: reduce) { .synchub__wire { animation: none !important; } }
.synchub__sources { position: relative; z-index: 1; display: flex; flex-direction: column; justify-content: space-between; gap: clamp(10px, 1.6vw, 18px); }
.synchub__chip {
  flex: 1; display: flex; align-items: center; justify-content: center;
  background: #fff; border: 1px solid #E6ECF4; border-radius: 14px;
  box-shadow: 0 6px 18px rgba(15, 20, 56, 0.08);
  padding: clamp(6px, 0.9vw, 12px) clamp(12px, 1.8vw, 22px);
}
.synchub__chip img { max-height: clamp(20px, 2.8vw, 34px); max-width: 82%; width: auto; height: auto; object-fit: contain; display: block; }
.synchub__hub { position: relative; z-index: 1; justify-self: center; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; }
.synchub__badge {
  display: grid; place-items: center; border-radius: 22px;
  background: #D9F4FF; box-shadow: 0 14px 32px rgba(27, 136, 200, 0.22);
  padding: clamp(16px, 2.2vw, 30px) clamp(18px, 2.4vw, 34px);
}
.synchub__badge img { max-height: clamp(38px, 4.8vw, 62px); width: auto; display: block; }
.synchub__badge--sync { background: #1B88C8; box-shadow: 0 14px 34px rgba(27, 136, 200, 0.32); }
.synchub__badge--sync img { height: clamp(30px, 3.8vw, 50px); max-height: none; width: auto; }
.synchub__sync { display: inline-flex; align-items: center; gap: 7px; color: #14679B; font-size: clamp(12px, 1vw, 14px); font-weight: 700; white-space: nowrap; }
.synchub__sync svg { width: 16px; height: 16px; flex: none; }

/* ------------------------------------------------------------
 * WHOLE CHILD — navy band with the photo pair (SEAM 3)
 * ------------------------------------------------------------ */
.band--navy {
  background: var(--ge-navy, #0F1438);
  padding: clamp(80px, 9vw, 140px) 0;
}
/* Light twin of .band--navy, on the home page's warm off-white (3 Aug 2026).
   Added because one.html had the navy "See the whole child" band immediately
   followed by the navy "before / after" band — two dark sections with no seam,
   which the design rules bar. Rebecca: "turn one to a light band".
   .wchild's copy colours are hard-coded white for the navy case and are used by
   three school-type pages, so they are NOT changed — the light case overrides
   them below instead. */
.band--warm {
  background: var(--ge-bg-warm, #F7F6F3);
  padding: clamp(80px, 9vw, 140px) 0;
}
.band--warm .wchild__copy h2 { color: var(--ge-navy, #0F1438); }
.band--warm .wchild__copy p { color: var(--ge-text-secondary, #4A5876); }
.band--warm .wchild__lead { color: var(--ge-navy, #0F1438) !important; }
/* the video sat on a 6%-white placeholder and the phone carried a 45%-black
   shadow — both tuned for navy. On off-white they need a visible edge instead. */
.band--warm .pair__photo {
  background: #E6ECF4;
  box-shadow: 0 24px 56px rgba(15, 20, 56, 0.14);
}
.band--warm .pair__phone { box-shadow: 0 20px 44px rgba(15, 20, 56, 0.18); }
.wchild {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: clamp(36px, 6vw, 100px);
  align-items: center;
}
.wchild__copy h2 { color: #fff; margin: 0 0 14px; }
.wchild__copy p { color: rgba(255,255,255,0.85); font-size: clamp(15px, 1.15vw, 18px); line-height: 1.6; max-width: 46ch; margin: 0 0 16px; }
.wchild__copy p:last-child { margin-bottom: 0; }
/* opening hook — larger and full white, per brand body copy is never bold */
.wchild__lead { color: #fff !important; font-size: clamp(19px, 1.7vw, 25px) !important; line-height: 1.4 !important; letter-spacing: -0.01em; }

/* the photo pair — phone low-left, photo behind right, tile at the seam */
.pair { position: relative; }
.pair__photo {
  display: block;
  margin-left: auto;
  width: 82%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 24px;
  background: rgba(255,255,255,0.06); /* holds the shape while a video loads */
}
.pair__phone {
  position: absolute;
  left: 0;
  bottom: -44px; /* breaks just past the video's bottom edge */
  width: 42%;
  z-index: 1;
  background: #fff; /* white photo frame */
  padding: 12px;
  border-radius: 22px;
  box-shadow: 0 28px 60px rgba(0,0,0,0.45);
}

/* ------------------------------------------------------------
 * STEPS — lime band, white card straddling navy → green (SEAM 4)
 * ------------------------------------------------------------ */
.band--light { background: var(--ge-bg-light, #F3F7FA); padding-top: clamp(80px, 9vw, 140px); padding-bottom: clamp(80px, 9vw, 130px); }
.stepscard {
  position: relative;
  z-index: 3;
  background: #fff;
  /* stroke dropped, radius unified (4 Aug) — it already carries a deep shadow,
     so the hairline only added a second competing edge */
  border-radius: var(--tile-radius);
  box-shadow: 0 24px 56px rgba(15,20,56,0.14);
  padding: clamp(36px, 5vw, 80px);
  margin-top: 0; /* sits clear of the navy band above */
  margin-bottom: clamp(-96px, -7vw, -56px); /* straddles down into the blue stats band */
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.4fr);
  gap: clamp(32px, 5.5vw, 96px);
  align-items: center;
}
.stepscard h2 { margin: 0; color: var(--ge-navy, #0F1438); max-width: 14ch; }
.stepscard__intro { margin: 18px 0 0; color: #4A5876; font-size: 16.5px; line-height: 1.6; max-width: 34ch; }
.steps { position: relative; margin: 0; padding: 0; list-style: none; }
.steps::before {
  content: "";
  position: absolute;
  left: 21px;
  top: 40px;
  bottom: 40px;
  border-left: 2px dotted #C9E9F8;
}
.step { position: relative; display: flex; gap: 22px; padding: 20px 0; }
.step__chip {
  position: relative;
  z-index: 1;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 14px;
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 15px;
  background: var(--ge-blue-3, #D9F4FF);
  color: var(--ge-blue, #1B367E);
  transition: background 420ms var(--hv2-ease), color 420ms var(--hv2-ease), transform 420ms var(--hv2-ease);
}
.step:nth-of-type(1) .step__chip { background: var(--ge-blue-3, #D9F4FF); color: var(--ge-blue, #1B367E); }
/* ⚠ #3D6B22, not --ge-green-4. That token resolves to #76A74F, which on the
   pale green chip measures 2.45:1 — the step numbers were nearly unreadable.
   4.6:1 now, and still unmistakably the green step. */
.step:nth-of-type(2) .step__chip { background: var(--ge-green-2, #E6F3D9); color: #3D6B22; }
/* 4.41:1 was a hair under AA; #7A5C00 is 5.4:1 */
.step:nth-of-type(3) .step__chip { background: var(--ge-yellow-2, #F9EFD0); color: #7A5C00; }
.step:nth-of-type(4) .step__chip { background: #ECE8FB; color: #4B3B9C; }
.step:nth-of-type(1).in .step__chip { background: var(--ge-sky, #2FB7F1); color: var(--ge-navy, #0F1438); transform: scale(1.06); }
.step:nth-of-type(2).in .step__chip { background: var(--ge-green-3, #8ACB55); color: var(--ge-navy, #0F1438); transform: scale(1.06); }
.step:nth-of-type(3).in .step__chip { background: var(--ge-yellow-3, #E8BA19); color: var(--ge-navy, #0F1438); transform: scale(1.06); }
.step:nth-of-type(4).in .step__chip { background: #9C8AE6; color: var(--ge-navy, #0F1438); transform: scale(1.06); }
.step h3 { margin: 4px 0 8px; font-size: 20px; color: var(--ge-navy, #0F1438); }
.step p { margin: 0; font-size: 15.5px; color: #4A5876; line-height: 1.6; max-width: 52ch; }

/* ------------------------------------------------------------
 * TESTIMONIAL + MARQUEE (SEAM 5: quote card straddles green → white)
 * ------------------------------------------------------------ */
.band--proof { background: #fff; padding-bottom: clamp(80px, 9vw, 140px); }
/* home: numbers → testimonial felt cramped (the carousel straddled UP into the
   numbers band). Drop the straddle here and give the band a normal top so the
   testimonial sits with clear space below (Rebecca, 23 Jul). School pages keep
   their own .band--navy + .band--proof handling.
   29 Jul: raised from clamp(40px,5vw,64px) to the standard section rhythm
   (Rebecca: "these sections look too close together - lets add more breathing
   room"). Measured across the home page, this was the ONLY band out of step —
   every other one sits at 120-160px top padding while this was on 64px. The
   smaller value dated from when the carousel still straddled up into the blue
   band and needed the room; it doesn't straddle any more. */
.band--blue + .band--proof { padding-top: clamp(80px, 9vw, 140px); }
.band--blue + .band--proof .quotes { margin-top: 0; }
/* ---------- plain testimonial (home page, 4 Aug) ----------
   "can we take the testimonial out of the banner... i think im enjoying a more
   simpliistic look with lots of white space." The words and the attribution are
   the carded version's, with the card itself gone: no background, no border, no
   oversized quote mark, nothing but the sentence and a small credit. The size
   sits between body copy and a section heading so it reads as someone SPEAKING,
   not as another headline, and the measure is capped so the line length stays
   comfortable however wide the window is.
   Kept separate from .quote/.quote--navy, which three other pages still use. */
.qplain {
  margin: 0 auto;
  /* widened with the type below (4 Aug: "make the testimonial nice and big") */
  max-width: min(1040px, 100%);
  text-align: left;
}
/* the anchor: crest above the words, at nearly twice the size it was as a
   figcaption stamp. Its own artwork is the frame, so no tile, no disc. */
.qplain__crest {
  /* Caption-sized, not the 84px standalone anchor it was: it now sits beside the
     school name inside the figcaption, so it reads as part of the attribution.
     alt="" because the name is right next to it — the crest is decorative there
     and a screen reader would otherwise say the school twice. */
  height: clamp(40px, 3.6vw, 52px);
  width: auto;
  flex: 0 0 auto;
  display: block;
}
/* the setup sentence: same words, demoted. Small and grey so the payoff line
   below is unmistakably the thing to read. */
.qplain__setup {
  margin: 0 0 clamp(16px, 1.8vw, 24px);
  max-width: 26ch;
  font-size: clamp(30px, 3.9vw, 54px);       /* up from 28-44px, 4 Aug */
  line-height: 1.18;
  letter-spacing: -0.02em;
  font-weight: 600;
  color: var(--ge-navy, #0F1438);
}
.qplain blockquote {
  margin: 0;
  max-width: 25ch;                           /* a char tighter — the type is bigger */
  font-size: clamp(30px, 3.9vw, 54px);       /* up from 28-44px, 4 Aug */
  line-height: 1.18;
  letter-spacing: -0.02em;
  font-weight: 600;
  color: var(--ge-navy, #0F1438);
}
/* the highlighted phrase: navy text on the signature pale-sky pill, matching
   every other highlight on the site (4 Aug rule) */
.qplain blockquote .q-hl {
  display: inline-block;
  line-height: 1;
  background: var(--ge-blue-3, #D9F4FF);
  color: var(--ge-blue, #1B367E);
  border-radius: 0.24em;
  padding: 0.1em 0.18em 0.16em;
  margin-block: 0.1em 0.04em;
}
/* credit only — the crest has moved to the top of the block */
.qplain figcaption {
  display: flex;
  align-items: center;
  gap: clamp(12px, 1.2vw, 18px);
  margin-top: clamp(22px, 2.4vw, 32px);
}
.qplain__by { display: block; min-width: 0; }
.qplain .q-name { display: block; font-size: 15px; font-weight: 600; color: var(--ge-navy, #0F1438); }
.qplain .q-role { display: block; font-size: 13.5px; color: var(--ge-text-secondary, #4A5876); }
/* room around it — this is the "lots of white space" half of the request */
.band--proof > .container > .qplain { padding: clamp(8px, 1.4vw, 20px) 0 clamp(56px, 7vw, 96px); }
/* THE GREY STRIP (Rebecca, 5 Aug: "get rid of grey strip"). .crests carries
   `margin-top: clamp(44px, 5vw, 78px)` — 72px at 1440. It used to sit BELOW the
   testimonial inside this band, so that margin was internal space. Now the
   testimonial has moved to its own photo band above, .crests is the band's first
   child, and because .band--proof sets padding-bottom only — no padding-top —
   that top margin COLLAPSES UP and out of the section. It showed as a 72px band
   of the body's warm off-white between the photo and the crests, which the
   section's own -56px seam only partly swallowed. Zeroed when it is first. */
.band--proof > .container > .crests:first-child { margin-top: 0; }
/* The short-window type step that used to sit here existed only so the pinned
   hold could engage on a 700-800px window. The pin was removed on 5 Aug, so the
   quote now keeps its full size at every height. */
@media (max-width: 700px) {
  .qplain blockquote { max-width: 20ch; }
}
/* ---------- testimonial carousel ---------- */
.quotes {
  position: relative;
  z-index: 3;
  margin: clamp(-64px, -4.5vw, -40px) 0 0;
  display: grid;                       /* stack all slides in one cell → stable height */
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 500ms var(--hv2-ease), transform 500ms var(--hv2-ease);
}
.quotes.in { opacity: 1; transform: none; }
/* Home (29 Jul): "let's not make it a carousel actually" — every slide sits
   in normal flow, stacked, all visible at once. The base .quotes/.quote
   rules above stay untouched for Primary/Secondary/LAs & MATs, which still
   cross-fade one slide at a time. */
.quotes--stack { display: flex; flex-direction: column; gap: clamp(24px, 3vw, 40px); }
.quotes--stack .quote {
  position: relative; opacity: 1; visibility: visible;
  /* even inset on all sides (~50-64px), matching the Starling reference —
     the base card's oversized bottom padding exists to clear the carousel's
     dot/arrow overlay, which the stack doesn't have (Rebecca, 29 Jul: "copy
     the margins in the examples"). */
  padding: clamp(40px, 5vw, 64px);
}

.quote {
  /* .quote is a <figure> — the browser's default 1em 40px margin was never
     reset (foundation.css's reset covers img/svg/button/a, not figure), so
     every quote card rendered 80px narrower than its container on every page
     that uses this carousel. Only surfaced now the home page compares its
     width directly against sibling tiles (Rebecca, 29 Jul). */
  margin: 0;
  grid-area: 1 / 1;
  position: relative;
  overflow: hidden;
  border-radius: var(--tile-radius);   /* was 28px — unified 4 Aug review */
  padding: clamp(36px, 5vw, 76px) clamp(36px, 5vw, 80px) clamp(72px, 7vw, 96px);
  display: block;
  opacity: 0;
  visibility: hidden;
  transition: opacity 420ms var(--hv2-ease);
}
.quote.is-on { opacity: 1; visibility: visible; }

/* core-colour variants (white text on navy/blue; navy text on sky) */
/* every card is a deep brand colour with white text — Rebecca doesn't
   like the light-background variants (July 2026) */
.quote--navy   { background: var(--ge-navy, #0F1438); color: #fff; }
.quote--violet { background: var(--ge-violet-4, #614DBA); color: #fff; }
/* blue sits between violet and navy, so the school-type pages can run their
   three slides violet -> blue -> navy as a light-to-dark progression instead
   of repeating the same colour twice (Rebecca, 28 Jul) */
.quote--blue   { background: var(--ge-blue, #1B367E); color: #fff; }

/* subtle GroupEd hexagon watermark */
.quote__mark {
  position: absolute;
  right: -6%;
  bottom: -22%;
  width: 46%;
  pointer-events: none;
  opacity: 0.06;                 /* subtle white ghost on the dark slides */
}
.quote--violet .quote__mark { opacity: 0.1; }  /* a touch more so it reads on the violet */
.quote__mark img { width: 100%; height: auto; display: block; }
.quote > *:not(.quote__mark) { position: relative; z-index: 1; }

/* TWO-COLUMN "photo" variant (trial, 29 Jul 2026) — Starling's testimonial
   layout: quote + meta on one side, a real photo of the person on the other,
   height-matched. Modifier so the plain text-only .quote still works
   anywhere it's used without this. No star rating / Trustpilot — not asked
   for, and we've no review platform to back a rating with. */
.quote--photo {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  align-items: stretch;
  gap: clamp(28px, 3.5vw, 56px);
}
.quote__text { display: flex; flex-direction: column; justify-content: center; }
.quote__photo {
  border-radius: 20px;
  overflow: hidden;
  min-height: 220px;
}
.quote__photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (max-width: 860px) {
  .quote--photo { grid-template-columns: 1fr; }
  .quote__photo { order: -1; height: clamp(180px, 46vw, 260px); }
}

.quote blockquote {
  margin: 0;
  max-width: 40ch;
  font-size: clamp(21px, 2.2vw, 32px);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.35;
}
.quote blockquote .q-hl { color: #fff; }
.quote figcaption { display: flex; align-items: center; gap: 14px; margin-top: 18px; }
.quote figcaption img { width: 46px; height: 46px; border-radius: 12px; background: #fff; object-fit: contain; padding: 4px; }
.quote figcaption .q-name { font-weight: 700; font-size: 15px; display: block; }
.quote figcaption .q-role { font-size: 13.5px; color: #fff; opacity: 0.85; display: block; }
.quote__stats { display: flex; flex-direction: column; gap: 16px; }
.quote__stat { border-left: none; background: rgba(255,255,255,0.10); border-radius: 18px; padding: 18px 22px; }
.quote--violet .quote__stat { background: rgba(255,255,255,0.1); }
.quote__statnum { font-size: clamp(26px, 2.4vw, 38px); font-weight: 800; line-height: 1.05; }
.quote__statlabel { font-size: 14px; opacity: 0.78; margin-top: 3px; }

/* nav: dots + arrows, bottom-right over the card */
.quotes__nav {
  grid-area: 1 / 1;
  align-self: end; justify-self: start;
  z-index: 2;
  display: flex; align-items: center; gap: 14px;
  /* aligned under the text column, not centred across the whole card —
     centring reads oddly now the photo column sits on the right (Rebecca,
     29 Jul: the Starling-style photo trial). Same left inset as the card's
     own padding so it lines up with the blockquote above it. */
  margin: 0 0 clamp(20px, 2.4vw, 30px) clamp(36px, 5vw, 80px);
}
.quotes__arrow {
  /* 40 -> 44px: the site's own touch-target floor (iPad pass, 29 Jul) */
  width: 44px; height: 44px; border-radius: 999px; cursor: pointer;
  border: 1px solid rgba(255,255,255,0.5); background: rgba(255,255,255,0.14); color: #fff;
  display: grid; place-items: center;
  transition: background 200ms var(--hv2-ease);
}
.quotes__arrow:hover { background: rgba(255,255,255,0.28); }
.quotes__arrow svg { width: 18px; height: 18px; }
.quotes__dots { display: flex; gap: 7px; }
.quotes__dot {
  position: relative;                /* anchors the invisible hit area below */
  width: 9px; height: 9px; border-radius: 999px; cursor: pointer; padding: 0;
  border: none; background: rgba(255,255,255,0.4);
  transition: background 200ms var(--hv2-ease), width 200ms var(--hv2-ease);
}

/* ============================================================
 * TOUCH TARGETS — iPad pass (Rebecca, 29 Jul: "make sure this
 * site will work well on ipad")
 *
 * The carousel dots are 9x9px. They LOOK right and stay that
 * size — what changes is an invisible ::after that enlarges the
 * tappable area, so nothing moves on screen.
 *
 * Height goes to the full 44px (there is nothing above or below
 * to collide with). Width can only grow to the dot plus its gap:
 * any wider and neighbouring hit areas would overlap, so tapping
 * dot 1 would sometimes fire dot 2 — worse than a small target.
 * The gaps themselves widen on touch devices only (below), which
 * buys real width without changing the look on a desktop.
 * ============================================================ */
.hero2__dot::after,
.quotes__dot::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  height: 44px;
  width: calc(100% + var(--dot-gap, 8px));
  border-radius: 999px;
}
.quotes__dot::after { --dot-gap: 7px; }

/* Coarse pointers get wider spacing, so the hit areas above get wider too
   (17px -> 27px each). Untouched on any device with a mouse. */
@media (hover: none) and (pointer: coarse) {
  .hero2__dots { gap: 18px; }
  .quotes__dots { gap: 18px; }
  .hero2__dot::after, .quotes__dot::after { --dot-gap: 18px; }
}
.quotes__dot.is-on { background: #fff; width: 22px; }
/* when the active slide has a light background, darken the nav controls (class set by JS) */

@media (prefers-reduced-motion: reduce) {
  .quotes { opacity: 1; transform: none; transition: none; }
  .quote { transition: none; }
}
@media (max-width: 700px) {
  .quote { grid-template-columns: 1fr; }
}

/* the trust strip — one quiet greyscale row, colour on hover */
/* Trusted-by crest wall — a contained panel, each crest in its own tile,
   so the logos hold their own instead of floating on white. */
/* Trusted-by crests — a full-bleed auto-scrolling marquee (was a boxed panel;
   changed 23 July 2026 so it isn't the same shape as the neighbouring cards) */
.crests {
  max-width: none;
  margin: clamp(44px, 5vw, 78px) 0 0;
  padding: 0;
  text-align: center;
}
/* SMALL LABEL, not a heading (Rebecca, 5 Aug: "we have lost the 'trusted by 100+
   schools across uk'. this doesnt need to be a big title"). It was 46px, and once
   the crest strip moved up under the statement it landed 56px below a 75px
   statement line — two big headings stacked, so it read as competing with the
   statement rather than labelling the logos beneath it. Now a quiet secondary
   line, sized like .crests__note below it. */
.crests__line {
  /* ⚠ SIZED TWICE ON 5 Aug. It was 46px navy; she cut it to a 17px grey label
     ("this doesnt need to be a big title") because at 46px it sat 56px under a
     75px statement line and read as a competing heading. Then: "make this a
     bigger title". So it is a title again — but 38px, not the original 46, so it
     still ranks below the statement above it rather than fighting it. Navy, and
     back on the heading letter-spacing. Say the word if you want the full 46. */
  margin: 0 0 clamp(20px, 2.4vw, 32px);
  font-size: clamp(26px, 2.6vw, 38px);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--ge-navy, #0F1438);
}
/* 4 Aug: no longer full-bleed. Rebecca: "i dont want the school logo carousel
   to be full width. can we have it to the width of the home page hero. add a
   white gradient on either side to make it look softer."
   WIDTH is computed from the same --hero-cap / --hero-gutter tokens the hero
   card itself uses, off the VIEWPORT (not the container), so the strip and the
   hero card line up exactly at every window size and can't drift apart.
   margin-left: 50% - half that width centres it: 50% is half the container's
   inner width, and the container is itself centred in the window.
   FADE: the mask runs to transparent over a longer 14% ramp (was 7%), which on
   this white band reads as a white gradient into the page on both sides. It is
   a mask rather than two white overlay strips so it still works if this band
   ever stops being white. */
.crests__marquee {
  /* Rebecca, 5 Aug: "can we bring in the school logo white gradients in a bit
     more i want them to be as wide as the content we have in under the hero".
     Was the HERO's width (--hero-cap, 1500px), which is wider than the page
     content, so the strip and its fades overhung every heading and card. Now it
     matches .container's CONTENT box — its 1280px cap LESS its own side padding
     — so the fades start where the content edge is. The 14%/86% mask stops below
     are unchanged; narrowing the box is what pulls the gradients in. Capping at
     1280 alone still left it 64px too wide: the outer width is not the content
     width. */
  --crest-w: min(calc(1280px - 2 * var(--sp-5, 20px)), calc(100vw - 2 * var(--sp-5, 20px)));
  width: var(--crest-w);
  margin-left: calc(50% - var(--crest-w) / 2);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}
/* Supporting line under the logo wall (Andrew's #8, 3 Aug) — deliberately
   small and secondary-grey like .statement__sub, not sized like
   .crests__line above. This section's whole point is ONE bold statement plus
   the wall of logos; a second 30-46px line here would read as a second
   headline competing with the first, not supporting it. */
.crests__note {
  margin: clamp(20px, 2.4vw, 32px) 0 0;
  font-size: 15px;
  color: var(--ge-text-secondary, #4A5876);
}
.crests__track {
  display: flex;
  align-items: center;
  gap: clamp(12px, 1.4vw, 20px);
  width: max-content;
  /* Duration is scaled to the TRACK WIDTH so the drift stays at the same
     px/second whatever the roster is (7 -> 77 -> 15 logos). The 4 Aug cut to
     15 crests made the track a fifth as long, so 500s would have crawled:
     the old track was ~24000px and travelled 12000px in 500s (24px/s); the new
     one is ~4800px and travels 2400px, so 100s keeps the same pace. */
  animation: crest-marquee 100s linear infinite;
}
/* .container swaps its padding from --sp-5 to --sp-8 at 768px, so the strip has
   to follow or it stops lining up with the content above that breakpoint. */
@media (min-width: 768px) {
  .crests__marquee { --crest-w: min(calc(1280px - 2 * var(--sp-8, 32px)), calc(100vw - 2 * var(--sp-8, 32px))); }
}
.crests__marquee:hover .crests__track { animation-play-state: paused; }
@keyframes crest-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.crests__track img {
  flex: 0 0 auto;
  width: clamp(104px, 10vw, 140px);
  height: clamp(104px, 10vw, 140px);
  object-fit: contain;
  background: #fff;
  border-radius: 18px;
  padding: clamp(11px, 1.2vw, 16px);
  /* no stroke: on the white band the old 1px #E6ECF4 outline read as a faint
     box round every crest, which is the tile stroke Rebecca has twice asked to
     lose. The shadow is lifted a little to carry the tile on its own. */
  box-shadow: 0 6px 18px rgba(15, 20, 56, 0.07);
  filter: grayscale(1);
  opacity: 0.9;
  transition: filter 200ms var(--hv2-ease), opacity 200ms var(--hv2-ease), transform 200ms var(--hv2-ease);
}
.crests__track img:hover { filter: none; opacity: 1; transform: translateY(-3px); }
@media (prefers-reduced-motion: reduce) {
  .crests__track { animation: none; flex-wrap: wrap; justify-content: center; width: auto; }
  .crests__marquee { -webkit-mask-image: none; mask-image: none; width: auto; margin-left: 0; }
}

/* ------------------------------------------------------------
 * FINAL CTA — dark photo board, phone stepping out (SEAM 6)
 * ------------------------------------------------------------ */
.final2 { padding: clamp(80px, 9vw, 140px) 0 clamp(280px, 22vw, 380px); background: #fff; overflow-x: clip; }
.final2__board {
  position: relative;
  border-radius: clamp(20px, 2.5vw, 34px);
  overflow: hidden;                        /* montage now lives inside the tile */
  background: var(--ge-blue, #1B367E);
}
.final2__inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 0.72fr) minmax(0, 2.25fr);
  gap: clamp(12px, 1.4vw, 24px);
  align-items: center;
  /* slim right padding so the montage can sit close to the board's edge */
  padding: clamp(32px, 4.5vw, 72px) clamp(8px, 1vw, 16px) clamp(32px, 4.5vw, 72px) clamp(32px, 4.5vw, 72px);
}
.final2__inner h2 { color: #fff; margin: 0 0 14px; font-size: clamp(30px, 3.4vw, 46px); text-wrap: balance; }
.final2__sub { color: rgba(255,255,255,0.87); font-size: clamp(15px, 1.2vw, 18px); line-height: 1.6; max-width: 44ch; margin: 0 0 24px; }
.final2__actions { display: flex; flex-wrap: wrap; gap: 14px; }
.final2__contact { margin: 22px 0 0; display: flex; gap: 22px; flex-wrap: wrap; }
.final2__contact a { color: rgba(255,255,255,0.85); text-decoration: none; font-size: 15px; border-bottom: 1px solid rgba(255,255,255,0.3); padding-bottom: 1px; }
.final2__contact a:hover { color: #fff; border-color: #fff; }
/* WATERMARK (Rebecca, 5 Aug: "do what you need to do to make the white icon logo
   a water mark"). It was a SOLID white hexagon at full opacity — a graphic in its
   own right, competing with the headline and the device montage rather than
   sitting behind them.
   Now the same faint ghost every hero on the site uses: white at 11%. See the
   hero-mark rule — one opacity, one colour, no per-page variation, so the mark
   reads as a texture in the navy instead of an object on it.
   The filter that used to force the pale-blue asset to white is gone: the src is
   now grouped-symbol-white.svg, which is already #FFFFFF. A filter would have
   been overriding the asset either way, and swapping the file is one less
   invisible transform to reason about. */
.final2__mark { opacity: 0.11; }
/* device montage version — landscape, transparent PNG, steps out over the form */
.final2__phonewrap { display: flex; justify-content: flex-end; align-items: center; min-width: 0; }
.final2__devices {
  width: 100%;
  max-width: 1123px;                        /* native size — SVG so it stays crisp when large */
  height: auto;
  filter: drop-shadow(0 24px 48px rgba(15,20,56,0.35));
}
/* Desktop (above the ≤1020 stacking breakpoint): the montage is positioned
   absolutely so its size is decoupled from the tile's height — the tile is
   driven by the text column and no longer grows when the montage grows.
   (≤1020 keeps the stacked flow.) */
@media (min-width: 1021px) {
  .final2__board { overflow: visible; }
  .final2__inner { grid-template-columns: minmax(0, 36%); }
  /* Wider copy column, home board only (3 Aug: "make the header 2 lines. put both
     buttons on the same line"). 36% gave a 412px column at 1280px, which forced
     the heading onto three lines. 44% lands it on two at every desktop width
     measured, and the buttons already fitted side by side.
     ⚠ It has to live INSIDE this breakpoint: above 1021px the montage is
     absolutely positioned and .final2__inner becomes a SINGLE column, so a rule
     outside the query (which is what I wrote first) was silently overridden.
     A modifier, not a change to .final2__inner, because Founders Club, Martyn's
     Law and the carbon page share that class. */
  .final2__inner--wide { grid-template-columns: minmax(0, 44%); }
  .final2__phonewrap {
    position: absolute;
    top: 50%;
    right: clamp(6px, 0.8vw, 18px);
    /* nudged right into the SVG's transparent padding so the devices sit
       closer to the board edge (same size — just shifted). The transparent
       bleed past the tile is clipped by .final2's overflow-x: clip. */
    transform: translate(clamp(70px, 10vw, 150px), -50%);
    width: 66%;
    max-width: 900px;
  }
}
@media (max-width: 900px) {
  .final2__phonewrap { justify-content: center; }
  .final2__devices { width: min(520px, 100%); margin-top: 8px; }
}

/* ------------------------------------------------------------
 * FOOTER
 * ------------------------------------------------------------ */
.foot2 { background: var(--ge-navy, #0F1438); color: #fff; padding: clamp(48px, 6vw, 80px) 0 28px; }
/* On pages with no contact-band (About Us, Careers, Blog, Book a Demo,
   Parent Help Centre) the section before the footer is allowed to curve
   straight into it — nothing straddles there to conflict with. But the
   curve's own negative margin-bottom pulls the footer's top edge UP by
   that same clamp(28px,4vw,56px), which shrinks the footer's effective
   padding-top by exactly that much: the footer's box now starts higher up
   (overlapping the curved section), while its padding-top is still counted
   from that new, higher top edge — so the logo/nav content ends up that
   much closer to the curve than it looks in the flat-seam case (Rebecca,
   29 Jul: "the content in the footer is now too close to the section
   above it"). Restore the padding-top the footer would have had WITHOUT
   the curve by adding the same amount back.
   This is a CLASS, not a `.seam-curve + .foot2` sibling selector — every
   page wraps its sections in <main>, with the footer as <main>'s sibling,
   not the last section's, so an adjacent-sibling combinator can never
   bridge that gap and silently never matches. Add `.foot2--after-curve`
   by hand on any footer that directly follows a curved section. */
.foot2--after-curve {
  padding-top: calc(clamp(48px, 6vw, 80px) + clamp(28px, 4vw, 56px));
}
.foot2__grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr 1fr; gap: clamp(20px, 2.4vw, 44px); }
.foot2__brand img { width: 168px; height: auto; }
.foot2__tag { color: rgba(255,255,255,0.75); font-size: 14.5px; max-width: 30ch; margin: 14px 0 0; line-height: 1.55; }
/* ⚠ h3, not h4 — changed 6 Aug 2026 with the markup, from the accessibility
   audit. The four footer column headings followed an <h2> with no <h3> between,
   which skips a level (SC 1.3.1 / 2.4.10) on 24 pages. h4 is kept in the
   selector ONLY so an unconverted page doesn't lose its styling; once nothing
   uses h4 in a footer it can go. The appearance is unchanged either way. */
.foot2 h3, .foot2 h4 { margin: 0 0 14px; font-size: 13px; letter-spacing: 0.1em; text-transform: uppercase; color: rgba(255,255,255,0.55); font-weight: 700; }
.foot2 ul { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 9px; }
/* 6 Aug 2026: PURE WHITE, not 85%. Rebecca: "can we make sure all the footer
   ctas are white not the off grey colour" — at 0.85 on the navy they read as pale
   grey rather than white.
   ⚠ The hover state had to change with it: it used to BE #fff, so once the resting
   colour went white there was no hover feedback left at all. It is an underline
   now, which also suits the accessibility work (underlined links in running text)
   without dimming a link on hover, which reads as disabled.
   ⚠ Deliberately NOT changed: `.foot2 h3/h4` at 0.55 (the small uppercase PLATFORM
   / COMPANY labels — they are headings, not CTAs, and white would flatten the
   column hierarchy) and `.foot2__bottom a` at 0.7 (Privacy / Terms / Carbon — legal
   utility links, deliberately quiet). Ask before changing either. */
.foot2 a { color: #fff; text-decoration: none; font-size: 15px; }
.foot2 a:hover { color: #fff; text-decoration: underline; text-underline-offset: 0.18em; }
.foot2__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px;
  margin-top: clamp(32px, 4vw, 56px);
  padding-top: 22px;
  border-top: 1px solid rgba(255,255,255,0.12);
  font-size: 13.5px;
  color: rgba(255,255,255,0.6);
}
.foot2__bottom a { color: rgba(255,255,255,0.7); margin-left: 18px; }


/* ------------------------------------------------------------
 * FOOTER — social icons + address
 * ------------------------------------------------------------ */
.foot2__social {
  display: flex;
  gap: 10px;
  margin-top: 18px;
}
.foot2__social a {
  display: grid;
  place-items: center;
  /* 42 -> 44px: the site's own touch-target floor (iPad pass, 29 Jul) */
  width: 44px;
  height: 44px;
  border-radius: 999px;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.18);
  color: #fff;
  transition: background 200ms var(--hv2-ease);
}
.foot2__social a:hover { background: var(--ge-blue, #1B367E); }
.foot2__social svg { width: 18px; height: 18px; }
.foot2__addr { color: rgba(255,255,255,0.85); font-size: 15px; line-height: 1.5; } /* matches the footer links */

/* ------------------------------------------------------------
 * FROM THE BLOG — three post cards on an off-white band
 * ------------------------------------------------------------ */
.band--blog { background: var(--ge-bg-light, #F3F7FA); padding-bottom: clamp(72px, 9vw, 130px); }

/* ------------------------------------------------------------
 * WHAT'S INCLUDED — comparison matrix (July 2026, after pricing)
 * Desktop: full 4-column table in a rounded card, product chips
 * sticky under the nav. Mobile: segmented product tabs show one
 * column at a time (no sideways scrolling). The bandhead's big
 * top padding clears the price cards straddling down from above.
 * ------------------------------------------------------------ */
.band--mtx { background: #fff; padding-bottom: clamp(80px, 9vw, 140px); }
/* was clamp(150px,15vw,210px) to clear the straddling price cards. That
   straddle is gone (see .pricegrid), so this drops to the standard section
   rhythm — left at 210px it would have opened a ~70px dead gap. */
.band--mtx .bandhead { padding-top: clamp(80px, 9vw, 140px); }
.vh { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.mtx {
  background: #fff;
  border: 1px solid #E6ECF4;
  border-radius: 24px;
  overflow: hidden;
}
.mtx__table { width: 100%; border-collapse: collapse; }

/* ============================================================
 * COLLAPSED COMPARISON TABLE (5 Aug 2026, flow review)
 * 8 of 25 rows until you ask for the rest. The collapse is driven by
 * .is-collapsed, which home-v2.js ADDS on init and toggles on click. It is
 * deliberately NOT in the shipped markup: with the class hard-coded, anyone
 * without JavaScript got 8 rows and a button that did nothing, so the collapse
 * could trap 17 rows. No JavaScript now means all 25 rows and no button.
 * ============================================================ */
.mtx.is-collapsed .mtx__table tbody tr:nth-child(n + 9) { display: none; }
/* A fade over the last visible row so the cut reads as "there is more" rather
   than as the end of the table. Sits on the wrapper, below the button. */
.mtx.is-collapsed { position: relative; }
.mtx.is-collapsed::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 84px;
  background: linear-gradient(to bottom, rgba(255,255,255,0), #fff);
  pointer-events: none;
}
.mtx__expand { display: flex; justify-content: center; margin-top: clamp(20px, 2.4vw, 32px); }
.mtx__more {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  /* 44px minimum hit area, per the touch-target standard */
  min-height: 44px;
  padding: 12px 26px;
  border-radius: 999px;
  border: 1px solid #D7DFEA;
  background: #fff;
  color: var(--ge-blue, #1B367E);
  font-family: inherit;
  font-size: 15.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background 200ms var(--hv2-ease), border-color 200ms var(--hv2-ease), color 200ms var(--hv2-ease);
}
.mtx__more:hover { background: var(--ge-blue-3, #D9F4FF); border-color: var(--ge-blue-3, #D9F4FF); color: var(--ge-navy, #0F1438); }
.mtx__more:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(47,183,241,0.45); }
.mtx__more svg { width: 18px; height: 18px; transition: transform 200ms var(--hv2-ease); }
/* chevron points up once everything is showing */
.mtx__more[aria-expanded="true"] svg { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
  .mtx__more, .mtx__more svg { transition: none; }
}
.mtx__table thead th {
  background: #fff;
  padding: 14px 16px;
  border-bottom: 1px solid #E6ECF4;
  text-align: center;
  vertical-align: middle;                    /* all four chips on one line */
}
.mtx__table .mtx__fcol { width: 34%; }
.mtx__chip {
  display: inline-grid;
  place-items: center;
  width: min(100%, 150px);
  padding: 11px 0;
  border-radius: 14px;
}
.mtx__chip img { height: 22px; width: auto; }
.mtx__chip--sync      { background: #1B88C8; }
.mtx__chip--classroom { background: #2248C0; }
.mtx__chip--one       { background: #1B88C8; }
.mtx__chip--onsite    { background: var(--ge-violet-4, #614DBA); }
.mtx__table tbody th {
  text-align: left;
  font-weight: 400;
  font-size: 15px;
  color: #2A3352;
  padding: 13px 16px 13px clamp(18px, 2vw, 28px);
}
.mtx__table tbody td { text-align: center; padding: 13px 16px; }
.mtx__table tbody tr { border-top: 1px solid #EEF2F6; }
.mtx__table tbody tr:hover { background: #F7FAFD; }
.mtx__tick {
  display: inline-grid;
  place-items: center;
  width: 22px; height: 22px;
  border-radius: 999px;
  background: var(--ge-green-2, #E6F3D9);
  color: var(--ge-green-4, #4F7D2C);
}
.mtx__tick svg { width: 12px; height: 12px; }
.mtx__no {
  display: inline-block;
  width: 18px; height: 18px;
  border-radius: 999px;
  border: 2px solid #E2E9F2;
}
/* mobile product tabs — hidden on desktop, where all columns show */
.mtx-tabs {
  display: none;
  align-items: center;
  gap: 4px;
  background: #EEF2F6;
  border-radius: 999px;
  padding: 4px;
  margin-bottom: 16px;
  width: fit-content;
}
.mtx-tab {
  border: none;
  background: none;
  padding: 9px 14px;
  border-radius: 999px;
  font: inherit;
  font-size: 14.5px;
  font-weight: 700;
  color: #4A5876;
  cursor: pointer;
}
.mtx-tab.is-on { background: #fff; color: var(--ge-navy, #0F1438); box-shadow: 0 2px 8px rgba(15,20,56,0.10); }
@media (max-width: 700px) {
  .mtx-tabs { display: inline-flex; }
  .mtx__table .mtx__fcol { width: auto; }
  /* Show only the active product's column.
     Keyed off a data-col attribute on each cell, NOT nth-child positions.
     The old positional mapping assumed one fixed column order (2=OnSite
     3=Sync 4=Classroom 5=One) — but the audience pages order their columns
     differently to the home page, so on those three pages the wrong column
     was being shown on a phone. Removing Sync on 29 Jul would have shifted
     every position again. This version cannot drift: it matches the cell's
     own product key, whatever order the columns sit in. */
  .mtx[data-active="onsite"]    .mtx__table [data-col]:not([data-col="onsite"]),
  .mtx[data-active="classroom"] .mtx__table [data-col]:not([data-col="classroom"]),
  .mtx[data-active="one"]       .mtx__table [data-col]:not([data-col="one"]) { display: none; }
}
/* footnote under the table — OnSite standalone note */
/* Centred (Rebecca, 5 Aug: "centralise this"). It follows the "Show all 25
   features" button, which is already centred by .mtx__expand — ranged left, the
   footnote's first word sat 600px away from the button above it and read as the
   start of a new block rather than a note under the table.
   `margin-inline: auto` matters as much as text-align: the measure is capped at
   62ch, well inside the module, so centring the type alone would have left the
   column still hugging the left edge. */
.mtx__foot {
  margin: clamp(14px, 1.6vw, 20px) auto 0;
  max-width: 62ch;
  text-align: center;
  text-wrap: pretty;
  font-size: 14.5px;
  line-height: 1.6;
  color: #4A5876;
}
.mtx__foot a { color: var(--ge-blue, #1B367E); font-weight: 700; }
.mtx__foot a:hover { color: var(--ge-navy, #0F1438); }

/* ---------- everything-included promise tile (under the matrix) ---------- */
.inclusive {
  position: relative;
  overflow: hidden;
  margin-top: clamp(40px, 4.5vw, 64px);
  padding: clamp(36px, 4.5vw, 64px);
  border-radius: 28px;
  background: var(--ge-blue, #1B367E);   /* flat brand blue — no gradient */
  color: #fff;
}
.inclusive__mark {
  position: absolute;
  right: -4%;
  bottom: -34%;
  width: 38%;
  opacity: 0.08;
  pointer-events: none;
}
.inclusive__mark img { width: 100%; height: auto; display: block; }
.inclusive__body { position: relative; z-index: 1; max-width: 46ch; }
.inclusive__title {
  margin: 0;
  font-size: clamp(24px, 2.6vw, 38px);
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.2;
  color: #fff;
}
.inclusive__lead {
  margin: 14px 0 0;
  font-size: clamp(16px, 1.4vw, 19px);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.85);
}

/* ============================================================
 * DEVICE VARIANT (5 Aug 2026) — the second .inclusive tile on the home page.
 * It briefly had a rotating "loading sign" watermark and a progress bar; she
 * dropped that ("remove the animations"), so this variant has NO motion and no
 * watermark at all. What it has instead: a phone mockup peeking up over the
 * tile's bottom edge, and NAVY rather than this component's own #1B367E, which
 * the "Free for LAs" banner higher up the same page now owns.
 * ============================================================ */
.inclusive--device {
  /* 5 Aug: BLUE, not navy — "style the dark navy tile about upgrades like we did
     with the blue tile on the home page". The blue tile she means is
     .final2__board, the "halve it" CTA at the foot of the home page: --ge-blue
     #1B367E with a white hexagon watermark at 11% bleeding off the top-right
     corner. Both are dark cards carrying a device shot, so having one navy and
     one blue read as two systems rather than one.
     Same value as .final2__board, from the same token — change one and change
     both, deliberately. */
  background: var(--ge-blue, #1B367E);
  /* the watermark needs a clipping context of its own, as on the CTA board */
  isolation: isolate;
  /* 5 Aug: the device went from a small phone peeking over the bottom edge to a
     near-full-height iPad ("make it much bigger so its almost the height of the
     module"), so the clearance had to grow with it — the iPad is ~1.13× as wide
     as it is tall and sits flush to the right edge. */
  /* room for a 58% montage plus its overhang — was 34vw/520px, sized for a
     narrow phone peeking in */
  padding-right: clamp(36px, 40vw, 620px);
  padding-bottom: clamp(44px, 5vw, 72px);
  /* the tile's height is now driven by the device, not the copy: 400px at 1440
     against the iPad's 372px, so it has ~14px of breathing room top and bottom */
  min-height: clamp(300px, 28vw, 420px);
  display: flex;
  align-items: center;
}
/* No max-width override — the base .inclusive__body's 46ch is right here. The
   30ch cap that was here fought the padding above and made the column narrower
   still, so the heading ran to five lines. */
/* the title carries the whole message now, so it is the only text in the tile */
.inclusive--device .inclusive__title { font-size: clamp(22px, 2.3vw, 34px); }
/* PEEKING IN: anchored to the tile's bottom edge and pushed BELOW it, so the
   tile's own overflow:hidden crops the phone off — the device reads as rising
   into the tile rather than sitting in it. Height-based, not width-based, so the
   crop stays the same fraction of the phone at every screen size. */
.inclusive--device .inclusive__phone {
  position: absolute;
  /* flush to the right edge and vertically centred, rather than hanging off the
     bottom — a device this size looks dropped rather than placed if it overhangs */
  /* MEASURED off the home page's "halve it" board, which is the banner she is
     comparing against: its montage is 66% of the board's width and overhangs the
     right edge by 132px. Matching the intent — big, hard right, breaking the
     edge slightly — at a size that suits this shorter tile. */
  right: clamp(-48px, -2.6vw, -20px);
  /* ⚠ centred with top/bottom + auto margins, NOT translateY(-50%). transform is
     reserved for GSAP's entry lift on the home page; using it here would be
     overwritten and drop the montage half its height down the tile. */
  top: 0;
  bottom: 0;
  margin-block: auto;
  z-index: 1;
  /* ⚠ THREE-DEVICE MONTAGE, not the single iPad (5 Aug: "i wanted you to use the
     same photo too and we used on the same banner on the home page - its of
     three mockups together"). It is the identical asset the home page's blue
     "halve it" board uses — same URL, so the browser fetches it once for both.
     Sized by WIDTH now, not height: the montage is landscape (1123×613) where the
     iPad was portrait, so a height-driven rule made it enormous. 52% keeps the
     three devices legible without crowding the headline. */
  width: 58%;
  height: auto;
  max-height: 104%;                     /* may stand a little proud, as theirs does */
  object-fit: contain;
  object-position: right center;
  width: auto;
  max-width: 52%;            /* never let it eat the headline's column */
  object-fit: contain;
  object-position: right center;
  display: block;
  filter: drop-shadow(0 24px 48px rgba(0, 0, 0, 0.38));
  pointer-events: none;
}
/* MESSAGE-ONLY (6 Aug 2026) — Rebecca, of the home page's copy: "lets remove
   this from the home page and just have the message". The device montage is gone
   from index.html only; the three audience pages (Primary, Secondary, LAs &
   Trusts) still carry it. Keyed off the ABSENCE of the image rather than a new
   class, so the tile can never be left holding a 620px gap for a device that
   isn't there. Everything below undoes the space the montage was reserving:
   the right padding, the device-driven min-height, and the 46ch copy column. */
.inclusive--device:not(:has(.inclusive__phone)) {
  padding-right: clamp(24px, 3.4vw, 56px);
  padding-bottom: clamp(32px, 3.6vw, 56px);
  min-height: 0;
}
.inclusive--device:not(:has(.inclusive__phone)) .inclusive__body { max-width: none; }
/* with the full width to play in, the message can carry the size it was always
   competing with the devices for — one line at desktop, never more than two */
.inclusive--device:not(:has(.inclusive__phone)) .inclusive__title {
  font-size: clamp(24px, 3vw, 44px);
  /* 48ch ≈ half of this 93-character sentence, so it balances onto two lines
     across the tile. 24ch was left over from the days when a 52%-wide montage
     owned the right half — with the devices gone it made three short lines
     against 438px of empty tile. */
  max-width: 48ch;
  text-wrap: balance;
}

/* ⚠ THE STACK POINT MOVED 760 → 1100 (5 Aug). With the small phone peeking in
   from the side, side-by-side survived down to 760px. The three-device montage
   is far wider — 52% of the tile — so from about 1100px down it ran straight
   under the headline: measured a 60px overlap at 900 and 274px at 390. 1100 was
   still too low — 1200 measured as colliding as well — so the stack point is
   1250, the first width at which the copy and a 52% montage both fit. */
@media (max-width: 1250px) {
  .inclusive--device {
    display: block;
    padding-right: clamp(36px, 4.5vw, 64px);
    /* the montage sits IN the flow now, so no bottom reserve is needed for it */
    padding-bottom: clamp(28px, 4vw, 48px);
    min-height: 0;
  }
  .inclusive--device .inclusive__body { max-width: none; }
  .inclusive--device .inclusive__phone {
    /* stacked layout below this width: the device sits under the copy at its own
       size rather than filling the height, or it swamps a phone screen */
    position: relative; right: auto; top: auto; bottom: auto; transform: none;
    height: auto; width: min(100%, 560px); max-width: 100%; max-height: none;
    margin: clamp(18px, 4vw, 28px) auto 0;
    object-position: center;
  }
}

/* ---------- pricing band (sky-tint, white product cards) ---------- */
.band--price .bandhead { padding-top: 0; }
.band--price {
  background: var(--ge-blue-3, #D9F4FF);
  /* bottom padding was 0 so .pricegrid could straddle out of the band; the
     straddle is gone (see .pricegrid) so this is now the standard section
     rhythm on all sides, giving the cards real space beneath them. */
  padding: clamp(80px, 9vw, 140px) 0;
}

.pricegrid {
  display: grid;
  /* auto-fit, not a hard column count: removing Sync on 29 Jul dropped this
     from 4 cards to 3 and the fixed repeat(4, 1fr) left an empty column with a
     287px hole on the right. This adapts to however many cards there are. */
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: clamp(18px, 1.8vw, 26px);
  margin-top: clamp(36px, 4vw, 60px);
  /* STRADDLE REMOVED (Rebecca, 29 Jul, twice: "i dont like how close these
     tiles are to the end of the section" then "you still havent fixed the
     padding here"). The cards used to hang clamp(-120px,-8vw,-70px) out of the
     bottom of the sky band, which is why there was no space beneath them —
     .band--price deliberately carried ZERO bottom padding to let them poke
     out. Shrinking the negative margin only made them poke out less; it could
     never create padding. So the straddle is gone and the band now has a real
     bottom padding on the standard section rhythm.
     The seam is still not flat: .band--price gains `.seam-curve` in the HTML,
     which is exactly the swap the seam notes above prescribe — the curve was
     only ever excluded here BECAUSE this straddle existed, and stacking both
     would have them fight. One overlap mechanism, as documented. */
  margin-bottom: 0;
  align-items: stretch;                        /* all four cards match the tallest */
}
.pcard {
  position: relative;
  z-index: 3;
  background: #fff;
  /* stroke dropped, 4 Aug review — the card already carries a shadow */
  border-radius: var(--tile-radius);
  overflow: hidden;                 /* header tab bleeds to the rounded top corners */
  box-shadow: 0 18px 44px rgba(15,20,56,0.12);
  display: flex;
  flex-direction: column;
  transition: transform 220ms var(--hv2-ease), box-shadow 220ms var(--hv2-ease);
}
.pricegrid .pcard:hover { transform: scale(1.03); box-shadow: 0 26px 60px rgba(15,20,56,0.2); }
.pcard__top { display: flex; flex-direction: column; gap: 12px; }
.pcard__logo {
  width: 100%; flex: none;
  height: clamp(64px, 6vw, 78px);
  border-radius: 0;
  display: grid; place-items: center;
}
.pcard__logo img { height: clamp(32px, 2.8vw, 40px); width: auto; }
/* padded body below the header tab */
.pcard__body {
  display: flex;
  flex-direction: column;
  gap: 20px;
  flex: 1;
  padding: clamp(24px, 2vw, 32px);
}
.pcard--sync      .pcard__logo { background: #1B88C8; }
.pcard--classroom .pcard__logo { background: #2248C0; }
.pcard--one       .pcard__logo { background: #1B88C8; }
.pcard--onsite    .pcard__logo { background: var(--ge-violet-4, #614DBA); }
/* min-height is TWO LINES of this text, and it is what aligns the three cards
   (Rebecca, 5 Aug: "the 2026 launch deal isnt centre alighed across the 3
   tiles"). Classroom's tag — "Behaviour, rewards & parent comms — syncs with your
   current MIS" — wraps to two lines while OnSite's and One's fit on one, so the
   eyebrow below it started 21px lower on that card and every row under it was
   pushed out of line. Reserving the second line here fixes the eyebrow, the
   prices and the tier labels in one go. In em, so it tracks the font-size. */
.pcard__tag { margin: 0; min-height: calc(2 * 1.4em); font-size: 15px; line-height: 1.4; color: #4A5876; }

.pcard__price { border-top: 1px solid #EEF2F6; padding-top: 18px; }
.pcard__eyebrow {
  display: block; font-size: 12.5px; font-weight: 700;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--ge-blue, #1B367E);
  margin-bottom: 8px;
}
.pcard__amount { margin: 0; font-size: clamp(34px, 3vw, 44px); font-weight: 800; line-height: 1; letter-spacing: -0.02em; color: var(--ge-navy, #0F1438); }
/* max() floors this at the site's 12px minimum — 0.42em of the price
   resolved to 10.9px (iPad pass, 29 Jul). */
.pcard__base { font-size: max(12px, 0.42em); font-weight: 700; color: #4A5876; }
.pcard__amount--soon { font-size: clamp(24px, 2.2vw, 30px); color: #4A5876; }
.pcard__per { margin: 6px 0 0; font-size: 15.5px; font-weight: 700; color: var(--ge-navy, #0F1438); }
.pcard--soon .pcard__per { font-weight: 400; color: #4A5876; }
/* ⚠ #6B7689, was #79849E. 3.75:1 on white — under AA, and this line carries a
   price commitment ("Years 2-5 frozen at the same price"), so it is real content
   rather than decoration. 4.6:1 now, at the same visual weight. */
.pcard__then { margin: 0; font-size: 13.5px; line-height: 1.5; color: #6B7689; }

/* The pale-sky .pricenote strip that was here is gone — she asked for the blue
   .inclusive tile instead ("for the 'free for las' can you use this design").
   That component already exists further down this file; nothing new was needed,
   which is why there is no replacement rule here. Only the top margin, so the
   tile clears the price cards above it. */
.pricegrid + .inclusive { margin-top: clamp(28px, 3.2vw, 48px); }

.pcard__cta {
  margin-top: auto;
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  padding: 13px 20px; border-radius: 999px;
  font-size: 15px; font-weight: 700; text-decoration: none;
  border: 1.5px solid var(--ge-navy, #0F1438); color: var(--ge-navy, #0F1438);
  background: #fff;
  transition: background 200ms var(--hv2-ease), color 200ms var(--hv2-ease);
}
.pcard__cta:hover { background: var(--ge-navy, #0F1438); color: #fff; }
.pcard__cta .btn__arrow { background: rgba(15,20,56,0.10); }
.pcard__cta:hover .btn__arrow { background: rgba(255,255,255,0.18); }
.pcard__cta .btn__arrow { width: 26px; height: 26px; border-radius: 999px; display: grid; place-items: center; }
/* secondary "Read more about X" link under the demo button */
.pcard__more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 12px 0 0;
  font-size: 14px;
  font-weight: 700;
  color: var(--ge-blue, #1B367E);
  text-decoration: none;
}
.pcard__more:hover { color: var(--ge-navy, #0F1438); }
.pcard__more svg { width: 15px; height: 15px; transition: transform 200ms var(--hv2-ease); }
.pcard__more:hover svg { transform: translateX(3px); }
/* two-tier pricing (OnSite): one clear row per school type */
.pcard__tier {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 0;
}
/* Rebecca, 5 Aug: "can we add a line between pricing for primary and
   secondary schools". The tier + tier rule already drew it on OnSite, where the
   two tiers are adjacent, but Classroom and One carry a per-pupil line between
   them so the rule never matched — hence no line on two of the three cards. */
.pcard__tier + .pcard__tier,
.pcard__per + .pcard__tier { border-top: 1px solid #E6ECF4; margin-top: 7px; padding-top: 14px; }
.pcard__tier .pcard__amount { font-size: clamp(26px, 2.2vw, 34px); }
.pcard__tierlabel { margin: 0; font-size: 14px; font-weight: 700; color: var(--ge-navy, #0F1438); }
.pcard__cta .btn__arrow svg { width: 14px; height: 14px; }
.pcard__cta:hover .btn__arrow { transform: translateX(3px); }

@media (max-width: 980px) { .pricegrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .pricegrid { grid-template-columns: 1fr; } }

/* ---------- stats band (blue colour-block, colourful stat cards) ---------- */
/* WHITE, not brand blue (3 Aug: "can we use a white background for the 'numbers
   schools actually feel'"). The four ROI cards each carry their own fill — sky
   tint, green tint, violet tint and navy — and their own text colours, so they
   read just as well on white as they did on blue; only the band and its heading
   needed changing. .band--blue is used on the home page ONLY, so the class name
   is now a misnomer but renaming it would touch nothing else — left as is.
   NB the band that follows (.band--proof) is also white, so it takes the warm
   off-white below to keep the site's no-two-adjacent-backgrounds rule. */
/* NAVY (5 Aug 2026) — the flow review. The page had only two dark moments in
   fourteen bands (the hero and the blue testimonial), and this band sits directly
   above the testimonial: on navy the two read as one deliberate peak two-thirds
   down the page instead of a flat middle. The four pastel stat tiles are
   unchanged — they were always the most confident thing here, and on navy they
   stop reading as a row of cards and start reading as a statement.
   Class name kept: .band--blue is home-page-only, so renaming it would churn
   markup for nothing. */
.band--blue {
  background: var(--ge-navy, #0F1438);
  color: #fff;
  /* symmetric — nothing straddles into this band any more, so equal
     space above the heading and below the grid keeps content centred */
  padding: clamp(72px, 9vw, 120px) 0;
}
.band--blue .bandhead { padding-top: 0; }
.band--blue .bandhead h2 { color: #fff; }
.band--blue .bandhead p { color: rgba(255, 255, 255, 0.78); }
/* The fourth tile was navy-on-white. On a navy band it would vanish, so it
   inverts to white with navy text — the same invert the site already uses for
   ticks on coloured surfaces. Its number keeps the sky it always had. */
.band--blue .roi--navy { background: #fff; color: var(--ge-navy, #0F1438); }
.band--blue .roi--navy .roi__ico { background: var(--ge-blue-3, #D9F4FF); color: var(--ge-blue, #1B367E); }
.band--blue .roi--navy .roi__num { color: var(--ge-blue, #1B367E); }
.band--blue .roi--navy .roi__unit { color: var(--ge-navy, #0F1438); }
.band--blue .roi--navy .roi__label { color: #2A3352; }

.roigrid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(16px, 1.8vw, 26px);
  margin-top: clamp(32px, 3.5vw, 52px);
  align-items: stretch;
}
.roi {
  border-radius: 24px;
  padding: clamp(22px, 2.2vw, 32px);
  transition: transform 220ms var(--hv2-ease), box-shadow 220ms var(--hv2-ease);
}
/* scoped to outrank the .rv.in reveal rule, which otherwise pins transform:none */
.roigrid .roi:hover { transform: translateY(-6px); box-shadow: 0 22px 48px rgba(15,20,56,0.28); }
.roi__ico {
  width: 48px; height: 48px; border-radius: 14px;
  display: grid; place-items: center;
  margin-bottom: 18px;
}
.roi__ico svg { width: 22px; height: 22px; }
.roi__num {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: clamp(40px, 4vw, 60px);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
}
.roi__unit { font-size: 0.42em; font-weight: 700; }
.roi__label { margin: 12px 0 0; font-size: 14.5px; line-height: 1.5; }

/* card palettes — primary family + accent tints (v2 colour rules) */
.roi--sky    { background: var(--ge-blue-3, #D9F4FF); color: var(--ge-navy, #0F1438); }
.roi--sky    .roi__ico { background: var(--ge-sky, #2FB7F1); color: #fff; }
.roi--sky    .roi__num { color: var(--ge-blue, #1B367E); }
.roi--sky    .roi__label { color: #2A3352; }
.roi--green  { background: var(--ge-green-2, #E6F3D9); color: var(--ge-navy, #0F1438); }
.roi--green  .roi__ico { background: var(--ge-green-3, #8ACB55); color: var(--ge-navy, #0F1438); }
.roi--green  .roi__num { color: #4F7D2C; }
.roi--green  .roi__label { color: #2A3352; }
.roi--violet { background: var(--ge-violet-2, #E8E4F9); color: var(--ge-navy, #0F1438); }
.roi--violet .roi__ico { background: var(--ge-violet-4, #614DBA); color: #fff; }
.roi--violet .roi__num { color: #5A3FC0; }
.roi--violet .roi__label { color: #2A3352; }

/* The .roi--quote fifth-tile variant that was here is gone. She asked for it,
   saw it, and rejected it the same day — "i dont like the testimonial tile -
   lets try the full width approach" — so the quote is now a full-width photo
   band (.band--quote, further down this file) and there is deliberately no tile
   version left to fall back to. */

/* ============================================================
 * TESTIMONIAL ON A FULL-WIDTH PHOTO (5 Aug 2026)
 * The quote sits on a school photo running the full page width — no card, no
 * tile. Reuses .band--photo (the switching band's component) for the blurred
 * image, so there is one implementation of that backdrop rather than three.
 *
 * The type goes WHITE, and that is the whole reason this band has its own
 * scrim: navy on a bright blurred photo is unreadable, and white unaided
 * measures 1.6-2.9:1 over photography. 62% navy takes white past AA with room
 * to spare. Do not lighten the scrim without re-checking the type.
 * ============================================================ */
/* ⛔ THE BLUE CARD IS GONE (5 Aug). She had it as a photo band, then a flat blue
   band, then a blue module — and then: "im not keen on the blue module for the
   testimonial... actually take it out of the module." So it is a plain WHITE
   section like the rest of the page, with the quote in navy. It is deliberately
   NOT in modular.css's dark-card list any more, and it is not a neutral module
   either: the quote carries itself at 54px with nothing behind it.
   That is now four surfaces tried and dropped for this one quote — a tile, a
   photo, flat blue, and a blue module. Don't offer a fifth. */
.band--quote {
  background: #fff;
  padding: clamp(80px, 9vw, 140px) 0;
}
.band--quote .qplain__setup,
.band--quote .qplain blockquote { color: var(--ge-navy, #0F1438); }
.band--quote .q-name { color: var(--ge-navy, #0F1438); }
.band--quote .q-role { color: var(--ge-text-secondary, #4A5876); }

.roi--navy   { background: var(--ge-navy, #0F1438); color: #fff; }
.roi--navy   .roi__ico { background: rgba(255,255,255,0.14); color: var(--ge-sky, #2FB7F1); }
.roi--navy   .roi__num { color: var(--ge-sky, #2FB7F1); }
.roi--navy   .roi__unit { color: rgba(255,255,255,0.92); }
.roi--navy   .roi__label { color: rgba(255,255,255,0.82); }

@media (max-width: 900px) { .roigrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .roigrid { grid-template-columns: 1fr; } }
.bloggrid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 2vw, 24px);
}
.blogcard {
  display: flex;
  flex-direction: column;
  background: #fff;
  /* 4 Aug: the stroke was previously removed by a blog-page-only rule, so the
     SAME card still wore it on the home page. Fixed at source instead. */
  border-radius: var(--tile-radius);
  box-shadow: var(--tile-shadow);
  overflow: hidden;
  text-decoration: none;
  transition: transform 220ms var(--hv2-ease), box-shadow 220ms var(--hv2-ease);
}
.blogcard:hover { transform: translateY(-4px); box-shadow: 0 22px 48px rgba(15,20,56,0.12); }
.blogcard__img {
  display: block;
  /* 4 Aug photo-ratio standard: 1:1 / 4:3 / 16:9 — was 16/10 */
  aspect-ratio: 16 / 9;
  overflow: hidden;
}
.blogcard__img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.blogcard__body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: clamp(20px, 2vw, 26px);
  flex: 1;
}
.blogcard__meta {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 13.5px;
  color: #4A5876;
}
.blogcard__tag {
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--ge-blue-3, #D9F4FF);
  color: var(--ge-blue, #1B367E);
  font-weight: 700;
  font-size: 12.5px;
}
.blogcard h3 { margin: 0; font-size: clamp(18px, 1.6vw, 21px); color: var(--ge-navy, #0F1438); line-height: 1.3; }
.blogcard__body p { margin: 0; font-size: 15px; line-height: 1.55; color: #4A5876; }
.blogcard__go {
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  font-weight: 700;
  color: var(--ge-blue, #1B367E);
}
.blogcard__go svg { width: 16px; height: 16px; transition: transform 200ms var(--hv2-ease); }
.blogcard:hover .blogcard__go svg { transform: translateX(5px); }

/* ------------------------------------------------------------
 * REVEALS + REDUCED MOTION
 * ------------------------------------------------------------ */
.rv { opacity: 0; transform: translateY(16px); transition: opacity 420ms var(--hv2-ease), transform 420ms var(--hv2-ease); }
/* stagger for a row of cards — scripts/home-v2.js sets --i per card. Defaults to
   0, so every hand-placed .rv on the site is unaffected. 70ms matches the step
   .casc__row already used. */
.rv { transition-delay: calc(var(--i, 0) * 70ms); }
.rv.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .rv { opacity: 1; transform: none; transition: none; }
  .hero2__chip { animation: none; opacity: 1; transform: none; }
  .nv--open .mm__tile { animation: none; opacity: 1; transform: none; }
  .odo__col { transition: none; }
  .rb__dot { animation: none; }
}

/* ------------------------------------------------------------
 * RESPONSIVE
 * ------------------------------------------------------------ */
@media (max-width: 1020px) {
  /* tablets: keep the floating chips clear of the hero copy by hugging the right edge */
  .hero2__chip--1 { right: 4%; top: 16%; }
  .hero2__chip--2 { right: 3%; top: 38%; }
  .hero2__chip--3 { right: 4%; top: 74%; }
  .nv__switch { display: none; }
  .nv__burger { display: grid; }
  /* the switcher is hidden here, so the burger menu must carry ALL three
     panels — stacked, labelled, and scrollable if taller than the screen */
  /* ⚠ overscroll-behavior: contain — 7 Aug 2026. Without it, scrolling to the
     bottom of the open mega-menu carries on and scrolls the PAGE behind it
     ("scroll chaining"), so you close the menu and find yourself somewhere else
     on the page. Only bites on a phone, where the menu is the scrolling one. */
  .nv--open .mm { max-height: calc(100dvh - 120px); overflow-y: auto; overscroll-behavior: contain; }
  .mm__inner { display: grid; grid-template-columns: 1fr; }
  .mm__inner::before {
    content: attr(data-mm-title);
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #6B7689;
  }
  .mm__inner + .mm__inner { border-top: 1px solid #EEF2F6; }
  .mm__tiles { grid-template-columns: repeat(2, 1fr); }
  .pillars { grid-template-columns: 1fr 1fr; }
  /* narrower screens: the two tiles stack — screenshot on top, copy + CTA below */
  .pst__panel { grid-template-columns: 1fr; }   /* copy under the photo on phones */
  .pst__screen { grid-column: 1; }
  .pst__copy { grid-column: 1; }
  .wchild { grid-template-columns: 1fr; }
  .pair__phone { bottom: -24px; }
  .stepscard { grid-template-columns: 1fr; }
  .quote { grid-template-columns: 1fr; }
  .quote__stats { flex-direction: row; }
  .final2__inner { grid-template-columns: 1fr; }
}
@media (max-width: 700px) {
  .hero2__cutout { height: 46%; max-width: 80%; right: -8%; }
  .hero2__mark { width: 90%; top: 4%; }
  .bloggrid { grid-template-columns: 1fr; }
  .psmenu { grid-template-columns: 1fr 1fr; }   /* 2-col tab grid on phones; the 5th wraps */
  .hero2__chip { display: none; }
  .hero2__copy { padding-bottom: clamp(90px, 13vh, 130px); }
  .proofrow { grid-template-columns: 1fr; margin-top: -48px; }
  .pillars { grid-template-columns: 1fr; }
  .statstrip { grid-template-columns: 1fr; gap: var(--sp-6, 24px); }
  .quote__stats { flex-direction: column; }
  .foot2__grid { grid-template-columns: 1fr 1fr; }
  .rb { font-size: 12.5px; text-align: center; }
}

/* ============================================================
 * SECURITY & TRUST band — white card floating over a photo.
 * Shared: home page + the three school-types pages.
 * ============================================================ */
.trust2 { background: var(--ge-bg-light, #F3F7FA); padding: clamp(80px, 9vw, 140px) 0; }
.trust2__frame {
  position: relative; border-radius: 28px; overflow: hidden;
  min-height: clamp(420px, 42vw, 540px);
  display: flex; align-items: center;
}
/* OVERSCAN for the parallax (5 Aug). At inset:0 the photo exactly filled its
   frame, so shifting it even a pixel would have pulled a hard edge into view.
   It is now 40px bigger than the frame on every side — the same trick
   .band--photo__img uses — which gives the drift 24px of slack to move within.
   ⚠ max-width: none is required: the global `img { max-width: 100% }` caps the
   width and the overscan only takes on the left, which is exactly the bug that
   bit .band--photo__img. */
.trust2__photo {
  position: absolute;
  inset: -40px;
  width: calc(100% + 80px);
  max-width: none;
  height: calc(100% + 80px);
  object-fit: cover;
  object-position: right center;
}
.trust2__card {
  position: relative; z-index: 1; margin: clamp(20px, 3vw, 48px);
  width: min(520px, 58%);
  background: #fff; border-radius: 22px; padding: clamp(28px, 3.2vw, 48px);
  box-shadow: 0 26px 64px rgba(15, 20, 56, 0.20);
}
.trust2__eyebrow {
  display: inline-block; margin-bottom: 14px; padding: 7px 15px; border-radius: 999px;
  background: #E7F4FD; color: var(--ge-blue, #1B367E);
  font-size: 12.5px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
}
.trust2__title { margin: 0 0 12px; color: var(--ge-navy, #0F1438); }
.trust2__lead { margin: 0 0 clamp(22px, 2.4vw, 30px); color: #4A5876; font-size: clamp(15px, 1.2vw, 17px); line-height: 1.55; }
.trust2__badges {
  display: flex; flex-wrap: wrap; align-items: center; gap: clamp(16px, 2vw, 30px);
  padding-top: clamp(18px, 2vw, 24px); border-top: 1px solid #E6ECF4;
}
.trust2__badge { display: block; width: auto; height: clamp(38px, 3.6vw, 48px); }
.trust2__badge--shield { height: clamp(56px, 5.2vw, 72px); }
@media (max-width: 820px) {
  .trust2__frame { display: block; min-height: 0; border-radius: 22px; }
  .trust2__photo { position: relative; width: 100%; height: clamp(200px, 46vw, 300px); object-position: center 28%; }
  .trust2__card { width: auto; margin: -44px 14px 14px; box-shadow: 0 16px 40px rgba(15, 20, 56, 0.16); }
}

/* ============================================================
 * PHOTO-CARD band — reusable: a white card floating over a photo.
 * Default = card left / photo subject right. Add .pcband--flip to
 * put the card on the right (photo subject left). Same family as .trust2.
 * ============================================================ */
.pcband { padding: clamp(80px, 9vw, 140px) 0; }
.pcband--tint { background: var(--ge-bg-light, #F3F7FA); }
.pcband__frame {
  position: relative; border-radius: 28px; overflow: hidden;
  min-height: clamp(440px, 44vw, 560px); display: flex; align-items: center;
}
.pcband__photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: right center; }
.pcband__card {
  position: relative; z-index: 1; margin: clamp(20px, 3vw, 48px);
  width: min(560px, 60%); background: #fff; border-radius: var(--tile-radius);
  padding: clamp(28px, 3.4vw, 52px); box-shadow: 0 26px 64px rgba(15, 20, 56, 0.20);
}
.pcband__eyebrow {
  display: inline-block; margin-bottom: 14px; padding: 7px 15px; border-radius: 999px;
  background: #E7F4FD; color: var(--ge-blue, #1B367E);
  font-size: 12.5px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
}
.pcband__title { margin: 0 0 14px; color: var(--ge-navy, #0F1438); }
.pcband__card p { margin: 0 0 12px; color: #4A5876; font-size: clamp(15px, 1.15vw, 16.5px); line-height: 1.55; }
.pcband__card p:last-child { margin-bottom: 0; }
.pcband__lead { color: var(--ge-navy, #0F1438) !important; font-weight: 600; font-size: clamp(16px, 1.3vw, 18.5px) !important; }
/* optional CTA at the bottom of the card (the card's last <p> zeroes its own
   bottom margin, so the button supplies the gap itself) */
.pcband__cta { margin-top: clamp(20px, 2.2vw, 28px); }
.pcband--flip .pcband__frame { justify-content: flex-end; }
.pcband--flip .pcband__photo { object-position: left center; }
@media (max-width: 820px) {
  .pcband__frame { display: block; min-height: 0; border-radius: 22px; }
  .pcband__photo { position: relative; width: 100%; height: clamp(200px, 46vw, 300px); object-position: center 28%; }
  .pcband__card { width: auto; margin: -44px 14px 14px; box-shadow: 0 16px 40px rgba(15, 20, 56, 0.16); }
}

/* ------------------------------------------------------------
 * PROOF CARD ICONS — one tint family each instead of three
 * identical sky tiles (Rebecca, 27 Jul: "make the 3 icons in the
 * 3 hero tiles different colours so they arent all blue").
 * Uses the three tint washes CLAUDE.md sanctions for this page —
 * sky, green, violet. Card 1 keeps the .proof__icon default.
 * The palette's own green-4/yellow-4 are too light to sit on their
 * own tints (2.66:1 and 2.04:1, under the 3:1 needed for a
 * graphic), so the green here is a deeper #357A43 at 4.91:1.
 * Scoped with --tinted because Founders Club and martyns-law reuse
 * .proofrow, and their cards are not the same three ideas.
 * ------------------------------------------------------------ */
.proofrow--tinted > :nth-child(2) .proof__icon {
  background: var(--ge-green-1, #F3FAEE);
  color: #357A43;
}
.proofrow--tinted > :nth-child(3) .proof__icon {
  background: var(--ge-violet-1, #F6F4FF);
  color: var(--ge-violet-4, #614DBA);
}

/* Card 3 is a link (to Founders Club) but was styled exactly like the two
   static cards beside it, so nothing said "click me" (Rebecca, 27 Jul: "make
   the 'see whats included' in the 3rd home hero tile an obvious link").
   Site link colour + underline per CLAUDE.md, arrow nudges on card hover. */
.proof__link {
  color: var(--ge-blue, #1B367E);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1.5px;
  /* keep the phrase and its arrow together — without this the line broke as
     "see what's" / "included →", stranding the arrow on its own line
     (Rebecca, 27 Jul). The whole link now wraps as one unit instead. */
  white-space: nowrap;
}
a.proof:hover .proof__link { color: var(--ge-navy, #0F1438); }
/* ONE trailing arrow for every underlined text link on the site (Rebecca,
   27 Jul: "make sure all underline type links have the arrow too"). Generated
   with ::after so no markup is needed per link. display:inline-block matters
   twice over: it stops the link's underline running under the arrow, and it
   makes the arrow un-breakable from the word before it, so the arrow can never
   be stranded on its own line. Both target links were reworded to END their
   sentence first — an arrow mid-sentence read as "…demo -> instead." */
.proof__link::after,
.cb--footer .cb__note a::after,
.ph-langs__req a::after {
  content: "\2192";
  display: inline-block;
  margin-left: 5px;
  transition: transform 200ms var(--hv2-ease);
}
a.proof:hover .proof__link::after,
.cb--footer .cb__note a:hover::after,
.ph-langs__req a:hover::after { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) {
  .proof__link::after,
  .cb--footer .cb__note a::after,
  .ph-langs__req a::after { transition: none; }
}

/* All three cards get a staggered entrance — previously only card 1 moved at
   all, via its odometer count-up (Rebecca: "only the 1st tile in the hero has
   an animation. give them all one"). fill-mode is BACKWARDS on purpose: it
   holds the hidden state during the delay but hands styling back to the
   stylesheet afterwards, so a.proof:hover's translateY still works. A
   forwards/both fill would pin the transform and kill the hover lift. */
@keyframes proof-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}
.proofrow--tinted > * { animation: proof-in 520ms var(--hv2-ease) backwards; }
.proofrow--tinted > :nth-child(1) { animation-delay: 260ms; }
.proofrow--tinted > :nth-child(2) { animation-delay: 380ms; }
.proofrow--tinted > :nth-child(3) { animation-delay: 500ms; }
@media (prefers-reduced-motion: reduce) {
  .proofrow--tinted > * { animation: none; }
}

/* ============================================================
 * ICON TILE COLOUR ROTATION — site-wide
 * Rebecca, 28 Jul: "across the site ... icon tiles are all the
 * same colour - can we use a variety to add interest."
 *
 * Each family was one flat colour repeated down the row:
 *   .st-card__ico  (Primary / Secondary / LAs)  --st-tint-2 / --st-ink
 *   .pv3-ben__ico  (all 5 product pages)        --pp-2 / --pp-ink
 *   .ph-ben__ico   (Parent Help Centre)         pink tint / pink ink
 *   .ml-cap__ico   (Martyn's Law)               violet-1 / violet-4
 *
 * The FIRST tile in every row deliberately keeps its page's own
 * colour, so each page still leads with its identity; tiles 2+
 * rotate through the palette's tint/ink pairs. Same four pairs the
 * Founders Club perks already use, so this reads as one system.
 * All pairs clear 3:1 as graphics (sky 9.78 · violet 7.28 ·
 * amber 5.44 · green 4.23).
 *
 * These selectors are deliberately more specific than each family's
 * own single-colour rule, so they win regardless of which stylesheet
 * loads last.
 * ============================================================ */

/* --- green --- */
.st-cards      > .st-card:nth-child(4n + 2) .st-card__ico,
.pv3-ben__grid > .pv3-ben:nth-child(4n + 2) .pv3-ben__ico,
.ph-ben__grid  > .ph-ben:nth-child(4n + 2)  .ph-ben__ico,
.ml-caps       > .ml-cap:nth-child(4n + 2)  .ml-cap__ico {
  background: #E6F3D9;
  color: #4F7D2C;
}
/* --- violet --- */
.st-cards      > .st-card:nth-child(4n + 3) .st-card__ico,
.pv3-ben__grid > .pv3-ben:nth-child(4n + 3) .pv3-ben__ico,
.ph-ben__grid  > .ph-ben:nth-child(4n + 3)  .ph-ben__ico,
.ml-caps       > .ml-cap:nth-child(4n + 3)  .ml-cap__ico {
  background: #ECE8FB;
  color: #4B3B9C;
}
/* --- amber --- */
.st-cards      > .st-card:nth-child(4n + 4) .st-card__ico,
.pv3-ben__grid > .pv3-ben:nth-child(4n + 4) .pv3-ben__ico,
.ph-ben__grid  > .ph-ben:nth-child(4n + 4)  .ph-ben__ico,
.ml-caps       > .ml-cap:nth-child(4n + 4)  .ml-cap__ico {
  background: #F9EFD0;
  color: #7A5C00;
}
/* --- sky, for the 5th tile in the six-tile rows (Parent Help Centre, Martyn's Law) --- */
.ph-ben__grid > .ph-ben:nth-child(4n + 5) .ph-ben__ico,
.ml-caps      > .ml-cap:nth-child(4n + 5) .ml-cap__ico {
  background: #D9F4FF;
  color: #1B367E;
}
/* Martyn's Law already LEADS violet (its first tile is violet-1), so the
   rotation's violet slot would read as a near-repeat two tiles along. Swap that
   one slot for sky on this family only. */
.ml-caps > .ml-cap:nth-child(4n + 3) .ml-cap__ico {
  background: #D9F4FF;
  color: #1B367E;
}

/* ------------------------------------------------------------
 * PRODUCT SWITCHER — resting tabs (Rebecca, 28 Jul: "the greyed out
 * versions arent the same grey. i want them to be uniform. also make
 * them fainter so they look like they are clickable")
 *
 * grayscale(1) could never be uniform: it preserves each colour's
 * LUMINANCE, so teal, violet, royal blue, sky and pink each convert
 * to a different grey. The only way to get one grey is to replace the
 * background outright, which is what this does — the per-product
 * colours below now apply only to the SELECTED tab.
 *
 * Because the wordmarks are white SVGs, they would vanish on a pale
 * grey, so they are tinted to a matching mid-grey with
 * brightness(0) invert(...) — black, then lifted to grey. At #8A8A8A
 * on this background that is ~3.4:1, clear of the 3:1 a graphic needs.
 * ------------------------------------------------------------ */
.psmenu__tab:not(.is-open) {
  background: #EDF1F6;
  box-shadow: inset 0 0 0 1px rgba(15, 20, 56, 0.06);
}
.psmenu__tab:not(.is-open) img {
  /* invert(0.54) landed the wordmark at only 3.04:1 on the grey — barely over
     the 3:1 a graphic needs. 0.48 gives ~3.9:1 while the TILE stays pale, which
     is where the "faint" reading comes from. */
  filter: brightness(0) invert(0.48);
  transition: filter 200ms var(--hv2-ease);
}
/* hover: darkens a step and lifts, so a resting tab reads as clickable
   rather than disabled */
.psmenu__tab:not(.is-open):hover {
  background: #E1E7F0;
  transform: translateY(-2px);
}
.psmenu__tab:not(.is-open):hover img { filter: brightness(0) invert(0.4); }

/* The Parent & Carer tab gets slimmer side padding than its siblings, buying its
   long lockup the extra width it needs to reach the others' height. */
/* padding override dropped 3 Aug: it existed only to fit the old
   full-width logo. The tab now uses the same padding as the other three. */

/* ============================================================
 * TOUCH-ONLY TARGET PADDING — iPad pass (Rebecca, 29 Jul: "make
 * sure this site will work well on ipad")
 *
 * These are standalone text links ("Read more about One", "All
 * articles", the footer legal links, the statement-page and
 * demo-tile links). They measured 20-23px tall, under the 24px
 * minimum target size, and they sit on their own rather than
 * inside a sentence — so the inline-link exemption doesn't cover
 * them.
 *
 * Scoped to coarse pointers so a desktop layout stays EXACTLY as
 * it is: this adds vertical padding only where there's a finger
 * instead of a cursor. Negative margin cancels the padding's
 * effect on surrounding spacing, so nothing shifts on iPad
 * either — the link just becomes easier to hit.
 * ============================================================ */
@media (hover: none) and (pointer: coarse) {
  .pcard__more,
  .bandhead__more,
  .ab2-link,
  .dm-tile__go,
  .art-all,
  .fxb__links a,
  .foot2__legal a {
    padding-block: 6px;
    margin-block: -6px;
  }
}

/* ============================================================
 * PHOTO PLACEHOLDER — shared component
 *
 * Promoted here from statement-page.css on 29 Jul when the
 * Founders' Club hero needed one ("just remove the images in the
 * founders hero - add one image tile placeholder instead").
 * Founders loads home-v2.css but NOT statement-page.css, and the
 * note on the original component said to promote rather than
 * copy — so this is now the single definition, used by About us,
 * Careers and the Founders hero.
 *
 * Deliberately obvious rather than tasteful: a placeholder must
 * never be mistaken for finished artwork on a page she is
 * showing people. Markup carries aria-hidden — it's a production
 * note, not content. Swap the element for an <img> when the real
 * photography arrives; the layouts are built so nothing moves.
 * ============================================================ */
.ph {
  display: grid;
  place-items: center;
  /* colour comes from the per-slot variants below; these are the defaults */
  background: var(--ph-fill, var(--ge-blue-3, #D9F4FF));
  border: 2px dashed var(--ph-line, var(--ge-sky, #2FB7F1));
  border-radius: clamp(18px, 2vw, 24px);
  min-height: 160px;
}
.ph__label {
  font-size: clamp(13px, 1.1vw, 15px);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ph-ink, var(--ge-blue, #1B367E));
  text-align: center;
  padding: 12px 20px;
}

/* A different brand colour per slot (Rebecca, 29 Jul: "can we choose different
   colours for each placeholder image"), so a page of placeholders reads as a
   deliberate rhythm rather than four identical blue boxes. Each pair is a
   tint-1/tint-2 fill with its own deep ink for the label — all straight from
   the brand scales, and every label measured at 4.5:1+ on its fill.
   Order down the page: sky, green, violet, yellow. */
.ph--c1 { --ph-fill: #D9F4FF; --ph-line: #2FB7F1; --ph-ink: #1B367E; }  /* sky    */
.ph--c2 { --ph-fill: #E6F3D9; --ph-line: #8ACB55; --ph-ink: #4A6B2A; }  /* green  */
.ph--c3 { --ph-fill: #E8E4F9; --ph-line: #8572DA; --ph-ink: #4B3A9E; }  /* violet */
.ph--c4 { --ph-fill: #F9EFD0; --ph-line: #E8BA19; --ph-ink: #7A5C00; }  /* yellow */

/* the beat pairs on About us / Careers — one shape, used by all of them */
.ph--beat { aspect-ratio: 4 / 3; width: 100%; }

/* ------------------------------------------------------------
 * BAND ON PHOTOGRAPHY (trial, 3 Aug 2026)
 * From a reference Rebecca sent where a section sits on a photo with
 * its cards floating over it. Applied to the home page's switching
 * band only, in place of the warm off-white.
 * The image is a real <img> rather than a CSS background so it can be
 * lazy-loaded and carry an empty alt; the scrim is a separate layer so
 * its opacity can be tuned without touching the photo.
 * ------------------------------------------------------------ */
.band--photo { position: relative; isolation: isolate; overflow: hidden; }
.band--photo__img {
  position: absolute;
  /* the blur softens the photo's own edges, so it is inset NEGATIVELY — without
     this the blurred edge would show as a pale fringe inside the band */
  inset: -40px;
  width: calc(100% + 80px);
  /* max-width: none is REQUIRED — the site's global `img { max-width: 100% }`
     was capping this at the band's own width, so the negative inset only took
     effect on the left and the blurred right edge finished 30px inside the band,
     leaving the pale fringe this negative inset exists to prevent. Measured on
     Classroom's testimonial band; the home page's switching band had it too. */
  max-width: none;
  height: calc(100% + 60px);
  object-fit: cover;
  object-position: center 42%;
  z-index: -2;
  filter: blur(9px);
}
/* NO scrim any more — a gaussian blur instead (3 Aug: "lets remove completely
   the gradient on the switching is the bit most schools dread. maybe lets try a
   gaussian blur instead"). The blur is on the IMAGE, not on this layer, so the
   photo reads as a soft out-of-focus backdrop at full brightness rather than a
   darkened one. This element stays in the markup as the faintest wash: a blurred
   photo is still busier than a flat band, and 12% navy is just enough to stop the
   white card's edge dissolving into a light patch of sky. Take it to 0 if you
   want the photo completely untouched. */
.band--photo__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: rgba(15, 20, 56, 0.12);
}
/* NO white text here. I flipped the heading to white on the assumption it sat
   on the photo — it does not: the h2 and its intro line are INSIDE the white
   .stepscard, so white-on-white made the title vanish ("the switching is the bit
   schools dread - title has gone"). Everything in this band is inside that card,
   so the card's own colours are correct and the photo only ever shows around it. */


/* ============================================================
 * MONZO GEOMETRY — ADOPTED (5 Aug 2026)
 * Switches 1 and 2 from the _hero-trial.html comparison, chosen by Rebecca.
 * Both values were MEASURED off monzo.com at 1440x900 on 5 Aug 2026:
 *   hero band     892px = 99% of the viewport   (ours was 663px = 74%)
 *   page margins  88px gutters, 1264px content  (ours was 112px / 1216px)
 * Her margins are NARROWER than ours were, not wider — Monzo's air is vertical.
 *
 * SCOPED TO body.home, which exists only on the home page. .container and
 * --hero-h are both shared by all 21 pages, so an unscoped change here would
 * silently re-lay-out the entire site. If this is ever rolled out further, move
 * these rules rather than copying them.
 *
 * NOT adopted: switch 3, Monzo's 49px/800 headline. Ours stays 80px/600 — she
 * kept the bigger type, and 800 would have broken the SemiBold-600 heading rule.
 * ============================================================ */

/* ---------- 1. hero height ---------- */
body.home {
  /* NOT 99svh. Copying Monzo's percentage was the wrong measure, because their
     hero STARTS higher up the screen than ours: theirs at y=87, ours at ~135
     (we carry a promo ribbon above the nav, plus the frame's own margin). Same
     percentage, different result — theirs overhung the fold by 55px, ours by 126,
     which is what Rebecca spotted as "taller than Monzo's".
     What actually matters is where the band ENDS relative to the fold, so that
     is what this sets: full viewport height minus the ~80px of chrome above it,
     which lands the bottom edge a consistent ~55px past the fold at every screen
     height — Monzo's number, held as a constant instead of drifting with the
     viewport. The 1100px ceiling stops it becoming absurd on a tall monitor. */
  /* ⚠ CORRECTED 5 Aug. I had been matching the wrong Monzo element. Their hero
     SECTION is 868px tall and its bottom edge does fall past the fold — but the
     visible rounded CARD inside it is only 750px, running from y=111 to y=861,
     which is 39px ABOVE the fold. Their card is fully on screen with room to
     spare; ours was overhanging by 55px, which is why Rebecca kept saying the
     banner still was not the same size as theirs.
     So: the card now ends 39px above the fold, exactly as theirs does. Chrome
     above our hero is nav 82 + the hero's 18px top padding = 100, so
     100svh - 139 puts the bottom edge at svh - 39. At 1440x900 that is a 761px
     card ending at 861 — Monzo's number to the pixel.
     If anything is added back above the nav, this figure is
     (chrome above the hero) + 39. */
  /* ⚠ THE NUMBER HAS BEEN 139 → 81 → 139 IN ONE DAY. Read this before changing it.
     · 139 originally: it lands the card's bottom edge 39px ABOVE the fold, so a
       band of white shows under the hero. Measured off monzo.com.
     · 81 briefly (6 Aug am): the top gap was being wrongly padded by 76px, and
       fixing that lifted the card 58px. I grew the height by the same 58 to hold
       the bottom edge still — which preserved a bug's side effect, not the design.
       With it the card overhung the fold and no white showed at all.
     · Back to 139 (6 Aug pm), Rebecca: "i want uor home page to have a white
       below the hero too - ours is too talls", against a monzo.com screenshot
       whose hero ends at ~97% of the window height.
     Chrome above the card is now nav 82 + the hero's own 18px top padding = 100,
     so 100svh − 139 puts the bottom edge at svh − 39. At 1440×900 that is a 761px
     card ending at 861, with 39px of white beneath it.
     ⚠ A sliver of the next section (~15px) shows below that white at 900 tall.
     That is intended — Monzo does the same; it is what signals there is more page. */
  --hero-h: min(calc(100svh - 139px), 1100px);
  /* Monzo: 24px inset, 64px radius. Ours were 22px and 34px — the inset was
     near enough, the radius was not: a 34px corner on a 1392px card reads as a
     square with the edges taken off, 64px reads as a card. */
  --hero-gutter: 24px;
}
/* REQUIRED companion, not a style choice. At 99% the hero copy's bottom anchor
   pushed "Book a free demo" 54px BELOW the fold on a 1440x900 laptop, measured.
   Monzo can afford a full-screen hero because their hero copy is compact and
   vertically centred; ours carries a headline, a lede, a CTA and three
   accreditation badges. Centring brings the CTA back to 809px, comfortably
   above the fold. The bottom padding this removes existed to clear hanging proof
   cards, and the home hero has none.
   ⚠ The accreditation badges still sit below the fold — they are docked to the
   photo's bottom-right corner and the photo is now taller than the screen. That
   is inherent to a full-screen hero; losing the badges from first view is a far
   smaller cost than losing the CTA. */
/* ⚠ DESKTOP ONLY — must stay inside this media query.
   home-personality.css deliberately sets `.hero2--graphic .hero2__frame
   { display: block }` below 900px so the 2:1 artwork shows uncropped as a
   banner with the copy flowing beneath it on the navy. This selector is
   (0,2,1) against its (0,2,0), so an unscoped `display: flex` here BEAT it
   however late that file loads — the photo went back in flow as a flex item
   868px wide, ate the whole row, and pushed .hero2__copy out past the frame's
   right edge where `overflow: hidden` clipped it. Net effect on every phone
   and iPad: no headline, no lede, no CTA, and 130px of page overflow.
   Introduced 5 Aug with the full-screen hero, caught the same day. */
@media (min-width: 901px) {
  body.home .hero2__frame { display: flex; align-items: center; }
}
/* 64px, measured off Monzo's hero card. Ours was clamp(20px, 2.5vw, 34px). */
body.home .hero2__frame { border-radius: 64px; }
body.home .hero2__copy { padding-bottom: clamp(28px, 4.5vw, 60px); }

/* ---------- 1b. the accreditation badges ----------
 * MEASURED off monzo.com, 1440x900, 5 Aug 2026, because Rebecca asked how they
 * keep theirs on screen with a near-full-height hero. Two things, and the second
 * is the one that matters:
 *
 *   - Their hero band also runs past the fold (bottom edge 955 on a 900px
 *     screen). They just put NOTHING in the overflowing part: their last element
 *     ends at 875 and the band runs to 955, so the off-screen 80px is empty.
 *   - Their badges are anchored IN the content column, at 7% and 71% down the
 *     hero — not pinned to its bottom edge. Lowest badge bottom: 773, a clear
 *     127px above the fold.
 *
 * Ours were `bottom: 28.7px`, i.e. glued to the band's bottom edge, so they
 * followed the band off the screen when it grew: badge bottom 997 against a
 * 900px fold, 97px below it.
 *
 * Re-anchored as a PROPORTION of the hero's height, so it holds at any screen
 * height instead of at one. 66%, not Monzo's 71%: their hero band starts at y=87
 * and ours starts at ~139 (we carry a promo ribbon above the nav, plus the
 * frame's own 22px margin), so the same percentage puts ours ~50px lower down the
 * screen. 66% reproduces their CLEARANCE rather than their percentage, which is
 * the thing that actually matters — Monzo's badges finish at 86% of the viewport
 * height, and at 66% ours finish at 86-89% across every size tested.
 * `bottom: auto` is required — without it the original bottom offset still
 * applies and the strip stretches between the two anchors.
 * Home page only: it is the only page with the tall hero. */
body.home .hero2__badges {
  /* 77%, measured off Monzo again on 5 Aug after Rebecca asked to move ours down
     to where theirs sit. Their two lower badges run y=703 to y=773 inside a card
     spanning 111 to 861 — so they FINISH at 88% of the card's height, sitting low
     near its bottom edge, not two-thirds down. Our strip is 80px tall in a 757px
     card, so a 77% top lands its bottom at that same 88%.
     Was 66%, which put ours noticeably higher than theirs. Clearance above the
     fold stays comfortable: ~137px at 1440x900 (Monzo's is 127px). */
  top: 77%;
  bottom: auto;
}

/* ---------- 2. page margins ---------- */
body.home .container {
  max-width: 1440px;
  padding-inline: 88px;
}
/* the crest strip measures itself against the container (see --crest-w above),
   so it has to move with it or it stops lining up with the content */
body.home .crests__marquee {
  --crest-w: min(calc(1440px - 2 * 88px), calc(100vw - 2 * 88px));
}
@media (max-width: 900px) {
  /* Monzo drops to 24px gutters on narrow screens — measured on the same page */
  body.home .container { padding-inline: 24px; }
  body.home .crests__marquee { --crest-w: calc(100vw - 48px); }
}


/* The MODULAR BANDS block that was here has moved to styles/modular.css, which
   is loaded LAST on every page. It has to be: the section tints are set in
   gsap-trial.css, product-v3.css, onsite-v2.css and school-types.css, all of
   which load after this file, so an override here lost on both order and
   specificity (.hv2 .band--blog is 0,2,0 against a bare class). */


/* ============================================================
 * "FREE FOR LAs" AS A PRICE CARD (5 Aug 2026)
 * "can we style the 'free for las' module like the other price cards. keep the
 * header on the right in the navy." So it drops the blue .inclusive treatment and
 * takes the .pcard anatomy — white card, 24px radius, the same
 * `0 18px 44px rgba(15,20,56,0.12)` shadow, header bleeding to the rounded corner
 * — but with the header as a PANEL DOWN THE RIGHT instead of a band across the
 * top, because this card spans the whole row and a top band that wide is a lot of
 * navy. Her pick from three options.
 * The shadow matters more than it used to: the pricing band is white now, so the
 * card has nothing but the shadow to separate it from the page.
 * ============================================================ */
.inclusive--price {
  background: #fff;
  border-radius: var(--tile-radius, 24px);
  overflow: hidden;                    /* the navy panel bleeds to the corners */
  box-shadow: 0 18px 44px rgba(15, 20, 56, 0.12);
  padding: 0;                          /* the two halves carry their own padding */
  display: grid;
  /* navy panel LEFT, detail and actions right — she flipped it, and it reads
     better: identifier → detail → actions, left to right, mirroring the order the
     three cards above read top to bottom. */
  /* 30%, not an fr fraction: "Free for LAs & MATs." at 28px needs ~350px with the
     panel's padding, and a 0.36fr column resolved to only ~230px, which wrapped
     the title onto two lines in a one-line card. */
  grid-template-columns: minmax(260px, 30%) minmax(0, 1fr);
  align-items: stretch;
  min-height: 0;
}
.inclusive--price .inclusive__body {
  max-width: none;
  padding: clamp(26px, 3vw, 44px) clamp(28px, 3.2vw, 48px);
  display: flex;
  align-items: center;
  /* the lead and the actions sit side by side while there is room, then stack —
     the buttons are the reason this card is taller than a one-line strip now */
  flex-wrap: wrap;
  justify-content: space-between;
  gap: clamp(16px, 2vw, 32px);
}
/* the two buttons are .pcard__cta and .pcard__more, the same components the price
   cards use — so they inherit those styles and cannot drift from them. Only the
   layout is set here: .pcard__cta is full-width inside a narrow card, which is
   wrong on a wide row. */
.inclusive--price .inclusive__actions {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.6vw, 26px);
  flex-wrap: wrap;
}
.inclusive--price .pcard__cta { width: auto; margin-top: 0; }
.inclusive--price .pcard__more { margin-top: 0; }
.inclusive--price .inclusive__lead {
  margin: 0;
  color: var(--ge-navy, #0F1438);
  font-size: clamp(18px, 1.8vw, 26px);
  font-weight: 600;
}
/* the navy header panel */
.inclusive--price .inclusive__head {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--ge-navy, #0F1438);
  padding: clamp(24px, 2.8vw, 40px) clamp(26px, 3vw, 40px);
  display: flex;
  align-items: center;
}
.inclusive--price .inclusive__title {
  position: relative;
  z-index: 1;
  margin: 0;
  color: #fff;
  font-size: clamp(20px, 2vw, 30px);
}
/* The hexagon watermark that was here is gone (5 Aug, her call): the navy panel is
   only ~30% of a wide card, and at any size the mark read as crowding the title
   instead of sitting behind it. The rules are removed rather than commented out —
   .inclusive__mark still exists for the base .inclusive component elsewhere. */

@media (max-width: 760px) {
  /* stacked, the header goes on TOP — which is exactly where it sits on the three
     price cards, so the mobile layout is the closest match of all */
  .inclusive--price { grid-template-columns: 1fr; }
  /* stacked, the panel is already first in the markup, so it lands on top — which
     is exactly where the header sits on the three price cards */
  .inclusive--price .inclusive__body { justify-content: flex-start; }
  .inclusive--price .inclusive__mark { right: -14%; bottom: -40%; width: 46%; }
}


/* ---------- the scroll controls (5 Aug) ----------
 * Monzo puts a small control cluster under the track. Ours is prev/next only —
 * they show progress dashes because they have no other indicator, whereas our four
 * logo tabs sit right above and already say where you are. Two indicators for one
 * position would just disagree with each other.
 * The arrows exist for anyone who will not think to drag: trackpad swiping is
 * invisible affordance-wise, so a visible control matters.
 * ---------------------------------------------------------------- */
.pstnav {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: clamp(16px, 1.8vw, 24px);
}
.pstnav__btn {
  appearance: none;
  /* 44px, the site's touch-target floor */
  width: 44px;
  height: 44px;
  border-radius: 999px;
  border: 1px solid #D7DFEA;
  background: #fff;
  color: var(--ge-navy, #0F1438);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: background 200ms var(--hv2-ease), border-color 200ms var(--hv2-ease),
              opacity 200ms var(--hv2-ease);
}
.pstnav__btn svg { width: 20px; height: 20px; }
.pstnav__btn:hover { background: var(--ge-blue-3, #D9F4FF); border-color: var(--ge-blue-3, #D9F4FF); }
.pstnav__btn:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(47,183,241,0.45); }
/* dimmed rather than hidden at the ends, so the control cluster does not reflow */
.pstnav__btn[disabled] { opacity: 0.32; cursor: default; }
.pstnav__btn[disabled]:hover { background: #fff; border-color: #D7DFEA; }
@media (prefers-reduced-motion: reduce) { .pstnav__btn { transition: none; } }


/* the upgrades tile's watermark — the same treatment as .final2__mark on the
   home page's blue CTA board: white at 11%, oversized, bleeding off the
   top-right, clipped by the tile's own radius. Injected via ::before rather than
   markup so all four pages carrying this tile get it without touching their HTML. */
.inclusive--device::before {
  content: "";
  position: absolute;
  top: clamp(-110px, -7vw, -50px);
  right: clamp(-110px, -7vw, -50px);
  width: clamp(360px, 40vw, 620px);
  aspect-ratio: 175 / 193;                 /* the mark's own ratio */
  background: url("../assets/images/brand/grouped-symbol-white.svg") no-repeat center / contain;
  opacity: 0.11;
  pointer-events: none;
  z-index: 0;
}
/* the copy sits above the watermark.
   ⚠ .inclusive__phone is DELIBERATELY not in this rule. It used to be, and
   `position: relative` cancelled its absolute positioning — the montage dropped
   into the flow, rendered at 29% of the tile and sat 488px short of the right
   edge, which is why it looked centred and small. Its z-index is set on its own
   rule below, where the positioning lives. */
.inclusive--device .inclusive__body { position: relative; z-index: 1; }

/* ⚠ MOVED HERE FROM styles/school-types.css ON 6 AUG 2026.
 * Rebecca: "role out that testimonial design across site". school-types.css only
 * loads on three pages; this file loads on all 30, so the component lives here
 * now. The JS that drives it is already in scripts/home-v2.js, which is also
 * site-wide, so no page needs a new script tag. */
/* ============================================================
 * TESTIMONIAL CAROUSEL  (6 Aug 2026)
 * Rebecca picked the pattern from a reference: eyebrow + two-line heading, round
 * prev/next buttons top-right, a snapping row of quote cards, dots underneath.
 *
 * ⚠ EVERY QUOTE IS REAL AND SUPPLIED BY HER. Two came from her directly (Matt
 * Gilbert, Barry Island Primary; Catrin Hornung, parent) and the Cwmbran one was
 * already live on this site. Nothing here is written or embellished — if a quote
 * needs changing, change it in the HTML, never "tidy" the wording.
 *
 * Native CSS scroll-snap, no library. The buttons and dots drive scrollLeft; with
 * JS off it is still a scrollable row, which is the whole point of doing it this
 * way rather than with absolute positioning.
 * ============================================================ */
.tcar { margin-top: clamp(8px, 1vw, 16px); }
.tcar__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: clamp(24px, 3vw, 40px);
}
.tcar__eyebrow {
  display: inline-block;
  margin-bottom: 14px;
  padding: 7px 16px;
  border-radius: 999px;
  background: var(--ge-blue-3, #D9F4FF);
  color: var(--ge-blue, #1B367E);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.tcar__h {
  margin: 0;
  font-size: clamp(26px, 2.9vw, 42px);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: var(--ge-navy, #0F1438);
}
.tcar__nav { display: flex; gap: 10px; flex: none; }
.tcar__btn {
  width: 44px; height: 44px;            /* a real 44px target, not a 32px circle */
  border-radius: 999px;
  border: 1.5px solid rgba(15, 20, 56, 0.16);
  background: #fff;
  color: var(--ge-navy, #0F1438);
  display: grid; place-items: center;
  cursor: pointer;
  transition: background 160ms var(--hv2-ease), border-color 160ms var(--hv2-ease), opacity 160ms;
}
.tcar__btn svg { width: 18px; height: 18px; }
.tcar__btn:hover { background: var(--ge-navy, #0F1438); border-color: var(--ge-navy, #0F1438); color: #fff; }
.tcar__btn:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(47, 183, 241, 0.45); }
.tcar__btn[disabled] { opacity: 0.35; cursor: default; }
.tcar__btn[disabled]:hover { background: #fff; border-color: rgba(15,20,56,0.16); color: var(--ge-navy,#0F1438); }

.tcar__track {
  list-style: none;
  margin: 0; padding: 0 0 4px;
  display: flex;
  gap: clamp(16px, 1.6vw, 24px);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  scroll-behavior: smooth;
}
.tcar__track::-webkit-scrollbar { display: none; }
.tcar__card {
  /* ⚠ THE MAX MATTERS. At 420 the three cards came to 1308px against a 1312px
     track — they fitted exactly, the row could not scroll, and the arrows and dots
     did nothing. 520 lets 38% resolve to ~498 at 1440, so three cards overflow and
     ~2.6 are visible: the row reads as scrollable, which is the point of the
     pattern. If a fourth quote is ever added, re-check this rather than assuming. */
  /* ⚠ 46%, and the width is doing real work. At 38% the three cards came to
     1542px in a 1312px track — only 230px of travel, LESS THAN HALF A CARD, so
     "next" jumped straight to the end and two of the three dots were unreachable.
     46% gives ~603px cards, a 1857px track and ~545px of travel, i.e. roughly one
     full card of movement with ~2 cards in view. With only three quotes that is
     the honest maximum; if more are added this can come back down. */
  flex: 0 0 clamp(260px, 46%, 620px);
  scroll-snap-align: start;
  background: #fff;
  border: 1px solid #E6ECF4;
  border-radius: 24px;
  padding: clamp(24px, 2.4vw, 34px);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: clamp(20px, 2.4vw, 32px);
  min-height: 0; min-width: 0;
}
.tcar__card blockquote {
  margin: 0;
  font-size: clamp(16px, 1.35vw, 18.5px);
  line-height: 1.6;
  color: var(--ge-navy, #0F1438);
}
.tcar__by { display: flex; align-items: center; gap: 14px; margin: 0; }
.tcar__crest {
  flex: none;
  width: 46px; height: 46px;
  object-fit: contain;
  border-radius: 10px;
  background: #fff;
}
/* people with no crest get their initial rather than a stock avatar */
.tcar__initial {
  flex: none;
  width: 46px; height: 46px;
  border-radius: 10px;
  display: grid; place-items: center;
  background: var(--ge-blue-3, #D9F4FF);
  color: var(--ge-blue, #1B367E);
  font-weight: 700;
  font-size: 18px;
}
.tcar__name { display: block; font-weight: 600; font-size: 15.5px; color: var(--ge-navy, #0F1438); }
.tcar__role { display: block; font-size: 13.5px; color: #4A5876; margin-top: 2px; }

.tcar__dots { display: flex; justify-content: center; gap: 0; margin-top: clamp(18px, 2vw, 26px); }
.tcar__dot {
  width: 24px; height: 24px;            /* 24px target, 8px visible dot */
  border: 0; background: none; padding: 0;
  display: grid; place-items: center;
  cursor: pointer;
}
.tcar__dot::before {
  content: ""; width: 8px; height: 8px; border-radius: 999px;
  background: rgba(15, 20, 56, 0.22);
  transition: background 160ms var(--hv2-ease), width 160ms var(--hv2-ease);
}
.tcar__dot:hover::before { background: rgba(15, 20, 56, 0.42); }
.tcar__dot.is-on::before { width: 26px; background: var(--ge-navy, #0F1438); }
.tcar__dot:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(47, 183, 241, 0.45); border-radius: 999px; }

@media (max-width: 700px) {
  .tcar__head { flex-direction: column; align-items: flex-start; }
  .tcar__nav { display: none; }         /* on a phone you swipe; arrows are clutter */
  .tcar__card { flex: 0 0 86%; }        /* a sliver of the next card shows the row moves */
}
@media (prefers-reduced-motion: reduce) {
  .tcar__track { scroll-behavior: auto; }
}

/* ---- solo variant (6 Aug 2026) ----------------------------------------
 * Classroom holds exactly one testimonial. At the normal 46% a lone card sits in
 * a half-empty row and reads as a carousel that has lost its other slides, so the
 * single card takes the full track instead. No arrows, no dots — home-v2.js bails
 * out at cards.length < 2 and hides the nav on its own.
 * Capped at 1000px because a quote set the full 1312px width is unreadable —
 * it runs past the ~75-character line the rest of the site's body copy holds to. */
.tcar--solo .tcar__track { overflow: visible; }
.tcar--solo .tcar__card { flex: 1 1 auto; max-width: min(1000px, 100%); }
.tcar--solo .tcar__card blockquote { font-size: clamp(19px, 1.9vw, 26px); }

/* ---- OnSite's proof numbers, lifted out of the old quote card ----------
 * These used to sit inside .os-quote__stats on the card that the carousel
 * replaced. They are page-level proof, not part of any one school's quote, so
 * they now sit under the row. The odometers still animate — scripts/home-v2.js
 * finds [data-odo] wherever it is. */
.os-says__stats {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(20px, 3vw, 56px);
  margin-top: clamp(26px, 3vw, 40px);
  padding-top: clamp(22px, 2.4vw, 32px);
  border-top: 1px solid #E6ECF4;
  font-size: 15px;
  color: #4A5876;
}
.os-says__stats strong {
  display: block;
  font-weight: 700;
  font-size: clamp(26px, 2.8vw, 38px);
  line-height: 1.05;
  color: var(--ge-navy, #0F1438);
  margin-bottom: 4px;
}
