/* ============================================================
 * APPLE LANDING PAGE — apple.html
 *
 * A BRAND page, not a product page: it spans One, OnSite, Classroom and the
 * Parent & Carer app, so it leads with the PRIMARY palette (navy / sky / blue)
 * exactly like the three school-type pages do. No product identity colour leads
 * here; product colour appears only on the app tiles.
 *
 * ⚠ LOADS BEFORE modular.css, like every other page stylesheet. modular.css has
 * to stay last or every section on this page keeps whatever tint it was given.
 *
 * Everything structural is borrowed rather than re-declared:
 *   · the hero is .hero2 (home-v2.css) with an --apple variant for its copy stack
 *   · section padding, card insets and the 64px radius come from modular.css
 *   · .container, .btn and the eyebrow family come from foundation.css
 * Only the page's own components live here, all prefixed .ap-.
 * ============================================================ */

/* ------------------------------------------------------------
 * EYEBROW
 * CLAUDE.md specifies 15px / 10px 22px / 0.12em, but nothing on the live site
 * is built that way: .ml-eyebrow, .hero2__eyebrow, .pp-eyebrow and .st-eyebrow
 * are all 13px / 700 / 0.1em with 8-9px x 16-18px padding. Matched to what
 * ships, so a visitor arriving here from any other page sees one component.
 * Navy pill on light, white pill on dark, one per section.
 * ------------------------------------------------------------ */
.ap-eyebrow {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  gap: 9px;
  margin-bottom: 4px;
  padding: 8px 16px;
  border-radius: 999px;
  background: var(--ge-navy, #0F1438);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.ap-eyebrow--on-dark { background: #fff; color: var(--ge-navy, #0F1438); }

/* ---- SECTION HEADS SIT ON THE PAGE'S LEFT EDGE ----
 * They were centred until 7 Oct. Changed after measuring this page against
 * monzo.com, which is where this site's whole layout system came from:
 * modular.css records that their body text and section headings, when not inside
 * a card, sit at 64px — the single most common left edge on their page, the same
 * line as their card edges, and every large heading outside a card is on it.
 *
 * Ours were centred while everything beneath them — tiles, cards, the case study,
 * the banner — ran to a hard left edge, so the page had two alignment systems and
 * the headings floated free of the content they introduced. One spine now.
 *
 * ⚠ The Next step CTA is deliberately still centred (.ap-cta). It is a closing
 * call to action with a centred row of buttons, not a section head.
 *
 * The wider measures live here too: the shared .bandhead caps h2 at 24ch, which
 * breaks "Three moments in the school day, three Apple devices" badly. */
.ap-head {
  align-items: flex-start;
  text-align: left;
  margin-inline: 0;
  /* ⚠ tighter than the shared .bandhead's 12px. A heading at this size carries a
     lot of air inside its own line box, so an even gap below it reads as a bigger
     gap than the number suggests. */
  gap: 7px;
}
.ap-head h2 { max-width: 30ch; margin-inline: 0; }
.ap-head p { max-width: 58ch; margin-inline: 0; }
.ap-head .ap-eyebrow { align-self: flex-start; }

/* ---- ONE SCALE MOMENT PER PAGE ----
 * Every H2 on this page was the same clamp(32-54px), so nothing was a landmark
 * and the eye had no reason to stop. home-v2.css has the principle already:
 * "once per page, go to 150px+ with a single sentence... oversized restraint
 * reads as confidence". This is the page's one use of it, on the Apple
 * integrations head.
 * ⚠ Ties with `.hv2 h2` at (0,1,1) and wins only because apple-landing.css loads
 * after home-v2.css. Do not move this file earlier in the <head>. */
.ap-head--display h2 {
  font-size: clamp(38px, 6vw, 84px);
  line-height: 1.02;
  letter-spacing: -0.03em;
  max-width: 22ch;
}

/* arrow link in running text — the site's .parents__link recipe. modular.css
   underlines bare prose links for SC 1.4.1; these carry a class and an arrow,
   which is the non-colour cue, so they are deliberately not underlined. */
.ap-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--ge-blue, #1B367E);
  font-weight: 600;
  font-size: 15.5px;
  text-decoration: none;
}
.ap-link:hover { color: var(--ge-navy, #0F1438); }
/* ⚠ The site applies the sky focus ring per component, not globally (see
   .nv__seg, .mm__tile, .btn in foundation.css), so a new component has to ask
   for it. Without this .ap-link fell back to the browser's own 1px outline:
   visible, but not the documented ring. Offset so the ring clears the glyph. */
.ap-link:focus-visible {
  outline: none;
  border-radius: 6px;
  box-shadow: 0 0 0 3px rgba(47, 183, 241, 0.45);
  outline-offset: 2px;
}
.ap-link svg { width: 18px; height: 18px; transition: transform 200ms var(--hv2-ease); }
.ap-link:hover svg { transform: translateX(4px); }

/* ------------------------------------------------------------
 * 1. HERO — SPLIT: solid navy half, photo half (Rebecca, 5 Oct 2026)
 *
 * Not the site's usual photo-hero-with-a-scrim. The frame becomes a two-column
 * grid inside the same inset 64px card: copy on a solid navy left half, the
 * reception photograph filling the right. The frame's own overflow: hidden is
 * what rounds the photo into the card's right-hand corners.
 *
 * ⚠ #253A7D — THE HOME PAGE HERO BLUE (Rebecca, 6 Oct). home-personality.css
 * records where it came from: sampled from her Illustrator artwork for the home
 * hero, and used there as the fill behind it. Written literally rather than as a
 * var, because that stylesheet is scoped to .hero2--graphic and is not loaded on
 * this page, so there is no token here to read.
 * Lighter and bluer than the navy this started as, and still comfortably legible:
 * white on it measures 10.6:1.
 * ⚠ If the home hero blue ever changes, this will NOT follow it automatically.
 * ------------------------------------------------------------ */
/* ⚠ NOT 1fr 1fr. Rebecca, 5 Oct: "move the photo over to the left more" — the
   divide sits left of centre so the photograph takes the larger share. At 1440
   that is a 577px blue half against 735px of photo. Any further left and the
   headline starts breaking two words per line. */
.hero2--apple .hero2__frame {
  display: grid;
  grid-template-columns: 0.88fr 1.12fr;
  align-items: stretch;
  background: #253A7D;
  min-height: max(540px, 70svh);
}
/* the photo comes OUT of the absolute overlay the base class puts it in and
   becomes an ordinary grid cell, so it owns its half rather than the whole card */
/* ⚠ BACK TO CENTRE, 6 Oct, and the reason is the new photograph's shape.
   This was 85% because the old hero source was 2100x980 (2.14:1) against a column
   of roughly 735x630 (1.17:1): cover discarded ~615px of width, and centring that
   cut straight through the iPad kiosk, so the window had to be pushed right.
   The replacement is 1600x1194 (1.34:1), which is very close to the column's own
   shape — only ~109px of width is discarded, about 13%. There is nothing to
   rescue, and 85% would now shove the receptionist out of frame. */
.hero2--apple .hero2__photo {
  position: static;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 48%;
}
.hero2--apple .hero2__scrim { display: none; }   /* nothing sits on the photo now */

.hero2--apple .hero2__copy {
  position: relative;
  display: flex;
  align-items: center;             /* copy centres vertically against the photo */
  max-width: none;
  padding: clamp(30px, 4.2vw, 76px);
}
/* ⚠ NO .hero2__wm ON THIS HERO. Rebecca, 5 Oct: "get rid of the icon logo water
   mark". The ghost hexagon every other hero carries is gone from the markup, not
   just hidden, so nothing downloads for it. The navy half is plain colour. */
.hero2--apple .ap-hero__inner {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-5, 20px);
  width: 100%;
}
/* half the width means half the headline. The shared clamp tops out at 82px,
   which is a full-bleed number and would break this one word per line. */
.hero2--apple .hero2__copy h1 { font-size: clamp(33px, 3.5vw, 54px); line-height: 1.1; }
.hero2--apple .hero2__lede { max-width: none; font-size: clamp(16px, 1.25vw, 18.5px); }
.hero2--apple .ap-hero__actions { display: flex; flex-wrap: wrap; gap: 14px; }

/* ---- accreditation tiles ----
 * The shared .hero2__badges component from home-v2.css, re-anchored. On the home
 * Pinned to the hero frame's bottom-right, over the photograph, exactly as on the
 * home page. The base rule in home-v2.css does all the work; all this adds is a
 * z-index, so the strip sits above the photo in the frame's stacking order.
 * ⚠ The shared rule hides them below 960px and that is kept: on a stacked hero the
 * copy owns the card, which is the same call the home page makes. */
/* ⚠ scoped to .hero2--apple so the home page's own strip is untouched: the shared
   component sits at 34-44px tall logos in 10x16px tiles, and the home hero is
   measured against Monzo's. These are bigger by request, 6 Oct. */
.hero2--apple .hero2__badges { z-index: 3; gap: 12px; }
.hero2--apple .hero2__badge { padding: 13px 20px; border-radius: 16px; }
.hero2--apple .hero2__badge img { height: clamp(44px, 4vw, 60px); }
/* white ghost secondary on photography, the .st-hero__ghost recipe */
.ap-hero__ghost.btn {
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.55);
  font-weight: 700;
  padding-inline: 28px;   /* symmetric: no arrow chip to reserve space for */
}
.ap-hero__ghost.btn:hover { background: rgba(255, 255, 255, 0.22); color: #fff; }

/* ---- the three hero facts ----
 * ⚠ NOT PILLS. Rebecca, 5 Oct: "all of these buttons dont need pills in the
 * hero". They read as three tappable chips when they are plain statements of
 * fact, and a hero should have exactly two things that look clickable: the two
 * buttons above them. A plain tick list instead.
 * White disc, navy glyph: the sanctioned inversion of the site's navy tick for
 * a dark surface. */
.ap-hero__ticks {
  display: flex;
  flex-direction: column;
  gap: 11px;
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
}
.ap-hero__ticks li {
  display: flex;
  align-items: center;
  gap: 11px;
  color: #fff;
  font-size: 15.5px;
}
.ap-hero__ticks svg {
  width: 19px;
  height: 19px;
  flex: none;
  padding: 4px;
  border-radius: 50%;
  background: #fff;
  color: var(--ge-navy, #0F1438);
}

/* ------------------------------------------------------------
 * SHARED CARD — white, 24px, soft navy shadow, no stroke
 * (.st-card, which is the site's current card after the hairline strokes came
 * off; these sit on white sections so the shadow is what separates them)
 * ------------------------------------------------------------ */
.ap-card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: var(--radius-xl, 24px);
  box-shadow: 0 8px 24px rgba(15, 20, 56, 0.08);
  padding: clamp(24px, 2.4vw, 34px);
  transition: transform 220ms var(--hv2-ease), box-shadow 220ms var(--hv2-ease);
}
.ap-card:hover { transform: translateY(-6px); box-shadow: 0 22px 48px rgba(15, 20, 56, 0.12); }
.ap-card h3 { margin: 0 0 10px; font-size: 19px; color: var(--ge-navy, #0F1438); }
.ap-card p { margin: 0; font-size: 15px; line-height: 1.6; color: #4A5876; }
.ap-card .ap-link { margin-top: 18px; }

/* ---- the device cards' product links are buttons now (Rebecca, 7 Oct) ----
 * The site's own .btn .btn--ghost does the work: pill, navy ghost on white, which
 * is the documented secondary button. All this adds is the layout — several sit
 * together at the foot of a card, so they need to wrap and sit on a row rather
 * than stack full width.
 * ⚠ The case study's "Read the full case study" is deliberately NOT one of these.
 * It sits inside running prose, where a pill would read as an interruption; it
 * stays an inline arrow link. */
.ap-card__btns { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }
.ap-card__btn.btn {
  padding: 9px 18px;
  font-size: 14.5px;
  font-weight: 600;
  gap: 8px;
  /* ⚠ 44px is the accessibility floor for a touch target (CLAUDE.md), and the
     padding above only gets these to 42 — they are the smallest buttons on the
     page because several sit together on one card row. min-height does the last
     2px without widening them or changing the type size. Measured at 375px. */
  min-height: 44px;
}
.ap-card__btn.btn svg { width: 15px; height: 15px; transition: transform 200ms var(--hv2-ease); }
.ap-card__btn.btn:hover svg { transform: translateX(3px); }

/* icon tile — 12-14px radius, Feather at 22px, navy glyph on the pale sky tint */
.ap-ico {
  width: 48px;
  height: 48px;
  flex: none;
  border-radius: 13px;
  display: grid;
  place-items: center;
  background: var(--ge-blue-3, #D9F4FF);
  color: var(--ge-blue, #1B367E);
  margin-bottom: 16px;
  transition: transform 220ms var(--hv2-ease);
}
/* ⚠ .ap-tile is NOT in this list any more: with its card gone nothing about a
   tile responds to the pointer, so a hover tilt would be the only motion on an
   element you cannot click. */
.ap-card:hover .ap-ico { transform: rotate(-8deg); }   /* the site "handshake" micro-tilt */
.ap-ico svg { width: 22px; height: 22px; }

/* ------------------------------------------------------------
 * 2. HOW IT WORKS — three devices, three moments
 * ------------------------------------------------------------ */
.ap-grid3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 1.8vw, 24px);
}
.ap-grid2 {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(16px, 2vw, 28px);
}
/* ---- photo-topped variant of .ap-card (the three How it works cards) ----
   Same card, same shadow, same hover: the photograph replaces the icon tile
   rather than sitting beside it. Padding moves off the card and onto the body
   so the picture runs edge to edge into the 24px corners. */
.ap-card--photo { padding: 0; overflow: hidden; }
.ap-card--photo .ap-card__shot {
  aspect-ratio: 16 / 10;
  background: var(--ge-bg-light, #F3F7FA);
  overflow: hidden;
}
.ap-card--photo .ap-card__shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* ⚠ per-image crop override, same idea as .ap-app__shot--lower further down.
   onsite-reception-signin.jpg is 2100x980 (2.14:1) in a 16:10 window, so cover
   discards about a quarter of its width. Centred, that took ~262px off each side
   and clipped the right edge of the iPad kiosk, which runs to x=1950 of 2100.
   75% slides the window right to roughly x=397-1968: the kiosk is whole and the
   loss comes off the empty left of the room instead. */
.ap-card__shot--right img { object-position: 75% center; }
.ap-card--photo .ap-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: clamp(24px, 2.4vw, 32px);
}

/* "At the door · iPad" — a tag line above the heading. Middle dot, per the
   brand's microcopy pattern; it is the only decorative character the system
   uses, and it is deliberately not an em dash. */
.ap-card__step {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ge-blue, #1B367E);
}

/* ------------------------------------------------------------
 * 3. AUTOMATIC SIGN-IN — copy and one screenshot
 *
 * Third design for this section. The pale module with the phone inside pulsing
 * rings went first, then the navy card with a numbered walk-in sequence; both
 * were too busy for what is, in the end, two paragraphs and four facts.
 * Rebecca picked this: a plain white section, the copy beside the product, the
 * four benefits on one line underneath. No card, no module, no sequence.
 *
 * ⚠ IT IS NOW A WHITE SECTION, so this is the page's flagship claim sitting on
 * the same surface as the eight sections around it. That is the trade she chose
 * knowingly. If it ever needs to carry more weight again, .band--navy on the
 * <section> and inverting the tick discs is the whole change.
 * ------------------------------------------------------------ */
/* ⚠ TWO COLUMNS OF TEXT, NO IMAGE (Rebecca, 6 Oct). Third layout for this
   section and the simplest: the heading block on the left, the two paragraphs on
   the right, the four benefits as a full-width row underneath.
   What it replaces and why: a 1:2 split with a device render in the wide column
   put every word in a ~380px column, which ran to roughly double the height of
   the image beside it and left a lot of empty blue. Splitting the text itself
   across the width balances by construction, because both columns hold copy.
   ⚠ align-items: start, not centre — the two columns hold different amounts, and
   centring them against each other floats the heading away from the top edge. */
/* ⚠ NOT A TWO-COLUMN SPLIT ANY MORE (Rebecca, 7 Oct). The heading sat in a left
   column and the prose in a right one, which left the bottom half of the heading
   column empty — a heading is two lines, the prose is nine. The head is centred
   now like every other section on the page, and the two detail paragraphs sit
   under it as a pair.
   ⚠ The paragraphs keep LEFT-aligned text inside a centred block. The head above
   them is centre-aligned, but centring nine lines of body copy makes every line
   start in a different place and is markedly harder to read; the brand's own type
   guidance puts hierarchy in size and spacing rather than in tricks like that.
   Change `text-align` below if you want them fully centred. */
.ap-sign { display: block; }

/* ⚠ COPY ONE SIDE, PICTURE THE OTHER (7 Oct). Third layout for this section, and
   the reason for it: the section describes a MOMENT — someone walks past a door
   and is signed in before they reach the staffroom — and we were describing it
   entirely in prose, with no image anywhere in the section.
   ⚠ This is not the split that was tried and rejected. That one put the heading in
   one column and the prose in the other, so the heading column ran out after two
   lines and left a hole. All the text is one column now, so both sides fill.
   The gaps still grow as the relationship weakens: heading -> lede 28px, between
   paragraphs 18px, copy -> tiles 64px. */
.ap-sign {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
}
.ap-sign .bandhead { margin-bottom: clamp(20px, 2.2vw, 28px); }
.ap-sign__copy p { margin: 0 0 18px; font-size: 16px; line-height: 1.7; color: #4A5876; max-width: 54ch; }
.ap-sign__copy p:last-child { margin-bottom: 0; }
/* the "single capability" line opens the copy: larger and navy, so it reads as a
   lead-in rather than as a third body paragraph */
.ap-sign__lead {
  font-size: clamp(17.5px, 1.5vw, 20px) !important;
  line-height: 1.55 !important;
  color: var(--ge-navy, #0F1438) !important;
}

/* ---- the picture side ----
 * ⚠ A PHOTOGRAPH NOW, not the phone render on a tint (Rebecca, 7 Oct). The
 * section describes a moment — someone walks past the desk and is signed in
 * before they reach the staffroom — and this is that moment: a member of staff
 * crossing reception, phone in hand, not stopping. A screenshot could only ever
 * show the result; the photograph shows the thing itself.
 * So the tinted ground goes with it: a photo fills its own frame and does not
 * need a panel behind it. */
.ap-sign__media {
  border-radius: var(--radius-xl, 24px);
  overflow: hidden;
}
/* ⚠ 4:3, which is almost exactly the source's own 1200x896, so the crop is
   negligible and nobody loses an arm. object-position holds the walking figure
   just right of centre where she sits in the frame. */
.ap-sign__media img {
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: 55% center;
}
/* ---- THE FOUR BENEFITS AS COLOURED TILES ----
 * Rebecca, 7 Oct: "put these in different tiles and make them different colours,
 * something like we did on the home page". This is the home page's .roi card,
 * selector for selector: 24px radius, 48px icon tile at 14px radius, the same
 * padding, the same lift on hover, and the same tint/icon pairs.
 * ⚠ The home page runs three tints and makes its fourth card navy (or white, on
 * the blue band). Neither works here: this section is white now, so a white tile
 * is invisible and a navy one would reintroduce the dark block she has just taken
 * out. Yellow is the fourth, from the same tint-2 scale as the other three.
 * ⚠ Tints are the -2 step of each scale, which is what the brand reserves for
 * washes and card grounds; the icon tile takes the -3/-4 step of the same hue. */
.ap-points {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(16px, 1.8vw, 26px);
  margin: clamp(40px, 4.2vw, 64px) 0 0;   /* clearly more than the gaps inside the copy */
  padding: 0;
  list-style: none;
  align-items: stretch;
}
.ap-point {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-xl, 24px);
  padding: clamp(22px, 2.2vw, 32px);
  transition: transform 220ms var(--hv2-ease), box-shadow 220ms var(--hv2-ease);
}
/* scoped like the home page's, to outrank the .rv reveal that pins transform */
.ap-points .ap-point:hover { transform: translateY(-6px); box-shadow: 0 22px 48px rgba(15, 20, 56, 0.16); }
.ap-point__ico {
  width: 48px;
  height: 48px;
  border-radius: 14px;
  display: grid;
  place-items: center;
  margin-bottom: 18px;
}
.ap-point__ico svg { width: 22px; height: 22px; }
.ap-point strong {
  display: block;
  margin-bottom: 8px;
  font-size: 17px;
  font-weight: 600;
  color: var(--ge-navy, #0F1438);
}
.ap-point__body { font-size: 14.5px; line-height: 1.55; color: #2A3352; }

.ap-point--sky    { background: var(--ge-blue-3, #D9F4FF); }
.ap-point--sky    .ap-point__ico { background: var(--ge-sky, #2FB7F1); color: #fff; }
.ap-point--green  { background: var(--ge-green-2, #E6F3D9); }
.ap-point--green  .ap-point__ico { background: var(--ge-green-3, #8ACB55); color: var(--ge-navy, #0F1438); }
.ap-point--violet { background: var(--ge-violet-2, #E8E4F9); }
.ap-point--violet .ap-point__ico { background: var(--ge-violet-4, #614DBA); color: #fff; }
.ap-point--yellow { background: var(--ge-yellow-2, #F9EFD0); }
.ap-point--yellow .ap-point__ico { background: var(--ge-yellow-3, #E8BA19); color: var(--ge-navy, #0F1438); }

/* ---- ON THE NAVY CARD ----
 * The section carries .band--navy, so modular.css insets it 64px with a 64px
 * radius and it becomes one of the page's dark cards again. Everything below is
 * the colour flip that has to come with it. Scoped to .band--navy on purpose:
 * removing that one class from the <section> returns the whole section to the
 * white version, with no other edit.
 * ⚠ .bandhead h2 and p are already handled by home-v2.css. */

/* ------------------------------------------------------------
 * 4. APPLE INTEGRATIONS — nine tiles
 * ------------------------------------------------------------ */
/* ⚠ NO CARD CHROME. These nine were the heaviest block on the page and carry its
   lightest content: an icon, a heading and one line each. As shadowed cards they
   made five of the twelve sections look identical, and the page read as one long
   card wall (22 cards in total before this). Stripped back to plain content on
   white, with the space doing the separating — which is also what Monzo does:
   their chrome lives in about six places on a page, not twenty-two.
   The gap is deliberately much wider than a card grid's, because there is no
   longer a card edge marking where one tile stops and the next begins. */
.ap-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(30px, 3.6vw, 56px) clamp(24px, 3vw, 46px);
}
.ap-tile { background: none; box-shadow: none; padding: 0; }
.ap-tile h3 { margin: 0 0 8px; font-size: 17.5px; color: var(--ge-navy, #0F1438); }
.ap-tile p { margin: 0; font-size: 14.5px; line-height: 1.55; color: #4A5876; }

/* ------------------------------------------------------------
 * 5. WHERE WE ARE TODAY — the navy stats card
 * Big display numbers keep 700/800: they are stats, not headings, and the
 * SemiBold-600 heading rule deliberately does not reach them.
 * ------------------------------------------------------------ */
/* centred in the tile (Rebecca, 5 Oct). The heading takes .ap-head--centre and
   the four stats centre their own column, so the whole card reads as one
   symmetrical block rather than a left-aligned list on a wide dark surface. */
.ap-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(24px, 3vw, 44px);
  margin-top: clamp(28px, 3.4vw, 44px);
  text-align: center;
}
.ap-stat__key { margin-inline: auto; max-width: 26ch; }
/* ⚠ inline-flex, and NO letter-spacing. Both are odometer requirements.
   scripts/home-v2.js turns a [data-odo] span into stacked digit columns and adds
   .odo, which is `display: inline-flex; letter-spacing: normal`. This file loads
   after home-v2.css, so a `display: block` or a negative letter-spacing here wins
   the tie and breaks it: block kills the baseline alignment of the digits, and
   home-v2.css records that -0.02em squeezes the fixed-width digit boxes and clips
   the last glyph of values like "100+". */
.ap-stat__num {
  display: inline-flex;
  align-items: baseline;
  font-size: clamp(40px, 4.4vw, 64px);
  font-weight: 800;
  line-height: 1;
  color: var(--ge-sky, #2FB7F1);   /* large display type: sky clears AA at this size on navy */
}
.ap-stat__key {
  display: block;
  margin-top: 12px;
  font-size: 15.5px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.86);
}

/* ------------------------------------------------------------
 * 6. SEE IT WORKING — the demo video slot
 * ------------------------------------------------------------ */
/* ⚠ FULL CONTENT WIDTH. Rebecca, 15 Oct: "lets make the video bigger though to
   fit the screen so its the width as other sections of the site."
   ⚠ THIS REVERSES AN EARLIER CALL, DELIBERATELY — do not put the cap back by
   reading the old note as current. It was capped to 860px on 5 Oct, when
   Rebecca said "the product video doesnt need to be so big". That was correct
   then and is wrong now, and the thing that changed is what is inside the
   frame: on 5 Oct this was an empty placeholder, so full width meant a 738px
   navy slab with a play icon floating in it. Since 15 Oct there is a real film
   in it. A big empty box and a big film are not the same object.
   So it now matches every other section: 1312px at 1440, same as .ap-tiles,
   .ap-grid3, .ap-sign and .ap-case, which is what "the width as other sections"
   means on this page. 16:9 is kept, so that is 738px tall.
   ⚠ 1500 IS THE CONTENT COLUMN'S OWN CEILING, MEASURED, NOT A ROUND NUMBER
   PICKED HERE. .container has `max-width: none` on this page, so it is always
   as wide as the viewport — but its padding GROWS with the viewport (64px at
   1440, 90px at 1680, 210px at 1920, 450px at 2400), and the content inside it
   therefore stops widening at 1500px from about 1680 up. Matching that number
   means the film is exactly as wide as the tiles and cards at every screen
   size, and 844px tall at its biggest, which fits the screens wide enough to
   trigger it.
   ⚠ An earlier version of this note said .container was uncapped and that the
   film would otherwise reach 1856px on a 1920 screen. That was wrong — it
   confused the container's width with its content width. The padding is what
   does the capping. */
.ap-video {
  position: relative;
  aspect-ratio: 16 / 9;
  width: 100%;
  max-width: 1500px;
  margin-inline: auto;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 24px;
  border-radius: var(--radius-xl, 24px);
  /* ⚠ BLUE, not navy. Three navy cards already run down this page, and a fourth
     navy rectangle here read as another one of them rather than as a player.
     #1B367E is the palette's Dark Blue: clearly a different surface, still the
     primary family, and white on it clears AA comfortably at 11.4:1. Sky was the
     other candidate and is ruled out — white text on #2FB7F1 fails. */
  background: var(--ge-blue, #1B367E);
  overflow: hidden;
}
.ap-video__wm {
  position: absolute;
  right: -6%;
  bottom: -30%;
  width: 34%;
  opacity: 0.07;
  pointer-events: none;
}
/* ⚠ id strength, same trap as the two other watermarks on this page: home-v2.js
   auto-applies .rv to elements and `.rv.in { opacity: 1 }` (0,2,0) beats a bare
   class, so the watermark jumps to fully opaque the moment it scrolls into view.
   It only shows up AFTER scrolling, which is what made it so easy to miss. */
#see-it-working .ap-video__wm { opacity: 0.07; }

/* ---- the film itself ----
 * The <video> fills the frame and the poster is what you see until play. The
 * frame keeps its 16:9 and its radius, so the resting state looks exactly like
 * the placeholder this replaced — only now there is a film behind it. */
.ap-video__el {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;   /* the frame's corners, or the video squares them off */
  background: var(--ge-blue, #1B367E);
}
/* ⚠ A scrim, because the play button and the caption sit over a PHOTOGRAPH now,
   not over flat blue. White on the poster's bright classroom wall is about
   2.4:1 without it. At 0.46 the text clears AA against every frame of the
   poster, and the picture still reads underneath. */
.ap-video__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  background: linear-gradient(180deg, rgba(15, 20, 56, 0.34) 0%, rgba(15, 20, 56, 0.54) 100%);
  transition: opacity 360ms var(--hv2-ease);
}
.ap-video__inner {
  position: relative;
  z-index: 2;
  transition: opacity 360ms var(--hv2-ease);
}
.ap-video__play {
  width: 74px;
  height: 74px;
  margin: 0 auto 20px;
  border: 0;
  padding: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: #fff;
  color: var(--ge-navy, #0F1438);
  cursor: pointer;
  transition: transform 200ms var(--hv2-ease), background 200ms var(--hv2-ease);
}
.ap-video__play:hover { transform: scale(1.06); background: var(--ge-blue-3, #D9F4FF); }
.ap-video__play:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(47, 183, 241, 0.45), 0 0 0 6px rgba(15, 20, 56, 0.35);
}
.ap-video__play svg { width: 26px; height: 26px; margin-left: 4px; }
.ap-video__title { margin: 0 0 8px; font-size: clamp(20px, 2vw, 26px); font-weight: 600; color: #fff; }
.ap-video__inner p { margin: 0; font-size: 15.5px; color: rgba(255, 255, 255, 0.86); }

/* ---- playing: the cover gets out of the way ----
 * Hidden with opacity AND pointer-events rather than display:none, so the fade
 * can run; visibility stops the button staying in the tab order once gone. */
.ap-video.is-playing .ap-video__scrim,
.ap-video.is-playing .ap-video__inner {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .ap-video__scrim,
  .ap-video__inner,
  .ap-video__play { transition: none; }
}

/* The no-JS fallback inside <video> — only ever seen if the element itself
   cannot play, since the script is what reveals controls. */
.ap-video__fallback { color: #fff; font-size: 15px; padding: 24px; }
.ap-video__fallback a { color: #fff; text-decoration: underline; }

/* ------------------------------------------------------------
 * 7. THE APPS — six cards with a screenshot header
 * ------------------------------------------------------------ */
/* ⚠ A HORIZONTAL SCROLLER, not a grid. Three across was the same shape as "How
   it works"; two across fixed that but made each card enormous. One row that
   scrolls sideways keeps every card a sensible size, gives the section a shape
   nothing else on the page has, and suits the content — six apps is a list, not
   a matrix.
   Earlier attempt recorded here so it is not repeated: turning the cards
   HORIZONTAL (thumbnail beside the copy) cropped the pictures to a vertical
   sliver, because a 38%-wide full-height column is the wrong window for a 16:9
   screenshot. The cards stay photo-on-top; only the track changed.

   ⚠ NO FULL-BLEED. home-v2.css records this one in detail for .pst: pulling the
   track out to the viewport edge with a negative margin gave the whole page a
   horizontal scrollbar on an iPad, because the bleed maths and the container's
   real padding disagreed between 768 and 900px. The track stays inside
   .container, so it lines up with every other section and cannot overflow. */
.ap-apps {
  display: flex;
  gap: clamp(16px, 1.8vw, 24px);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* no visible scrollbar: the card peeking at the right edge is the cue */
  scrollbar-width: none;
  -ms-overflow-style: none;
  padding-bottom: 4px;              /* room for a focus ring on a snapped card */
}
.ap-apps::-webkit-scrollbar { display: none; }
@media (prefers-reduced-motion: reduce) { .ap-apps { scroll-behavior: auto; } }
/* ⚠ a scrollable region must be reachable by keyboard, so the track carries
   tabindex="0" in the markup. This is its focus ring. */
.ap-apps:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(47, 183, 241, 0.45); border-radius: 8px; }

.ap-app {
  /* 0 0 so cards never squeeze to fit: the track scrolls instead. ~340px at
     desktop puts just under four in the content width, so the fourth peeks and
     tells you there is more. */
  flex: 0 0 clamp(258px, 25vw, 340px);
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: var(--radius-xl, 24px);
  overflow: hidden;
  box-shadow: 0 8px 24px rgba(15, 20, 56, 0.08);
  transition: transform 220ms var(--hv2-ease), box-shadow 220ms var(--hv2-ease);
}
.ap-app:hover { transform: translateY(-6px); box-shadow: 0 22px 48px rgba(15, 20, 56, 0.14); }
.ap-app__shot {
  aspect-ratio: 16 / 9;
  background: var(--ge-bg-light, #F3F7FA);
  overflow: hidden;
}
.ap-app__shot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center top; }
/* ⚠ per-image crop override. `center top` is right for the portrait UI
   screenshots, where the interesting part is the top of the screen, but wrong for
   a SQUARE photograph: reception-product.png is 1024x1024 in a 16:9 window, so
   only 56% of its height survives and anchoring that to the top filled the card
   with ceiling tiles and cut the iPad off at the bottom. 62% centres the window
   on the iPad instead. */
.ap-app__shot--lower img { object-position: center 62%; }
.ap-app__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: clamp(22px, 2.2vw, 28px);
}
.ap-app h3 { margin: 0 0 4px; font-size: 18px; color: var(--ge-navy, #0F1438); }
.ap-app__platform { margin: 0 0 12px; font-size: 13.5px; font-weight: 600; color: var(--ge-blue, #1B367E); }
.ap-app__desc { margin: 0 0 18px; flex: 1; font-size: 14.5px; line-height: 1.55; color: #4A5876; }
.ap-app__status {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
}
.ap-app__status--live { background: var(--ge-green-2, #E6F3D9); color: var(--ge-navy, #0F1438); }
.ap-app__status--live::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--ge-green-4, #76A74F);
}
.ap-app__status--soon { background: var(--ge-bg-light, #F3F7FA); color: #4A5876; }

/* ------------------------------------------------------------
 * 8. MARTYN'S LAW — the photo module
 * The third .band--photo on the site. overflow: hidden on the section (set by
 * modular.css) is what clips the photo to the 64px corners, including its own
 * negative inset, so the picture never paints outside the card.
 * ------------------------------------------------------------ */
/* ⚠ THE PHOTO IS A CHILD OF THE SECTION, NOT OF .ap-ml. modular.css pads every
   section by --band-pad (32-80px) top and bottom; with the photo one level
   deeper it only covered .ap-ml and that padding rendered as white strips
   across the top and bottom of the rounded card. */
.ap-ml__photo {
  position: absolute;
  inset: -30px;                  /* overscan, so the blur's own soft edge is clipped away */
  z-index: 0;
  width: calc(100% + 60px);
  /* ⚠ REQUIRED, and the reason the right-hand edge fringed. foundation.css sets
     a global `img { max-width: 100% }`, which caps this back to the band's own
     width — so the negative inset only took effect on the LEFT and the blurred
     right edge finished 30px inside the card. home-v2.css records the identical
     bug biting both of the site's existing photo bands. */
  max-width: none;
  height: calc(100% + 60px);
  object-fit: cover;
  object-position: center 40%;
  filter: blur(3px);
}
/* ⚠ z-index 1, explicitly. The site's own .band--photo__scrim sits at -1, which
   puts it BEHIND the photo it is supposed to be dimming; stated here so this one
   actually does its job. */
.ap-ml__scrim { position: absolute; inset: 0; z-index: 1; background: rgba(15, 20, 56, 0.42); }
.ap-ml {
  position: relative;
  z-index: 2;
  min-height: clamp(360px, 38vw, 480px);
  display: flex;
  align-items: center;
}
.ap-ml__card {
  /* ⚠ margin-INLINE only. The section's own --band-pad already provides the
     vertical inset; a margin on all four sides stacked on top of it and pushed
     the card 144px off the top edge against 64px off the side. */
  margin-inline: clamp(24px, 4vw, 64px);
  max-width: 58ch;
  padding: clamp(28px, 3vw, 44px);
  background: #fff;
  border-radius: var(--radius-xl, 24px);
  box-shadow: 0 18px 44px rgba(15, 20, 56, 0.18);
}
.ap-ml__card h3 { margin: 14px 0 12px; font-size: clamp(24px, 2.4vw, 32px); color: var(--ge-navy, #0F1438); }
.ap-ml__card p { margin: 0; font-size: 16px; line-height: 1.65; color: #4A5876; }
.ap-ml__card .ap-link { margin-top: 20px; }

/* ------------------------------------------------------------
 * 9. CASE STUDY — Cantonian High School
 * ------------------------------------------------------------ */
/* ⚠ TWO COLUMNS AGAIN, but not the old quote-plus-facts-box pair. The facts box
   was removed in the October rewrite, which left the story running as one 820px
   column; centred under a centred head that was fine, but once the heads moved to
   the page's left edge it left ~490px of empty page beside it and the section read
   as adrift. The quote carries the left column, the story the right.
   ⚠ align-items: start — the two sides hold different amounts and centring them
   against each other floats the crest away from the top. */
.ap-case {
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
}
/* ⚠ margin-inline: auto is what centres it. The global `img { display: block }`
   in foundation.css makes this a block box, so text-align on an ancestor has no
   effect on it — it has to centre on its own margins. */
/* ---- the crest credits the quote, it does not head the column ----
 * ⚠ It used to sit on its own above the quote, which is what made the two columns
 * look unbalanced: the left one opened with a small square and the right one
 * opened with text, so the two sides never started on the same line. Paired with
 * the attribution it reads as a testimonial credit — quote first, then the mark
 * and the person who said it — and both columns now begin with type. */
.ap-case__by { display: flex; align-items: center; gap: 14px; margin-top: clamp(18px, 2vw, 24px); }
.ap-case__crest { width: 56px; height: 56px; flex: none; margin: 0; object-fit: contain; }
.ap-quote {
  margin: 0;
  font-size: clamp(21px, 2.1vw, 28px);
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: -0.01em;
  color: var(--ge-navy, #0F1438);
}
.ap-attrib { margin: 0; font-size: 14.5px; line-height: 1.5; color: #4A5876; }
/* ⚠ The school sits on its own line by an explicit <br>, not by letting it wrap
   there — at this column width it would otherwise land wherever the measure put
   it. Not a copy change: every character is as written, including the comma
   before the break. The school is a shade stronger so the two lines read as
   "who said it" then "where", rather than as one sentence that broke oddly. */
.ap-attrib__org { color: var(--ge-navy, #0F1438); font-weight: 600; }
.ap-case__body { margin: 0 0 18px; font-size: 16px; line-height: 1.7; color: #4A5876; }
.ap-case__story .ap-link { margin-top: 4px; }
.ap-case .ap-link { margin-top: 22px; }

/* "At a glance" — a definition list, not a table: six label/value pairs */
.ap-facts {
  background: #fff;
  border-radius: var(--radius-xl, 24px);
  box-shadow: 0 8px 24px rgba(15, 20, 56, 0.08);
  padding: clamp(24px, 2.4vw, 34px);
}
.ap-facts dl { margin: 18px 0 0; }
.ap-facts div {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 13px 0;
  border-bottom: 1px solid var(--ge-neutral-lt3, #E6ECF4);
}
.ap-facts div:last-child { border-bottom: none; padding-bottom: 0; }
.ap-facts dt { font-size: 14.5px; color: #4A5876; }
.ap-facts dd { margin: 0; font-size: 14.5px; font-weight: 600; text-align: right; color: var(--ge-navy, #0F1438); }

/* ------------------------------------------------------------
 * 11. NEXT STEP — the closing navy card
 * ------------------------------------------------------------ */
.ap-cta { text-align: center; }
.ap-cta h2 { margin: 16px 0 14px; color: #fff; }
.ap-cta > p { margin: 0 auto clamp(26px, 3vw, 36px); max-width: 56ch; font-size: clamp(16px, 1.3vw, 19px); line-height: 1.65; color: rgba(255, 255, 255, 0.86); }
.ap-cta .ap-eyebrow { align-self: center; }
.ap-cta__actions { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; }
/* white primary on navy, with a navy arrow chip — the .st-board recipe */
.ap-cta__actions .btn { background: #fff; color: var(--ge-navy, #0F1438); border: none; font-weight: 700; }
.ap-cta__actions .btn:hover { background: #EAF1F8; color: var(--ge-navy, #0F1438); }
.ap-cta__actions .btn__arrow { background: var(--ge-navy, #0F1438); color: #fff; }
.ap-cta__actions .btn--ghost {
  background: rgba(255, 255, 255, 0.10);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.55);
}
.ap-cta__actions .btn--ghost:hover { background: rgba(255, 255, 255, 0.22); color: #fff; }
/* top-right, mirrored from bottom: -34% so the same amount of the mark shows */
.ap-cta__wm { position: absolute; right: -4%; top: -34%; bottom: auto; width: 26%; opacity: 0.05; pointer-events: none; }
.ap-cta__wm img { width: 100%; height: auto; }

/* ------------------------------------------------------------
 * 12. CONTACT
 * ------------------------------------------------------------ */
/* Two equal cards rather than two bare text columns: the left one was two big
   links floating in space against a block of prose on the right, which read as
   unfinished. Same card language as the rest of the page, and they bottom out
   level because the grid stretches them. */
.ap-contact {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 24px);
  align-items: stretch;
}
/* ⚠ NOT .ap-card. These are not links and nothing happens when you click them,
   so the hover lift that every other card on the page carries would be a lie. */
.ap-contact__card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: var(--radius-xl, 24px);
  box-shadow: 0 8px 24px rgba(15, 20, 56, 0.08);
  padding: clamp(26px, 2.6vw, 38px);
}
.ap-contact__lead {
  margin: 0 0 6px;
  font-size: clamp(19px, 1.7vw, 24px);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.35;
  /* the email is one long unbreakable token on a phone */
  overflow-wrap: anywhere;
}
.ap-contact__lead a { color: var(--ge-blue, #1B367E); text-decoration: none; }
.ap-contact__lead a:hover { color: var(--ge-navy, #0F1438); }
.ap-contact__note { margin: auto 0 0; padding-top: 18px; font-size: 15px; color: #4A5876; }
.ap-contact h3 { margin: 0 0 12px; font-size: 19px; color: var(--ge-navy, #0F1438); }
.ap-contact p { margin: 0 0 12px; font-size: 16px; line-height: 1.65; color: #4A5876; }
.ap-contact p:last-child { margin-bottom: 0; }

/* ------------------------------------------------------------
 * RESPONSIVE
 * One set of breakpoints for every grid on the page, matching .st-cards /
 * .st-prods (1000px to two columns, 700px to one).
 * ------------------------------------------------------------ */
/* the split hero stacks before the rest of the page does: below ~900px each half
   is under 450px, and the headline starts breaking badly long before the cards do */
@media (max-width: 900px) {
  .hero2--apple .hero2__frame {
    grid-template-columns: 1fr;
    grid-template-rows: auto clamp(200px, 42vw, 320px);
    min-height: 0;
  }
  .hero2--apple .hero2__copy { padding: clamp(28px, 6vw, 44px); }
  .hero2--apple .hero2__copy h1 { font-size: clamp(33px, 6.4vw, 48px); }
  .hero2--apple .hero2__wm { width: clamp(260px, 52vw, 420px); }
}

@media (max-width: 1080px) {
  .ap-sign { grid-template-columns: 1fr; gap: clamp(22px, 3vw, 36px); }
  .ap-sign__media { order: 2; }
  .ap-sign__copy p { max-width: none; }
  .ap-stats { grid-template-columns: repeat(2, 1fr); }
  .ap-case { grid-template-columns: 1fr; }
}
@media (max-width: 1000px) {
  .ap-grid3,
  .ap-tiles { grid-template-columns: repeat(2, 1fr); }
  /* the ticks go two-up before the three beats do: four of them on one line is
     the first thing to get cramped */
  .ap-points { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 700px) {
  .ap-grid3,
  .ap-grid2,
  .ap-tiles,
  .ap-points,
  .ap-stats,
  .ap-contact { grid-template-columns: 1fr; }
  /* the one scale moment has to come down or it is four words a line */
  .ap-head--display h2 { font-size: clamp(34px, 9vw, 48px); }
  .ap-ml { min-height: 0; }
  .ap-ml__card { max-width: none; margin: 16px; }
  /* ⚠ 16:9 IS KEPT ON PHONES — do not put `aspect-ratio: auto` back here.
     It used to say exactly that, which was right when this frame held a
     placeholder: a blue box with a play icon and two lines of text, which
     needed to shrink to its own content rather than hold a letterbox full of
     empty blue. There is a real film in it now, and `auto` let the box collapse
     to the height of the caption — 247px at every width below 700, so the
     picture was being cropped to a 1.39:1 letterbox on a phone. Measured 15 Oct
     at 375/414/600px. The padding goes too: it inset the video inside its own
     frame and showed a blue border down both sides. */
  .ap-video { padding: 0; }
  .ap-hero__ticks li { font-size: 14.5px; }
}


/* ============================================================
 * CERYS'S AMENDS, 6 Oct 2026 — ported back from the review copy.
 * Her session made these edits on the standalone file; they are the only
 * stylesheet changes in it, so they live here rather than in any shared sheet.
 * ============================================================ */

/* hero pill only: sentence case, so the brand name reads as GroupEd */
.ap-eyebrow.ap-eyebrow--sentence { text-transform: none; letter-spacing: 0.01em; font-size: 14px; }

/* keeps the word "Lockdown" on the class display in frame on the Apple TV card */
.ap-card__shot--left img { object-position: 0% center; }

/* the case study lost its "At a glance" box, so the story runs one column */

/* ⚠ .foot2__grid--apple belongs to home-v2.css's footer component, but it is
   defined HERE on purpose: home-v2.css is shared by all 30 other pages and this
   footer shape is this page's alone. apple-landing.css loads after it, so the
   override lands without touching anything else on the site. */
.foot2__grid--apple { grid-template-columns: 2fr 1fr 1.2fr; }

/* ---- the closing banner takes the hero blue ----
 * Rebecca, 7 Oct. Keyed to this section's own id rather than to .band--navy: the
 * sign-in band carried that class too at the time, and colouring by class would
 * have changed both. That band has since come out of its tile, so this is now the
 * only dark card on the page — the id is kept anyway, so adding another card back
 * cannot silently recolour it.
 * ⚠ #253A7D is written out again rather than shared with the hero: there is no
 * token for it (it lives in home-personality.css, which this page does not load).
 * If the hero blue changes, change it in both places.
 * The id beats .band--navy's navy (1,0,0 against 0,1,0), so nothing else needs
 * touching: the white eyebrow pill, white buttons and navy arrow chips all keep
 * working, and white on this blue measures 10.6:1. */
#next-step { background: #253A7D; }

/* ⚠ AND A BIGGER GAP BEFORE THE FOOTER. modular.css already gives a last-child
   card `margin-bottom: var(--band-seam)` = 24px, and on every other page that is
   right: there the card is navy and so is the footer, or the last band is white.
   Here the card is #253A7D and the footer is #0F1438, so 24px of white between
   two different dark surfaces read as a seam someone had forgotten to close
   rather than as breathing room (Rebecca, 7 Oct: "looks too close to the footer").
   ~57px at 1440 — more than double the card-to-card seam, because the footer is
   full-bleed and a card needs more air against an edge-to-edge surface than it
   does against another card. */
#next-step { margin-bottom: clamp(40px, 4.4vw, 72px); }

/* ============================================================
 * DEVICE BANNER (7 Oct 2026) — the product line-up above "Where GroupEd runs".
 * A carded band in the hero blue, so the page's colour runs hero -> banner ->
 * closing CTA and the three dark moments are evenly spaced down the page.
 *
 * ⚠ AN ID, NOT A CLASS, AND THAT IS DELIBERATE. modular.css's
 * `body.hv2 main > section:not(:first-of-type)` is (0,2,3) and loads AFTER this
 * file, so a class-based selector here ties on specificity and loses on order —
 * the band padding would come back and the card geometry would not apply. An id
 * is (1,0,0) and settles it.
 *
 * ⚠ The mockup is a transparent PNG whose devices sit inside their own margin of
 * empty space. That padding is doing the work here, so the band needs very little
 * of its own; adding more would float the devices in a sea of blue.
 * ============================================================ */
/* ⚠ AN ID, NOT A CLASS, AND THAT IS DELIBERATE. modular.css's
   `body.hv2 main > section:not(:first-of-type)` is (0,2,3) and loads AFTER this
   file, so a class-based selector ties on specificity and loses on order — the
   band padding would come back and the card geometry would not apply. An id is
   (1,0,0) and settles it.
   ⚠ The mockup is a transparent PNG whose devices sit inside their own margin of
   empty space, so the band needs very little padding of its own; more would
   leave them floating in a sea of blue. */
body.hv2 main > section#device-banner {
  position: relative;            /* the watermark anchors to the band */
  overflow: hidden;              /* and is clipped by its rounded corners */
  margin-inline: var(--band-inset, 24px);
  /* ⚠ SKY, the logo blue (Rebecca, 7 Oct: "maybe the light blue from our logo").
     Was #253A7D, the hero blue.
     ⚠ This band is the one place sky can be a large surface: CLAUDE.md reserves
     it for actions and says it fails AA on white, but that rule is about TEXT and
     there is no text in this band — only device renders and a watermark. Do not
     copy this to a band that carries copy.
     ⚠ The watermark had to change with it: white at 0.05 vanishes on a light
     ground, so it is now navy. See .ap-banner__wm below. */
  background: var(--ge-sky, #2FB7F1);
  border-radius: var(--band-radius, 64px);
  padding: clamp(18px, 2.2vw, 36px) clamp(18px, 2.6vw, 44px);
}
/* ⚠ BIGGER THAN THE NEXT STEP BANNER'S (Rebecca, 7 Oct). It was 26%, matched to
   .ap-cta__wm so the two marks were identical; then 36%, now 45% (26 -> 36 -> a
   further 25%). They are
   deliberately no longer the same width — this band is shorter and wider than the
   closing one, so the same percentage reads as a smaller mark on it.
   Everything else still matches: same top-right corner, same -34% / -4% offsets,
   same 0.05 opacity. */
.ap-banner__wm {
  position: absolute;
  z-index: 0;
  /* ⚠ TOP-right (Rebecca, 7 Oct). -34% is mirrored from the bottom-anchored
     version it replaces, so exactly the same amount of the hexagon shows — just
     hanging off the top edge instead of the bottom. On this band that is also
     the clear corner: the devices sit low, so nothing covers the mark. */
  right: -4%;
  top: -34%;
  bottom: auto;
  left: auto;
  width: 45%;
  height: auto;
  /* ⚠ 0.09, not 0.05, and a NAVY mark not a white one. On the navy band a white
     hexagon at 0.05 read correctly; on sky the band is light, so white has almost
     nothing to darken against. Navy at 0.09 gives the same whisper of a shape. */
  opacity: 0.09;
  pointer-events: none;
}

/* ⚠⚠ THE OPACITY HAS TO BE RE-STATED AT ID STRENGTH, AND THIS IS THE WHOLE BUG
   BEHIND "the watermark is 100% opacity".
   scripts/home-v2.js walks the page and auto-applies `.rv` to anything it decides
   is a reveal block — it decided these watermarks were. home-v2.css then carries
   `.rv.in { opacity: 1 }` at (0,2,0), which beats a bare `.ap-banner__wm` at
   (0,1,0). So the mark sat correctly at 0.05 until it scrolled into view, and the
   reveal then slammed it to fully opaque. Every static check I ran reported 0.05,
   because nothing had scrolled yet.
   An id selector is (1,1,0) and settles it. ⚠ Do not "simplify" these back to a
   bare class, and give any future watermark on this page the same treatment. */
#device-banner .ap-banner__wm,
#next-step .ap-cta__wm { opacity: 0.05; }

/* ---- the device stage ----
 * ⚠ aspect-ratio is load-bearing. The four devices are absolutely positioned by
 * percentage, so the stage has to hold its own shape or they lose their
 * arrangement. 2.7603 is the ratio of the artwork's real bounding box, measured
 * when the devices were exported — not a round number by choice. */
.ap-banner__stage {
  position: relative;
  z-index: 1;
  width: min(100%, 1160px);
  aspect-ratio: 3.1198;
  margin-inline: auto;
}
.ap-dev { position: absolute; display: block; height: auto; }
/* ⚠ THE APPLE TV NEEDS ITS SHADOW ADDED; the other three already have one baked
   into the PNG. Measured on the exports: the iMac, iPad and iPhone all carry a
   soft alpha skirt below and to the side (peak alpha 59-124, 6-40px deep), and
   appletv.png has none at all — peak alpha 0, the artwork stops dead at its edge.
   Without this it looked pasted on while the others sat on a surface.
   drop-shadow, not box-shadow: box-shadow would draw a rectangle round the image
   box, while drop-shadow follows the alpha silhouette — so it wraps the screen,
   the Apple TV box and the remote rather than the file they live in.
   Two layers, the same way the others read: a soft ambient and a tighter contact
   shadow underneath.
   ⚠ In px, so it does not scale with the banner the way the baked ones do. Tuned
   for the desktop size; the banner only ranges about 700-1160px, so the drift is
   small. If it ever needs to be exact, the fix is to bake the shadow into the PNG
   — but that changes the file's canvas, and every percentage above is derived
   from that canvas, so they would all have to be regenerated. */
.ap-dev--appletv {
  filter: drop-shadow(0 9px 18px rgba(15, 20, 56, 0.20))
          drop-shadow(0 2px 5px rgba(15, 20, 56, 0.14));
}
/* ⚠ EQUAL SPACING (Rebecca, 7 Oct). Every pair is now 2.56% of the stage apart,
   measured device-edge to device-edge — not image-box to image-box, because each
   PNG carries a different amount of transparent margin for its own drop shadow,
   so matching the boxes would have left the visible gaps uneven.
   Nothing overlaps any more: the first two joins used to, the last did not.
   ⚠ Spreading them stretched the strip, so the aspect-ratio above moved from
   2.8802 to 3.1198 and every percentage changed with it. These are a set,
   generated together from the artwork — do not retune one by hand. */
.ap-dev--appletv { left:  0.00%; top:  0.00%; width: 42.15%; }
.ap-dev--imac    { left: 44.62%; top: 31.69%; width: 23.60%; }
.ap-dev--ipad    { left: 66.01%; top: 54.13%; width: 28.40%; }
.ap-dev--iphone  { left: 92.21%; top: 58.72%; width:  7.79%; }
