/* ============================================================
   Sweet — site layer
   Composition rules for the Sweet website. Sits on top of the
   generated design system in _ds/ (never edit that — it is
   regenerated). Everything the site needs that the component
   library does not provide lives here, named, once.

   Read the two rules at the top before adding anything.
   ============================================================ */

:root {
  /* ---- RULE 1 · The radius ladder ----------------------------
     Six values, each with exactly one job. Nothing else is
     allowed. If a shape does not fit a rung, the shape is wrong,
     not the ladder.

       --radius-sm   10px  media tiles nested inside a card
       --radius-md   16px  inputs, logo tiles, small chips
       --radius-lg   24px  cards
       --radius-xl   32px  large feature surfaces (hero, footer)
       --radius-2xl  40px  full-width section bands
       --radius-pill 999   buttons, tags, avatars
     ---------------------------------------------------------- */
  --radius-2xl: 40px;

  /* ---- RULE 2 · Concentric radii -----------------------------
     A shape nested inside another shape has
         inner radius = outer radius − the gap between them.
     Get this wrong and you get the "bubble in a box" look. Use
     .sw-tile, which does the arithmetic for you.
     ---------------------------------------------------------- */
  --tile-inset: 14px;

  /* ---- Slant ------------------------------------------------
     The brand's signature angle (Brand Guidelines V1, p.43).
     TWO values only — one for slanted horizontal edges, one for
     slanted vertical edges. Never invent a third.
     ---------------------------------------------------------- */
  --slant-h: 2.4deg;   /* band top/bottom edges  */
  --slant-v: 7deg;     /* hero's vertical edge   */

  /* The RISE of a slanted band edge: band width x tan(--slant-h). The colour on
     the shallow side of a slant stops this much higher than the band's box
     does, so a band whose content sits at that end has to clear it or the copy
     overhangs the colour.

     It is a function of WIDTH, which is the whole point — it cannot be written
     as a fixed pixel value or as a percentage of height. 0.041914 is
     tan(2.4deg); CSS has no tan(), and hard-coding the ratio beside the angle
     it comes from is the least-bad option. If --slant-h ever changes, change
     this with it. */
  /* min() against --page-max, not 100vw: the rise is a function of the BAND's
     width, and past the cap the band stops growing while the viewport does not.
     Left as 100vw it came out about 50% too steep on a wide monitor. Identical
     below the cap, where min() resolves to 100vw. */
  --slant-rise: calc((min(100vw, var(--page-max)) - (2 * var(--page-inset))) * 0.041914);

  /* Overshoots slightly at the end, so a dropping element lands rather than
     glides. Used by the Highlight Tags on the "What is Sweet" photo. */
  --ease-drop: cubic-bezier(0.22, 1.5, 0.36, 1); /* @kind other */

  /* ---- Danger -----------------------------------------------
     The design system's --danger is #C8553D, which measures 4.35:1 on white
     and 3.73:1 on Paper Ivory — under the 4.5:1 AA threshold on BOTH of the
     site's two light grounds. It is the colour of the required-field asterisk
     on every form label and of the inline error message, which is the one
     piece of text on the site someone is most likely to be squinting at.

     Darkened along its own hue to #A7442F: 5.98:1 on white, 5.12:1 on ivory.
     It is only ever used as TYPE and as an input's error border — never as a
     fill — so darkening it has no downside. One token rather than a per-class
     override, so .sw-field__req, .sw-field__help--error, .sw-input--error and
     .sw-inlineerr all move together.
     ---------------------------------------------------------- */
  --danger: #A7442F;

  /* ---- Page rhythm ---------------------------------------- */
  --page-inset:  16px;   /* gap from band to viewport edge  */

  /* Rebecca, 15 October 2026: "the site looks best on laptop screens ... on a
     big screen it looks a bit too stretched." She was reading a real mismatch.
     Content has always been capped at --content-max (1440px); the BANDS were
     not, so on a 2560px monitor a 2528px-wide band held a 1440px column with
     544px of empty colour either side of it. On a 1680px laptop that slack is
     144px, which is the proportion the site was designed at.

     Capping the whole page — header, bands, slants and footer together — makes
     every screen wider than this look like that laptop. Nothing below 1760px
     changes at all. */
  --page-max: 1760px;    /* the widest the site itself ever gets     */

  /* White space between the capped page and the window edge — 0 below the cap.
     The header uses it to break back out to the full window, and the nav
     dropdown uses it to stay lined up with the page while the bar does not. */
  --page-gutter: max(0px, calc((100vw - var(--page-max)) / 2));
  --surface-page: var(--simple-white);  /* page canvas — white, not ivory */
  --band-gap:    56px;   /* vertical gap between sections   */
  /* Band padding. Two tokens, not one --band-pad-y: the bottom of a band runs a
     little deeper than the top across the whole site, which is deliberate — a
     heading sits closer to the band edge above it than the last line of body
     copy does to the edge below.

     76/84 is the HOUSE VALUE, arrived at by counting: it was already what 21 of
     78 content bands used, more than any other. The token said 104/104 and
     almost nothing used it, because every band overrode it inline. A default
     nothing uses is a trap — the next page built "to the token" comes out
     visibly different from its neighbours. See .sw-band--pad-* below. */
  --band-pad-top:    76px;
  --band-pad-bottom: 84px;
  /* The responsive shrink is a MULTIPLIER, not a second set of values, and it
     is only ever set on :root. A custom property set closer to the element
     wins outright — so the moment a --pad-* modifier writes --band-pad-top on
     the inner itself, a :root media-query override of that same property can
     never reach it again, and every band with a modifier would have frozen at
     its desktop padding on a phone. Scaling in the shorthand keeps one lever
     for every band at once.

     THE LADDER STARTS BELOW 1, and that is the site-wide whitespace setting.
     Rebecca, 16 September 2026: "my boss likes a little less white space". It
     runs 0.86 / 0.74 / 0.54 and --band-gap runs 56 / 48, where both used to be
     1 / 0.82 / 0.6 and 72 / 60. One lever rather than 78 edits, and it moves
     the six --pad-* modifiers with it, so the relationship between a standard
     band and a snug one is unchanged — only the whole scale moved.

     This is the knob to turn if the answer is ever "a bit less again" or "that
     went too far". Do not reach for the individual tokens or the modifiers:
     they encode the RATIOS, and k encodes the amount. */
  --band-pad-k:  0.86;
  --band-pad-x:  40px;
  --content-max: 1440px;
  --hero-min:    clamp(220px, 24vw, 330px);  /* floor for a hero's media column */
  /* Floor for the hero BAND. The inner is border-box, so this is the whole band
     height including its padding — 480px is what the About hero measures, which
     is the reference Rebecca set. */
  --hero-band-min: clamp(300px, 34.5vw, 480px);

  /* Header sizing. --header-h is the lockup height plus its padding; the
     sticky shop filters offset from it, so changing the padding here keeps
     them in step instead of needing a second hand-tuned number. */
  --header-pad-y: 24px;
  --header-h: calc(46px + 2 * var(--header-pad-y));

  /* Header padding, named so the full-width dropdown can offset against it */
  /* How far a slanted fill bleeds past its far edge */
  --fill-overhang: 280px;

  --header-pad-l: 26px;
  --header-pad-r: 18px;
}

/* ============================================================
   Page shell
   ============================================================ */

/* `clip` — not `hidden`. `overflow-x: hidden` silently makes this
   element a scroll container, which kills scroll-behaviour: smooth
   on <html> and breaks sticky/anchor behaviour. */
/* The clip lives on <body>, NOT on .sw-page, and that is load-bearing. Once
   .sw-page is capped at 1760px, a clip on it would cut the header off at the
   page edge — and the header is the one thing that has to escape. On <body> it
   still stops anything causing a horizontal scrollbar, which is all it was ever
   doing, but it does it at the window edge where it used to. */
body { overflow-x: clip; }

.sw-page {
  max-width: var(--page-max);
  margin-inline: auto;
}

/* ============================================================
   Bands — a full-width inset rounded surface
   ============================================================ */

.sw-band {
  position: relative;
  margin: var(--band-gap) var(--page-inset) 0;
  border-radius: var(--radius-2xl);
  overflow: hidden;
}

.sw-band__inner {
  position: relative;
  max-width: var(--content-max);
  margin: 0 auto;
  padding: calc(var(--band-pad-top) * var(--band-pad-k))
           var(--band-pad-x)
           calc(var(--band-pad-bottom) * var(--band-pad-k));
}

/* ---- Band padding modifiers ----------------------------------------------
   Six named values, and every band on the site is one of them. Before this,
   78 content bands carried 27 different inline padding combinations and the
   token in :root was used by four of them — the site had no spacing system,
   it had 27 decisions.

   They set TOKENS rather than padding, so the shorthand above stays the one
   place padding is written and the modifiers never fight each other on
   specificity. It also keeps them out of the way of the one rule that must
   still win: `.sw-band--slant-bottom + .sw-band > .sw-band__inner` sets
   padding-top: 20px directly, at (0,3,0), because a band under a diagonal
   needs to pull up tight. These modifiers are (0,2,0) and the shorthand is
   (0,1,0), so that rule still governs wherever it applies — which is exactly
   what the old inline values were breaking.

   Put the class on the SECTION, never an inline style. An inline value beats
   the stylesheet, which is how the site drifted to 27 values and how the
   hero-gap rule sat dead for weeks. */
.sw-band--pad-tight   > .sw-band__inner { --band-pad-top: 44px; --band-pad-bottom: 64px; }
.sw-band--pad-cta     > .sw-band__inner { --band-pad-top: 72px; --band-pad-bottom: 72px; }
.sw-band--pad-doc     > .sw-band__inner { --band-pad-top: 56px; --band-pad-bottom: 88px; }
.sw-band--pad-product > .sw-band__inner { --band-pad-top: 44px; --band-pad-bottom: 84px; }
.sw-band--pad-snug    > .sw-band__inner { --band-pad-top: 40px; --band-pad-bottom: 40px; }
.sw-band--pad-sheet   > .sw-band__inner { --band-pad-top: 24px; --band-pad-bottom: 24px; }

/* Every hero on the site is 44/64, so it needs no class — the band already
   says it is a hero. */
.sw-heroband > .sw-band__inner { --band-pad-top: 44px; --band-pad-bottom: 64px; }

/* ---- Wales | England tabs (`.sw-tabs`) --------------------------------------
   The PGW curriculum-fit band. The first tabbed control on the site, and it
   exists because PGW is ONE page where WorkSkills is three: WorkSkills routes
   to a country page, PGW has to hold both countries in place.

   The tablist is injected by assets/js/country-tabs.js, never written in the
   markup. Without JavaScript the band is two ordinary sub-sections under their
   own headings, both readable — which is also what every crawler and AI system
   reading the static HTML gets, and the two qualification-size tables are among
   the most searched facts on this page. Tabs that shipped hidden in the source
   would have cost half of them. Same reasoning as the blog filter's chips. */
/* A SEGMENTED CONTROL, not two loose pills. As two separately outlined
   buttons sitting in whitespace they read as optional chips you may click, and
   on a phone they arrived after about 110 words of preamble, so the one
   interaction in the band was the easiest thing on it to miss.

   One track holding both options, with the live one filled, is the universal
   "pick one of these two" shape: the outline belongs to the GROUP, so the two
   options are visibly alternatives to each other rather than two separate
   invitations. White track on the band's Paper Ivory so the control sits
   forward of the page rather than in it. */
.sw-tabs__list {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 5px;
  border-radius: var(--radius-pill);
  background: var(--simple-white);
  border: var(--border-width) solid var(--focus-black);
}

.sw-tabs__tab {
  padding: 11px 26px;
  /* No border of its own — the track above carries the outline. Keeping one
     here put a stroke inside a stroke and the group stopped reading as a
     single control. */
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--focus-black);
  font-family: var(--font-primary);
  font-size: 17px;
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: background var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out);
}

/* A stroke, not a drop shadow. These are interactive, and the brand's hard
   offset shadow is reserved for label pills — on a control it reads as a
   second, conflicting "press me". */
/* The lead-in that sits directly above the control, in the section's heading
   block. It replaced a title of its own, .sw-tabs__title, at h3 scale — which
   gave one section two competing headings about 300px apart. The control is
   now under the section's OWN h2, where a title for it would only repeat what
   the heading already says.

   What this line has to carry is the one fact "Wales | England" cannot: that
   the qualifications themselves differ, so the choice is not cosmetic. Capped
   short so it reads as a lead-in to the control rather than a third paragraph
   of preamble. */
/* 18px on top of the stack's 12px gap, and nothing underneath. The line and
   the control have to read as ONE unit, separated from the two body
   paragraphs above them — at an even 12px on both sides it read as a third
   paragraph that happened to have a control after it, which is close to the
   problem this whole section started with. 30px above against 12px below is
   what makes the grouping obvious. */
.sw-tabs__why {
  margin: 18px 0 0;
  max-width: 48ch;
  font-size: 16px;
  line-height: 1.45;
  color: var(--text-secondary);
}

.sw-on-dark .sw-tabs__why { color: rgba(255, 255, 255, 0.72); }

.sw-tabs__tab:hover { background: var(--focus-black); color: var(--simple-white); }

/* The selected pill needs a signal hover cannot produce. Hover sets exactly the
   same background and colour, so hovering the inactive tab made BOTH pills read
   as selected — and worse on touch, where iOS and Chrome keep :hover on the
   last-tapped element, leaving a stale second "selected" pill with no pointer on
   screen to explain it. An inverted inner stroke: selected always has the ring,
   hover never does. */
.sw-tabs__tab.is-selected {
  background: var(--focus-black);
  color: var(--simple-white);
  box-shadow: inset 0 0 0 var(--border-width) var(--simple-white);
}

/* On the navy curriculum band. The track's outline is Focus Black, which on
   --surface-inverse (the same colour) is not an outline — it is 1.5px of band
   painted inside the control, nibbling the white pill's edge. White merges it
   into the track instead, so the control keeps its exact geometry and reads as
   one clean white pill against the navy.

   (This rule once had a label beside it, then a title. Both are gone; the
   control's words are now .sw-tabs__why in the section's heading block.) */
.sw-on-dark .sw-tabs__list { border-color: var(--simple-white); }

/* .sw-body is margin: 0 by design — spacing normally comes from the .sw-stack
   wrapping it. The panels' paragraphs are bare children of the panel, so they
   had no gap at all: the England panel's five paragraphs rendered as one
   undifferentiated wall, and those five are the most carefully hedged copy on
   the site ("may consider", "can form part of", "does not replace a school's
   statutory responsibilities"). Each hedge is a separate claim and has to read
   as one. 12px matches the intro stack above. */
.sw-tabs__panel .sw-body + .sw-body { margin-top: 12px; }

/* The panels used to follow the control inside one container and leaned on the
   control's own bottom margin for the gap. The control now lives in the
   section's heading block, so the panel block carries its own clearance — and
   it has to, because stacked on a phone the panel heading lands directly under
   the control with nothing between them. */
.sw-tabs { margin-top: clamp(26px, 3vw, 38px); }

.sw-tabs__panel.is-hidden { display: none; }

/* ---- Clearing a slant -----------------------------------------------------
   A band whose content runs to the shallow end of its own diagonal needs the
   house padding PLUS the slant's rise, or the last line overhangs the colour.

   These four were inline pixel values, measured once at one screen width, and
   they were wrong at every other width — which is what an inline value buys
   you. At 1440 they gave 150px to clear a 59px rise; at 2560 the rise grows to
   106px and 150px was no longer enough; at 375px the rise is 15px and they
   were still spending 150px on it, three times the padding of every band
   around them. Derived from the geometry they exist for, all three cases come
   out right on their own, and they now scale with --band-pad-k like everything
   else.

   FOUR classes deep on purpose. `.sw-band--slant-bottom + .sw-band >
   .sw-band__inner` sets padding-top: 20px at (0,3,0) to pull a band up under a
   diagonal, and three of these four sit exactly there — at (0,3,0) this would
   only tie and lose on source order. Naming the slant class as well also says
   out loud that you only clear a slant on a band that has one. */
.sw-band.sw-band--slant-top.sw-band--clear-slant > .sw-band__inner {
  padding-top: calc(var(--slant-rise) + (var(--band-pad-top) * var(--band-pad-k)));
}

.sw-band.sw-band--slant-bottom.sw-band--clear-slant > .sw-band__inner {
  padding-bottom: calc(var(--slant-rise) + (var(--band-pad-bottom) * var(--band-pad-k)));
}

/* --pad-balance gives the TOP the same figure the bottom just got.

   Clearing a bottom slant pads only the bottom, so a short band ends up
   top-heavy: the stats band measured 65px above its figures and 131px of
   empty colour below them, with the content sitting in the top third of
   373px. On a tall band that is invisible; on a short one it looks like the
   content has slipped.

   OPT-IN, not folded into --clear-slant, because the fix costs height. The
   courses band is the other slant-bottom band with clearance and it is
   1060px with plenty of content — balancing it would add 66px to an already
   long page for no gain. Use this only where the band is short enough for
   the imbalance to show.

   FIVE classes deep: --clear-slant above is (0,3,0) and the 20px pull-up for
   a band under a diagonal is also (0,3,0), so anything less would tie. */
.sw-band.sw-band--slant-bottom.sw-band--clear-slant.sw-band--pad-balance > .sw-band__inner {
  padding-top: calc(var(--slant-rise) + (var(--band-pad-bottom) * var(--band-pad-k)));
}

/* Every band is inset by --page-inset with --radius-2xl. There are no
   full-bleed content sections: the header and footer run edge to edge as the
   page's bookends, and everything between them sits on the same margin.

   The old .sw-band--bleed modifier has been removed from the markup rather
   than kept as a no-op, so the stylesheet does not describe a behaviour the
   site no longer has. */

/* A band that runs edge to edge instead of sitting as an inset tile. The old
   `--bleed` modifier was deleted from the markup and the stylesheet years ago and
   survives only in comments, so writing that class on a section did nothing at
   all — worth knowing before reaching for it again.

   Square corners are correct here and not a breach of the no-sharp-corners rule:
   both sides run off-screen and the top and bottom meet their neighbours, so
   there is no visible corner to round. The inner picks up --page-inset so content
   still lines up with the inset bands above and below. */
.sw-band--flush {
  margin-inline: 0;
  border-radius: 0;
}

.sw-band--flush > .sw-band__inner {
  padding-inline: calc(var(--band-pad-x) + var(--page-inset));
}

/* Two flush bands in a row have NO EDGE between them — same ground, no inset,
   no corners, no colour change. Everywhere else on the site the band gap is
   punctuated by a tile edge or a change of surface, so padding + --band-gap +
   padding reads as three events. Across an invisible seam it reads as one
   void: on Contact that was 266px of empty page between the last field of the
   enquiry form and the first contact tile.

   So the two bands close up: no gap between them, and a shorter padding on
   each side of the join. 48 + 48 = 96px at desktop, all of it inside the
   shorthand so --band-pad-k still shrinks it on a phone — which a literal
   margin would not do. The same problem Blog's tuned junction was solving by
   hand; this is it as a rule.

   Contact is the only junction of this kind on the site today. These sit
   AFTER the --pad-* modifiers deliberately: both are (0,2,0), so if a band
   ever needs both, source order has to give this one the last word. */
.sw-band--flush + .sw-band--flush { margin-top: 0; }
.sw-band--flush:has(+ .sw-band--flush) > .sw-band__inner { --band-pad-bottom: 48px; }
.sw-band--flush + .sw-band--flush > .sw-band__inner { --band-pad-top: 48px; }

/* ---- Slanted bands: exactly ONE slanted edge --------------------------
   The band is a plain rounded rectangle that clips. Inside it sits a skewed
   fill that bleeds past the OPPOSITE edge, so only one slanted edge is ever
   visible and the other three stay straight.

   Every corner still rounds: the fill is flush left and right, carries its
   own radius on the two corners of the slanted edge, and is oversized on the
   far edge so the band's own rounding clips the other two. Oversizing it
   sideways instead would push the slant out through the band's side above
   the corner curve and produce a sharp point.

   skewY only displaces points vertically, so flush left/right edges stay
   flush and perfectly vertical after the skew.

   The content is a SIBLING of the fill, not a child, so it never needs a
   counter-skew — no copy is ever slanted. Padding on the slant's side has to
   clear its deepest point or text will overhang the colour.
   -------------------------------------------------------------------- */

.sw-band--slant-bottom,
.sw-band--slant-top { background: transparent; }

.sw-band__fill {
  position: absolute;
  left: 0;
  right: 0;
  background: var(--band-fill, var(--surface-inverse));
  transform: skewY(var(--slant-h));
}

/* Straight top, slanted bottom falling left to right. */
.sw-band__fill--bottom {
  top: calc(-1 * var(--fill-overhang));
  bottom: 0;
  border-bottom-left-radius: var(--radius-2xl);
  border-bottom-right-radius: var(--radius-2xl);
  transform-origin: right bottom;
}

/* When a slanted-bottom band is followed by a slanted-top one, the two edges
   lean AWAY from each other, so the visible gap is --band-gap plus the full
   slant rise at every point across the width — uniformly too big. This tucks
   the band up by that rise so the gap reads the same as everywhere else.
   The rise is the band's width times tan(--slant-h); 0.0419 is tan(2.4deg),
   restated numerically because CSS has no tan(). */
.sw-band--tuck {
  margin-top: calc(var(--band-gap) - (min(100vw, var(--page-max)) - 2 * var(--page-inset)) * 0.0419);
}

/* Straight top, slanted bottom RISING left to right — the mirror of
   --bottom. The left end reaches the band's bottom-left corner and picks up
   its rounding; the right end is flush with the band's side, so it carries
   its own. */
.sw-band__fill--bottom-rise {
  top: calc(-1 * var(--fill-overhang));
  bottom: 0;
  border-bottom-left-radius: var(--radius-2xl);
  border-bottom-right-radius: var(--radius-2xl);
  transform: skewY(calc(-1 * var(--slant-h)));
  transform-origin: left bottom;
}

/* Slanted top falling left to right, straight bottom. */
.sw-band__fill--top {
  top: 0;
  bottom: calc(-1 * var(--fill-overhang));
  border-top-left-radius: var(--radius-2xl);
  border-top-right-radius: var(--radius-2xl);
  transform-origin: left top;
}

/* Slanted top RISING left to right — high on the right, low on the left.
   The mirror of --top. Pairs with --bottom-rise above it: both edges lean
   the same way, so the diagonal carries straight down the page. */
.sw-band__fill--top-rise {
  top: 0;
  bottom: calc(-1 * var(--fill-overhang));
  border-top-left-radius: var(--radius-2xl);
  border-top-right-radius: var(--radius-2xl);
  transform: skewY(calc(-1 * var(--slant-h)));
  transform-origin: right top;
}

/* ============================================================
   Sections — content sitting directly on the page canvas
   ============================================================ */

/* Vertical rhythm uses MARGIN, not padding, so that a section sitting
   next to a band collapses into a single --band-gap instead of stacking
   the section's padding on top of the band's margin. */
.sw-section {
  max-width: var(--content-max);
  margin: var(--band-gap) auto;
  padding: 0 var(--band-pad-x);
}

/* A section sandwiched between two slanted bands needs more room than the
   standard rhythm: the slants lean into the gap, so an even --band-gap on
   each side reads as tight. Used by "What is Sweet". */
.sw-section--airy { margin-block: calc(var(--band-gap) * 1.75); }

.sw-split {
  display: grid;
  /* minmax(0, 1fr) not 1fr: a bare 1fr track refuses to shrink below its
     content's min-content width, so wide children overflow the container. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 56px;
  align-items: center;
}

/* Without this a grid track cannot shrink below its content's min-content
   width, so one wide child (an image slot, a long word) pushes the whole
   column past the viewport at narrow widths. */
.sw-split > *,
.sw-figure,
.sw-figure__photo { min-width: 0; }

image-slot { min-width: 0; width: 100%; }

.sw-split--cta { grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); gap: 48px; }

/* Copy-heavy split: three paragraphs beside a single cut-out. The text gets the
   larger share, because at an even 1fr the prose column ran short and tall while
   the artwork column had space going spare. */
.sw-split--copy { grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr); }

/* The mirror of --copy: the SECOND column carries the weight. For a split whose
   right-hand side is a 2x2 of its own — the four differentiators on the Pearson
   page — where an even split leaves each sub-column about 290px, roughly 30
   characters a line. At 1.15fr they get ~338px and 35-42.

   A CLASS, not an inline style, and that is the whole point of it being here.
   Inline `grid-template-columns` beats the stylesheet, including the
   max-width:1000px rule that collapses .sw-split to one column — so written
   inline this held two columns all the way down and the copy ended up 122px
   wide on a phone. Declared here, before that media query, it is overridden by
   it exactly as it should be. */
.sw-split--aside { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); }

/* One band only, 1001-1180px, and the bounds are the point. A .sw-split--aside
   holding a 2x2 divides TWICE: at 1024 the split gives the aside 492px and the
   2x2 halves that again to 218px — about 22 characters a line. Collapsing the
   inner grid there gives the full 492px, around 60.

   Bounded below 1001 because the split itself stacks there and the aside gets
   the whole band: one column would then be ~656px, about 80 characters, too
   wide rather than too narrow. Bounded above 1180 because by then the split is
   wide enough for the 2x2 to breathe on its own. */
@media (min-width: 1001px) and (max-width: 1180px) {
  .sw-split--aside .sw-diff { grid-template-columns: minmax(0, 1fr); }
}

/* Copy left, media right — the mirror of the default, for when two .sw-splits
   would otherwise sit back to back with their pictures down the same edge.

   On About Sweet the evidence band and "What you get" were exactly that:
   measured, both had their media LEFT, both headings at x=748, both on white,
   with no divider between them. 1,184px that read as one section with two
   pictures stacked down its left side, which killed the zig-zag at the very
   point the page should be establishing it.

   Done with `order`, not by moving the markup, and that is the point. Stacked,
   every other section on this page puts its picture above its copy — the book
   panel, the collage, the bleeding photo on the managed band. Swapping the DOM
   would have made this one section read text-then-picture on a phone, out of
   step with all of them. `order` only applies while the grid has two columns,
   so the stacked order is untouched.

   Scoped above the stacking breakpoint for the same reason: below 1000px the
   grid is a single column, where `order` would reverse the stack. */
@media (min-width: 1001px) {
  .sw-split--media-right > :first-child { order: 2; }
}

/* A single block of copy raised into a card, used as emphasis rather than as a
   default wrapper — "The qualification" on the PGW page, which is the half that
   carries the call to action. Its neighbour stays as plain copy on the band: if
   both halves were tiled, neither would stand out and the tile would be doing
   nothing but drawing a box.

   Paper Ivory on the white band: an ivory card on white needs no stroke and no
   shadow to have an edge, the same as the team grid. The split keeps
   `align-items: start`, so the tile hugs its own content instead of stretching to
   match the taller column beside it and trailing empty space. */
.sw-infotile {
  padding: clamp(24px, 2.6vw, 34px);
  border-radius: var(--radius-lg);
  background: var(--paper-ivory);
}

/* An .sw-infotile whose copy is not final yet. Marked, never silently
   provisional — the same principle as .sw-topic__ph, and it
   carries the identical soft ruled edge (inset 2px at 12% Focus Black) so the
   three read as one family of "this is not finished".

   The flag is the point. Draft copy that looks like finished copy is how a
   placeholder ships: it has to be obvious at a glance, in the page, to someone
   who was not in this conversation. */
/* UNUSED as of 11 October 2026, and deliberately kept. The WorkSkills "The
   qualification" tile carried both of these until Rebecca signed that copy off
   ("we can remove the draft badge"); nothing on the site wears them now.

   Unlike .sw-band--bleed further down, which was deleted from the stylesheet
   years ago and survives only as a trap for anyone who writes it on a section,
   these two still work. Put them on a tile and it gets the ring and the pill.
   That is the point of keeping them: the next piece of copy that goes live
   before it is confirmed should be visibly marked, not quietly shipped. */
.sw-infotile--draft { box-shadow: inset 0 0 0 2px rgba(11, 30, 47, 0.12); }

.sw-infotile__flag {
  align-self: start;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  background: var(--simple-white);
  box-shadow: inset 0 0 0 2px rgba(11, 30, 47, 0.12);
  font-size: 12px;
  font-weight: var(--weight-medium);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* The fan of PGW covers beside "What it is" — now IN A BOX (Rebecca,
   11 October 2026: "can we add a box to the what it is photo").

   It had none, deliberately, on the reasoning that a cut-out with its own drop
   shadow needs no plate. What that missed is the rest of the page: the
   classroom photo in the curriculum band below is a 636x477 panel at 4:3, and
   .sw-bookcycle on About Sweet is the same panel holding these same covers.
   Unboxed, this was the one picture on the page with no edge and no size of its
   own — 380px square, floating, while everything near it was a 4:3 block.

   So it takes the house panel: 4:3 and --radius-xl, which makes it exactly the
   636x477 of the classroom photo further down the page.

   PAPER IVORY, though, and NOT the Curious Blue that .sw-bookcycle uses for
   these same covers on About Sweet. Built in blue first and compared against
   ivory, violet and Focus Black in place:

     - Curious Blue loses twice. The PGW hero immediately above this section is
       Curious Blue AND already has this cover artwork in it, so the blue box
       repeats both of the hero's ingredients one band later. It also fights the
       blue inside the artwork — the right-hand book and the girl's top.
     - Future Violet fights the violet book on the right of the fan.
     - Focus Black makes the covers pop, but the page already has two navy
       bands and the hero's own right-hand panel is navy with these covers on
       it. A fourth dark block.
     - Paper Ivory is the only one where all five cover colours — green, red,
       pink, violet, blue — read independently, and the drop shadow reads as a
       shadow rather than as a smudge.

   About Sweet keeps its blue: nothing sits above that bookcycle to repeat.

   Padding, not a flush fill, for the reason recorded on .sw-bookcycle__slide:
   a cut-out touching its panel edge reads as clipped, which is the one thing a
   set of books must never look like. */
.sw-coverfan {
  position: relative;
  aspect-ratio: 4 / 3;
  min-width: 0;
  display: grid;
  place-items: center;
  padding: clamp(22px, 3vw, 40px) 8%;
  border-radius: var(--radius-xl);
  background: var(--paper-ivory);
  overflow: hidden;
}

/* image-slot carries aspect-ratio 3/2 from shape="rect" and height:100% on its
   own :host; both have to go or the covers size from their own width and
   ignore the panel. Same fix as .sw-splitphoto and .sw-bookcycle. */
.sw-coverfan image-slot {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  display: block;
}

/* Rebecca, 15 October 2026: "the who we support image on that page can be full
   bleed". .sw-coverfan was built to hold a FAN OF BOOK COVERS — the 8% side
   padding and the ivory ground are the shape the covers sit on, and England's
   "What Sweet offers" still needs them. A photograph wants neither: it fills
   the panel and the rounded corner does the rest. Modifier, not an override of
   the base, so the two uses cannot drift into each other. */
.sw-coverfan--bleed {
  padding: 0;
  background: transparent;
}

.sw-stack {
  display: flex;
  flex-direction: column;
  gap: 22px;
  align-items: flex-start;
}

/* ============================================================
   Type — two large sizes for the whole page, not five
   ============================================================ */

.sw-display {
  margin: 0;
  font-size: clamp(38px, 5.2vw, 82px);
  line-height: 0.98;
  letter-spacing: -0.03em;
  font-weight: var(--weight-regular);
  text-wrap: balance;
}

.sw-h2 {
  margin: 0;
  font-size: clamp(30px, 3.6vw, 52px);
  line-height: 1.04;
  letter-spacing: -0.03em;
  font-weight: var(--weight-regular);
  text-wrap: balance;
}

.sw-h3 {
  margin: 0;
  font-size: clamp(21px, 1.8vw, 26px);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: var(--weight-semibold);
}

/* The brand's heading device: regular weight with the key phrase bold. */
.sw-em { font-weight: var(--weight-bold); }

.sw-eyebrow {
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.sw-lede {
  margin: 0;
  font-size: clamp(17px, 1.5vw, 21px);
  line-height: 1.4;
  color: var(--text-secondary);
  text-wrap: pretty;
}

/* Standalone text link — the underlined "read on" affordance used outside
   prose, e.g. "All posts" and the outbound Pearson link. */
/* A real underline, not a bottom border.

   `display: inline-block` plus `border-bottom` drew the rule along the bottom of
   the element's box, and an inline-block's box is the full line height — so in
   body copy at `--lh-relaxed` the rule sat roughly 8px under the text and the gap
   grew or shrank with whatever line-height it happened to inherit.
   `text-underline-offset` measures from the baseline instead, so the gap is the
   same everywhere and stays put. It also lets a link that wraps underline both
   lines, which an inline-block could never do because it does not wrap. */
.sw-textlink {
  font-size: 17px;
  font-weight: var(--weight-medium);
  color: var(--text-primary);
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-decoration-thickness: var(--border-width);
  text-underline-offset: 0.16em;
  transition: color var(--duration-base) var(--ease-out);
}

.sw-textlink:hover { color: var(--violet-2); }

/* Rebecca, 15 October 2026: "in the checkout the back to basket button is very
   close tot he place order button". It was TOUCHING it — a measured gap of
   exactly 0px.

   The cause is worth writing down because it is invisible in the markup: the
   link carried `margin-top: 18px` as an inline style, and that style did
   nothing at all. .sw-textlink is an INLINE element, and vertical margins do
   not apply to non-replaced inline boxes — the declaration is valid, it parses,
   it shows in devtools, and it has no effect. The basket's "Keep shopping" link
   carried the same dead 26px.

   A standalone back-link becomes a block of its own so its spacing is real.
   Links inside prose keep .sw-textlink alone and stay inline, because they have
   to wrap with the sentence around them. */
/* Same 44px floor for the medium buttons. The DS bundle sets their padding
   (13px 22px) at runtime, which lands them on 42px; min-height lifts them
   without overriding a value the bundle owns. --lg is already 52px. */
.sw-btn--md { min-height: 44px; }

/* A NATIVE <select> IGNORES BOTH THE PADDING AND min-height. WebKit lays a
   control with `appearance: auto` out from its own metrics, so the two selects
   on this site rendered 24px tall against 45px for the text fields beside them,
   and `min-height: 44px` alone did nothing at all — the rule matched, it was in
   the cascade, and the computed value stayed at the UA's 18px.

   `appearance: none` is what makes the box properties apply. That also removes
   the native chevron, so one is drawn back on as a background image, in Focus
   Black, with right padding to clear it. Found in the pre-launch check with the
   chips and the buttons; 44px is the touch-target floor. */
select.sw-input {
  appearance: none;
  -webkit-appearance: none;
  min-height: 44px;
  padding: 12px 42px 12px 16px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='7' viewBox='0 0 11 7' fill='none'%3E%3Cpath d='M1 1l4.5 4.5L10 1' stroke='%230B1E2F' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
}

.sw-textlink--back {
  display: inline-block;
  margin-top: 26px;
}

/* ── Order received ─────────────────────────────────────────────────────────
   Rebecca, 15 October 2026: "we dont have a page that comes up to say order
   confirmed" — "Place order" pointed at href="#", so the checkout went
   nowhere at all.

   The tick is Spark Lime behind Focus Black, which is the brand's own
   affirmative pairing and 15.1:1. It is aria-hidden: "Order received" is
   already the h1, and a screen reader announcing a decorative tick before it
   adds nothing. */
.sw-tick {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: var(--radius-pill);
  background: var(--spark-lime);
  color: var(--focus-black);
  box-shadow: var(--shadow-tag-sm);
}

.sw-tick svg { width: 28px; height: 28px; }

/* The same rows as the checkout summary, so the order the visitor just placed
   looks like the thing they were just looking at. Capped at the copy column
   rather than running the full band: it is a receipt, not a table. */
.sw-ordercard {
  margin-top: 0;
  padding: clamp(18px, 2vw, 24px) clamp(20px, 2.2vw, 26px);
  border-radius: var(--radius-lg);
  background: var(--paper-ivory);
}

/* Inline in body copy the link should be the size of the sentence it is in;
   17px is for the standalone ones ("← All posts" and the like). */
.sw-article .sw-textlink,
.sw-body .sw-textlink,
.sw-lede .sw-textlink { font-size: inherit; }

.sw-body   { margin: 0; font-size: 16px; line-height: 1.45; color: var(--text-secondary); }
.sw-meta   { font-size: 15px; color: var(--text-muted); }
.sw-on-dark .sw-lede,
.sw-on-dark .sw-body { color: rgba(255, 255, 255, 0.72); }
/* .sw-h3 was missing from this list until 11 September 2026, so a subheading
   on a dark panel rendered in Focus Black ON Focus Black — present, correct,
   and completely invisible. Fixed here rather than per-component: the next
   dark panel with an h3 in it would have hit exactly the same thing. */
.sw-on-dark, .sw-on-dark .sw-h2, .sw-on-dark .sw-h3, .sw-on-dark .sw-display { color: var(--text-on-dark); }
/* Mid-tone panels (violet, blue, green) sit between the light and dark cases
   and break --text-secondary: 66% black on Future Violet measures 3.6:1,
   under the 4.5:1 AA threshold for body sizes. Full Focus Black on the same
   violet is 7.1:1. Use .sw-on-mid on any saturated mid-tone surface. */
.sw-on-mid .sw-lede,
.sw-on-mid .sw-body { color: var(--text-primary); }

/* .sw-meta was missing from this list until 9 October 2026, found when the
   homepage testimonial became a violet card: --text-muted is 46% black and
   measures 2.36 : 1 on Future Violet, 2.51 on Progress Green, 2.73 on Curious
   Blue and 2.89 on Spark Lime — failing AA on every mid-tone the site uses.

   Full Focus Black, matching what the blog cards already do for exactly this
   reason: the meta line is the smallest text on a card, so it fails first.
   0.8 alpha would also pass everywhere (4.88 on violet, the worst case) and
   keep a little hierarchy, but one value matching the documented precedent is
   worth more than the nuance. */
.sw-on-mid .sw-meta { color: var(--text-primary); }

/* 0.80, not the 0.72 this used to be. The old value was recorded here as
   "only just holds" — 4.12 : 1 on Future Violet, under the 4.5 threshold, and
   4.71 on Progress Green. It stayed because no band on the site was violet.
   The Pearson CTA is now, so it stopped being theoretical: measured on the
   live band at 0.72 it failed.

   0.80 gives 4.88 : 1 on violet, the darkest of the three mid-tones, so the
   other two clear it by more. Raising the alpha only ever increases contrast
   against a light ground, so no mid-tone band loses anything.

   It matters more than the number suggests: the eyebrow is small uppercase
   with letter-spacing, which is the hardest case to read of anything on the
   page — the same reason .sw-meta was fixed. */
.sw-on-mid .sw-eyebrow { color: rgba(11, 30, 47, 0.80); }

.sw-on-dark .sw-eyebrow { color: rgba(255, 255, 255, 0.5); }
/* Form field labels and helper text default to Focus Black, which is
   invisible on a dark panel. */
.sw-on-dark .sw-field__label { color: var(--text-on-dark); }
.sw-on-dark .sw-field__help { color: rgba(255, 255, 255, 0.6); }
.sw-on-dark .sw-cobrand__x { color: rgba(255, 255, 255, 0.45); }

/* A text link on a dark band. `.sw-textlink` inherits Focus Black, so on Focus
   Black it measured 1:1 — invisible, not merely low contrast. Found on the
   Contact hero, 16 September 2026, the first time a text link was put on a dark
   band anywhere on the site. White at 14.7:1; the underline keeps it readable
   as a link without relying on colour alone. */
.sw-on-dark .sw-textlink { color: var(--text-on-dark); }
.sw-on-dark .sw-textlink:hover { color: var(--spark-lime); }

/* Same failure, same place: an outline button is Focus Black type inside a
   Focus Black stroke, so on a dark band BOTH the label and the outline
   disappear — 1:1, not merely low. Found on the Sweet in England hero,
   16 September 2026, where "Talk to our team" sat invisible beside a working
   dark button.

   Hover has to invert the other way too. The design system fills the button
   with Focus Black on hover, which on a navy band is the button vanishing at
   the moment it is pressed.

   HALF-FIXED UNTIL 11 OCTOBER 2026, and it is worth saying why, because the
   rule looked right. `border-color` was doing nothing: the design system
   draws this button's ring as an INSET BOX-SHADOW and leaves border-width at
   0, so there was no border to recolour. The label went white as intended and
   the stroke stayed Focus Black — invisible on navy — for most of a month, on
   the very button the comment above names. Measured: border-top-width 0px,
   box-shadow rgb(11, 30, 47) 0 0 0 1.5px inset.

   border-color is kept in case the component ever switches to a real border;
   the box-shadow is what actually paints today. */
.sw-on-dark .sw-btn--outline {
  color: var(--text-on-dark);
  border-color: var(--text-on-dark);
  box-shadow: inset 0 0 0 var(--border-width) var(--text-on-dark);
}
.sw-on-dark .sw-btn--outline:hover:not(:disabled) {
  background: var(--simple-white);
  color: var(--focus-black);
}

/* A FILLED DARK BUTTON ON A DARK BAND IS THE BAND. .sw-btn--dark is
   `background: var(--focus-black)` in the design system bundle, and
   --surface-inverse IS --focus-black, so the pill vanishes and leaves its
   white label floating. Measured on the Alternative Provision and Sweet in
   England heroes: fill rgb(11,30,47) against band rgb(11,30,47), 1.00:1, with
   box-shadow: none — no fill, no ring, nothing but the words.

   Both of those now use variant="primary" instead, which is the site's own
   answer for a primary action on navy. This rule is the net, so the next one
   cannot fail silently: scoped to the hero because a hero never contains a
   light card, and a blanket .sw-on-dark rule would turn a dark button inside
   a white .sw-unit white-on-white — the light-island trap, the other way up. */
.sw-heroband.sw-on-dark .sw-btn--dark {
  background: var(--simple-white);
  color: var(--focus-black);
}

/* And the same fault inverted: Spark Lime on a WHITE CARD measures 1.12:1, so
   a primary button inside a light island on a dark band is a pale shape with
   a readable label and no edge. Found on the WorkSkills country cards, which
   now use variant="dark"; this keeps the next one honest. */
.sw-on-dark .sw-unit .sw-btn--primary,
.sw-on-dark .sw-iqa .sw-btn--primary {
  background: var(--focus-black);
  color: var(--text-on-dark);
}

/* ============================================================
   Tiles — media nested inside a card, with the radius arithmetic
   done for you (RULE 2)
   ============================================================ */

.sw-tile {
  position: relative;
  margin: var(--tile-inset) var(--tile-inset) 0;
  border-radius: calc(var(--radius-lg) - var(--tile-inset));
  overflow: hidden;
}

/* ============================================================
   Card internals — so every card in a row lines up
   ============================================================ */

.sw-card-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  padding: 22px 26px 28px;
}

/* Pushes the actions to the bottom so buttons align across a row
   even when titles wrap to different line counts. */
.sw-card-actions {
  margin-top: auto;
  padding-top: 8px;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.sw-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }

/* Two equal columns, stretched. .sw-split would not do here: it is
   align-items: center, so two cards of different heights float against each
   other instead of squaring off, and these two are a matched pair the reader
   is meant to compare. */
.sw-grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }

/* ============================================================
   Team cards
   ============================================================ */

/* ============================================================
   Shop
   ============================================================ */

/* Products are grouped by WHO is buying — new centre, existing centre, or
   via an IQA — rather than listed flat with filters. Someone arriving here
   knows which of those they are, so they can find their product without
   understanding the catalogue first. */
/* Left-aligned, not centred: the grid sits beside the filter sidebar, and
   centring pushes the products away from the controls that drive them. */
.sw-shop-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
}

/* Two-up, not three. Every audience group holds two products, so a three-wide
   track left one empty cell in every row. Against the old white page that was
   invisible; on the coloured ground it is a hole in the panel. Two-up fills
   each row exactly and gives the cut-out books room to be seen. */
.sw-shop-grid > * { flex: 0 0 calc((100% - 24px) / 2); }

@media (max-width: 720px) {
  .sw-shop-grid > * { flex: 0 0 100%; }
}



/* Three-up, for a row of exactly three. The two-up rule above is right for the
   shop, where every audience group holds two products — but the Pearson page
   lists THREE qualifications, and at two-up the third is a half-width orphan
   with a half-width void beside it at every width above 720px. That is the
   ragged row the two-up rule exists to prevent, so the answer is a track count
   that divides the content, not a third card squeezed into a grid built for
   pairs.

   It goes 3 to 1, never 3 to 2 — two columns would reintroduce the orphan at
   the middle widths, which is the whole problem.

   DOUBLED CLASS on purpose. The base rule and its 1000px restatement are both
   (0,1,0) and one of them sits inside a later media query, so a single-class
   modifier would lose to it however late it came in the file. Same trap as
   .sw-collage__tile--mark. */
.sw-shop-grid.sw-shop-grid--three > * {
  flex: 0 0 calc((100% - 48px) / 3);
  /* Required, not tidiness. A flex child's default min-width: auto floors it at
     its own min-content, and these cards hold an image-slot — so at 760px the
     first two ignored their 200px basis, sat at 278px, and pushed the third
     onto a row of its own at a DIFFERENT width. Precisely the ragged row this
     modifier exists to prevent, produced by the modifier itself. It is the
     trap already recorded under Layout; two-up never hit it because those
     widths are always above the min-content. */
  min-width: 0;
}

@media (max-width: 720px) {
  .sw-shop-grid.sw-shop-grid--three > * { flex: 0 0 100%; }
}

/* A qualification whose books do not exist yet still needs something in the
   photo tile. The Skills Suite card carries the official Curriculum for Wales
   Skills Suite mark instead — the other two tiles show the product because the
   product IS how Sweet delivers them, and this one shows what identifies the
   qualification, which is the honest substitute.

   Deliberately NOT an unfilled image-slot (that renders the component's grey
   drop-target wash and a "Drop a photo" prompt, which is editor furniture) and
   deliberately not a "cover in design" placeholder — that
   one is right on the shop, where the subject IS a forthcoming book, and wrong
   here, where the subject is a qualification that already exists and is
   already recognised.

   TWO specificity traps here, both hit on the first attempt. `.sw-product
   .sw-tile` is (0,2,0) and sets height: 176px AND padding: 14px, so a
   single-class `.sw-product__mark` cannot change the padding — the modifier
   has to be scoped through .sw-product too. And a `max-height: 100%` image
   inside a GRID whose single implicit row is auto-sized resolves its 100%
   against the ROW, which the image itself has just made 325px tall: the
   constraint is circular, so the mark rendered at full size and burst out of a
   176px tile. Sizing the img to the box and letting object-fit letterbox it
   inside removes the dependency on how the row was sized. */
.sw-product .sw-tile.sw-product__mark { padding: 20px 26px; }

.sw-product__mark img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}


/* The whole card is the link — a small "View" target would be a needlessly
   fiddly hit area for the primary action on the page. */
.sw-product {
  display: flex;
  flex-direction: column;
  height: 100%;
  /* Course colour, set from data-course below; beige is the fallback. */
  background: var(--paper-ivory);
  /* No outline. The card is now a solid colour block against a beige ground,
     so the colour itself is the edge — a black stroke on top of that reads as
     a second, competing boundary. */
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform var(--duration-base) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out);
}

.sw-product:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-pop);
}

/* ── No strokes on cards ─────────────────────────────────────────────────────
   Card surfaces across the site carry no outline. This includes the design
   system's own Card component, overridden here rather than in _ds/, which is
   generated output.

   A white card on a white band has no edge of its own, so removing the stroke
   would make it disappear. --shadow-soft replaces it: quieter than a 2px black
   rule, and it still separates the card from the page. Cards that already sit
   on a colour (the shop's course cards, the ivory variants) need neither and
   get no shadow.

   Interactive controls keep their strokes — buttons, filter chips, inputs, the
   nav basket pill, the dropdown tiles and text links. Those are not cards, and
   an unbordered button stops looking pressable. */
.sw-card { border: 0; }
.sw-card--white { box-shadow: var(--shadow-soft); }

/* Product tiles show the books whole (fit="contain" on the slot), so they need
   inset breathing room — a cut-out set touching the tile edge reads as clipped.
   Concentric rule: the tile is --radius-sm inside a --radius-lg card, and this
   padding is the slot's own inset, not a nested shape, so no further step.

   The photo box is white against the card's beige, so it reads as a lit stage
   under the product rather than another panel. */
/* 176px, down from 260px on 11 September 2026. Rebecca's note was that the
   icons are very large and you cannot see the range — and the constraint here
   is VERTICAL, not horizontal. Seven products sit in four groups of two, one
   and two, so a three-across track cannot help: it would leave a ragged gap in
   every row and show no more products than two-across does. Shortening the
   card is what actually gets more of the catalogue on one screen. The books
   are cut-outs on fit="contain", so they lose no content — they are simply
   smaller, which is the ask. */
.sw-product .sw-tile { height: 176px; background: var(--simple-white); padding: 14px; }

/* Product cards tighten with them; the roomy card padding was sized against a
   260px photograph and left a visible skirt under a 176px one. */
.sw-product .sw-card-body { gap: 10px; padding: 18px 22px 22px; }

/* Card colour by course, not by position: Curious Blue for Personal Growth and
   Wellbeing, Progress Green for WorkSkills. The colour then means something —
   scanning the grid tells you which course a product belongs to before you
   read a word of it — where a rotation was only decoration.

   Set from data-course on the card wrapper, so it stays right no matter how
   the products are ordered or filtered. e-Sweet is a PGW product and takes
   lime with the rest.

   The photo box stays white inside both — it is what holds the cut-out, and
   the only constant across the grid. */
/* Homepage course tiles hold photographs, so they run edge to edge — the inset
   and white surround were for the cut-out book sets that were there before,
   and a photograph floating inside a white border reads as a mistake. */
.sw-coursetile {
  height: 240px;
  background: var(--simple-white);
}

[data-course="pgw"] .sw-product { background: var(--accent-blue); }
[data-course="workskills"] .sw-product { background: var(--accent-green); }
/* Future Violet for the Skills Suite, the same colour its own page and its blog
   category already use — so the three cards on the Pearson page read as the
   three course identities, not as decoration. It shares the violet with
   e-Sweet, which is not a clash: they never appear in the same grid, e-Sweet
   being a product and this a qualification. */
[data-course="wave3"] .sw-product { background: var(--accent-violet); }

/* e-Sweet is the odd one out: a PGW product, but digital rather than printed.
   Future Violet separates it from the printed sets at a glance, which is the
   distinction that actually matters when someone is choosing. Placed after the
   course rules so it wins on source order — same specificity. */
[data-type="esweet"] .sw-product { background: var(--accent-violet); }
.sw-product__hero[data-course="esweet"] { background: var(--accent-violet); }

/* The card eyebrow is violet type, which sits weakly on both of these, and
   Progress Green is a mid-tone where 66% black measures under AA. Both drop to
   full Focus Black on a coloured card. */
[data-course] .sw-product .sw-body,
[data-course] .sw-product .sw-product__course { color: var(--focus-black); }

.sw-product__course {
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--violet-2);
}

/* ---- The shop as one wall of cards ---------------------------------------
   Rebecca chose this from four layouts, 14 October 2026. The shop was 4,552px
   — 4.6 screens for seven products — because a 248px sidebar held the grid to
   two cards a row at every width, the cards carried a 176px tile plus a
   description plus an endorsement line, and four sections each repeated an h2
   and a standfirst. The sidebar is gone, the cards are compact, and there is
   one grid.

   FOUR ACROSS, and the card count is built around it: seven products plus ONE
   coming-soon card is eight, which divides exactly. Two coming-soon cards
   would make nine and leave the 4/4/1 orphan that .sw-shop-grid--three, the
   homepage .sw-grid-4 and this file's two-up rule all exist to prevent.

   Scoped to --wall throughout. .sw-product and .sw-shop-grid are used on ten
   other pages — both product pages and the homepage — and none of them want
   any of this.

   THIS BLOCK HAS TO SIT AFTER THE [data-course] RULES ABOVE, and that is the
   only reason it is this far down the file. `[data-course="pgw"] .sw-product`
   and `.sw-shop-grid--wall .sw-product` are both (0,2,0): an exact tie, which
   the later rule wins. Written above them, every card body came out in its
   course colour and every tile stayed 176px and white. Same trap as
   .sw-collage__tile--mark and .sw-shop-grid--three. Move this block back up
   and the layout breaks silently. */
.sw-shop-grid--wall { gap: 18px; margin-top: 26px; }

/* min-width: 0 is REQUIRED, not tidiness — the same trap recorded on
   .sw-shop-grid--three. A flex child's default min-width: auto floors it at its
   own min-content width, and these cards hold an image-slot, so the basis was
   simply ignored: the computed flex-basis read calc(25% - 13.5px) = 318px and
   the cards rendered at 463px, four per row became two, and the page came out
   3,009px instead of 1,470px. */
.sw-shop-grid--wall > * { flex: 0 0 calc((100% - 3 * 18px) / 4); min-width: 0; }

/* Seven products divide into four-up as 4 then 3 — a last row three-quarters
   full, which is fine. They do NOT divide into three-up or two-up: both leave
   one card alone on the last row, which is the orphan this file objects to in
   three other places. It appeared the moment the Skills for Life and Skills
   for Work card came out of the grid on 14 October 2026.

   The lone card is centred rather than left hanging. Full rows fill exactly so
   centring does nothing to them, which is why this is safe to put on the
   container — and it stays OFF at four-up, where the last row holds three and
   must keep the same left edge as the row above it. Same answer as .sw-collage,
   and robust while the filter is hiding cards, where a :last-child rule is not:
   the last card in the DOM is often not the last one on screen. */
@media (max-width: 1180px) {
  .sw-shop-grid--wall { justify-content: center; }
  .sw-shop-grid--wall > * { flex-basis: calc((100% - 2 * 18px) / 3); }
}

@media (max-width: 880px) { .sw-shop-grid--wall > * { flex-basis: calc((100% - 18px) / 2); } }
@media (max-width: 560px) { .sw-shop-grid--wall > * { flex-basis: 100%; } }

/* White, where the grouped shop gave each card its course colour. A 4px
   course-coloured bar above the tile replaced that wash on 14 October 2026 and
   came off the same day — Rebecca: "i dont like the colour tabs above the
   images". The course is named in full on every card, so nothing is lost but
   the decoration. */
.sw-shop-grid--wall .sw-product {
  background: var(--simple-white);
  border-radius: var(--radius-xl);
  box-shadow: 0 1px 0 rgba(11, 30, 47, 0.08), 0 14px 34px -24px rgba(11, 30, 47, 0.5);
}

/* 4:3, where .sw-product .sw-tile is a fixed 176px. A ratio keeps every tile
   the same shape as the cards narrow through the three breakpoints; a fixed
   height would letterbox them. */
.sw-shop-grid--wall .sw-tile {
  height: auto;
  aspect-ratio: 4 / 3;
  margin: 0;
  padding: 14px;
  border-radius: 0;
  background: var(--paper-ivory);
}

/* Rebecca, 15 October 2026: "change the background of every other photo to
   navy". The cut-outs are the same either way; alternating the ground stops the
   wall reading as one flat block of ivory. nth-child runs in DOM order, so on a
   four-wide row the navy tiles land 2nd and 4th and the pattern staggers on the
   next row — which is the point. Specificity: this is (0,3,0) against the
   (0,2,0) `.sw-shop-grid--wall .sw-tile` above, so it wins outright. */
.sw-shop-grid--wall > *:nth-child(2n) .sw-tile { background: var(--focus-black); }

.sw-shop-grid--wall .sw-card-body {
  gap: 6px;
  padding: 16px 18px 18px;
}

.sw-shop-grid--wall .sw-product__course { font-size: 11px; color: var(--text-muted); }
.sw-shop-grid--wall .sw-h3 { font-size: 18px; line-height: 1.2; }
.sw-shop-grid--wall .sw-product__foot { padding-top: 12px; align-items: center; }
.sw-shop-grid--wall .sw-product__price { font-size: 21px; }
.sw-shop-grid--wall .sw-product__cta { padding: 8px 16px; font-size: 14px; }

/* Rebecca, 15 October 2026: "for the free items in the shop can we add a
   diagonal yellow banner on the right corner that says FREE".

   A 45-degree band across the top-right corner. It is wider than the corner it
   crosses and hangs off both edges, so the card's own overflow:hidden trims it
   to the rounded corner — that is what makes the ends look mitred rather than
   stuck on. The word is in the accessible name too (sw-visually-hidden text in
   the markup would double-read it, so the span itself carries the text and the
   price still says "Free" underneath).

   Spark Lime with Focus Black is 15.1:1 — the ribbon is small, uppercase and
   tracked out, which is exactly where contrast has to be highest. */
.sw-shop-grid--wall .sw-product { position: relative; overflow: hidden; }

.sw-ribbon {
  position: absolute;
  top: 20px;
  right: -46px;
  z-index: 2;
  width: 168px;
  padding: 6px 0;
  transform: rotate(45deg);
  background: var(--spark-lime);
  color: var(--focus-black);
  text-align: center;
  font-size: 12px;
  font-weight: var(--weight-medium);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  box-shadow: 0 8px 18px -10px rgba(11, 30, 47, 0.55);
}

/* ---- The Skills for Life / Skills for Work panel ------------------------
   Everything on this page is orderable except these two, so they sit below the
   grid rather than in it, and the sign-up that used to be a page-level panel
   belongs to them. */
.sw-signupbox--soon { margin-top: clamp(22px, 2.4vw, 32px); }

/* Doubled class on purpose: `.sw-signupbox { background: var(--paper-ivory) }`
   lives further down this file, and a single-class selector up here loses that
   tie on source order alone. (0,2,0) settles it wherever either rule sits. */
.sw-signupbox.sw-signupbox--soon { background: var(--accent-violet); }

/* Rebecca, 15 October 2026: "can we add a proper title to wave 3 reminder
   banner in the shop". It had one, but it was doing two jobs at once —
   "Skills for Life and Skills for Work — tell me when they land" named the
   products AND asked for the sign-up, at 20px, which read as a form label
   rather than a title. The products are the title now; the asking is the
   button's job ("Keep me posted") and the paragraph's. Same regular-weight
   headline with a bold tail that every other section on the site uses. */
.sw-signupbox--soon .sw-signupbox__h {
  font-size: clamp(24px, 2.4vw, 32px);
  line-height: 1.1;
}

.sw-signupbox--soon .sw-signupbox__p { margin-top: 10px; }

/* Rebecca, 15 October 2026: "can we make the wave three banner at the bottom
   purple". Accent Violet is a saturated mid-tone, so the same rule as .sw-on-mid
   applies — 66% black (--text-secondary / --text-muted) does not clear AA on it,
   and the white sign-up inputs need their own ground back. Full Focus Black on
   every piece of copy inside the panel. */
.sw-signupbox--soon .sw-signupbox__p,
.sw-signupbox--soon .sw-soonhead .sw-product__course,
.sw-signupbox--soon .sw-field__label { color: var(--focus-black); }

.sw-soonhead {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}

.sw-soonhead__mark {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: var(--radius-md);
  background: var(--simple-white);
}

.sw-soonhead__mark img { width: 22px; height: auto; }

.sw-soonhead .sw-product__course { color: var(--text-muted); margin-bottom: 6px; }

@media (max-width: 560px) {
  .sw-soonhead { grid-template-columns: minmax(0, 1fr); }
}

/* ---- The filter, as one row above the grid ------------------------------- */
.sw-filterbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px 24px;
  flex-wrap: wrap;
  margin-top: clamp(26px, 3vw, 36px);
}

.sw-filterbar__chips { display: flex; gap: 10px; flex-wrap: wrap; }
.sw-filterbar__count { font-size: 15px; color: var(--text-secondary); white-space: nowrap; }

.sw-product__foot {
  margin-top: auto;
  padding-top: 16px;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.sw-product__price {
  font-size: 22px;
  font-weight: var(--weight-semibold);
  letter-spacing: -0.02em;
}

.sw-product__price--lg { font-size: clamp(28px, 3vw, 40px); }

/* Unit price, shown once the quantity passes one so the headline figure is
   never mistaken for the price of a single item. */
.sw-product__each {
  margin-top: -8px;
  font-size: 16px;
  color: var(--text-secondary);
}

/* The card's action, as a pill button rather than a text link with an arrow.
   Every other control on the site is a pill; this was the one affordance still
   reading as underlined-ish text, on ~24 cards across 10 pages.

   A SPAN styled as a button, not an <a>: the whole .sw-product card is already
   one link, and a nested <a> inside it is invalid and unfocusable. So this is
   the visual affordance for the card's own link, which is also why it needs no
   focus style of its own — the card takes focus and the pill moves with it.

   Dark fill rather than outline: the cards are Curious Blue, Progress Green and
   Future Violet, all light mid-tones carrying Focus Black text, so an outline
   pill would be the fourth dark line in a small space. Filled, it is the one
   element with its own weight, white on Focus Black at 16.9:1.

   align-self keeps it to its content — in a flex column it would otherwise
   stretch the full card width and stop looking like a button at all. */
.sw-product__cta {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  background: var(--focus-black);
  color: var(--text-on-dark);
  font-size: 15px;
  font-weight: var(--weight-medium);
  transition: background var(--duration-base) var(--ease-out);
}

/* The whole card is the hit area, so the pill reacts to the CARD's hover, not
   its own — hovering the copy or the photo lights the button too. */
.sw-product:hover .sw-product__cta { background: var(--violet-2, #4B57C8); }

/* The foot aligns on baseline for a text link; a pill is a box, so it needs
   centre alignment or it sits a few pixels proud of whatever shares the row. */
.sw-product__foot { align-items: center; }

.sw-product__flag {
  align-self: flex-start;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
}

/* The product page hero echoes the card the visitor just clicked: the course
   colour as the surface, a white box holding the cut-out. Without the coloured
   frame a white box on a white page has no edge at all, and the product would
   look different here from the way it looked in the grid.

   Concentric rule: --radius-xl (32px) outer, 20px gap, so the inner box is
   12px — not --radius-sm, which would be 2px too tight. */
.sw-product__hero {
  border-radius: var(--radius-xl);
  overflow: hidden;
  padding: 20px;
  background: var(--paper-ivory);
  height: clamp(280px, 38vw, 520px);
}

.sw-product__hero[data-course="pgw"] { background: var(--accent-blue); }
.sw-product__hero[data-course="workskills"] { background: var(--accent-green); }

.sw-product__herobox {
  height: 100%;
  padding: 14px;
  border-radius: 12px;
  background: var(--simple-white);
}

/* Three-step explainer in the shop hero */
.sw-steps {
  list-style: none;
  margin: clamp(34px, 4vw, 52px) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 14px 30px;
}

.sw-steps li {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 17px;
  line-height: 1.35;
  flex: 1 1 260px;
}

.sw-steps__n {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-pill);
  background: var(--focus-black);
  color: var(--text-on-dark);
  display: grid;
  place-items: center;
  font-size: 15px;
  font-weight: var(--weight-semibold);
}

.sw-crumb {
  display: flex;
  gap: 10px;
  align-items: center;
  font-size: 15px;
  color: var(--text-muted);
}

.sw-crumb a { color: var(--text-primary); font-weight: var(--weight-medium); }
.sw-crumb a:hover { color: var(--violet-2); }

/* Add to basket is a link, so it cannot use :disabled — this is the visual
   and assistive equivalent while a required option is unchosen. */
[data-add].is-disabled { opacity: 0.45; pointer-events: auto; cursor: not-allowed; }
[data-add].is-disabled .sw-btn { pointer-events: none; }

.sw-inlineerr {
  /* Hidden by default via CSS, not the `hidden` attribute — the DC runtime
     strips bare boolean attributes when it re-renders. */
  display: none;
  margin: 0;
  font-size: 15px;
  font-weight: var(--weight-medium);
  color: var(--danger);
}

.sw-inlineerr.is-shown { display: block; }

.sw-buyrow { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; }

/* A row of buttons of equal standing. Distinct from .sw-buyrow, which bottom-
   aligns so a price can sit beside its button; here every child is a button
   and they align on their centres. */
.sw-btnrow { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }

.sw-note {
  padding: 14px 18px;
  border-radius: var(--radius-md);
  background: var(--paper-ivory);
  font-size: 15px;
  line-height: 1.45;
  color: var(--text-secondary);
}

/* Where a figure came from, credited under it. Plain small text, not a boxed
   note — .sw-note carries an ivory ground, which reads as a panel of its own
   on a dark band rather than a quiet credit. */
.sw-source {
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-secondary);
}
.sw-on-dark .sw-source { color: rgba(255, 255, 255, 0.55); }

/* Product info blocks — the bullets, postage and returns text carried over
   from the live product pages. */
.sw-infoblock { width: 100%; margin-top: 10px; }

.sw-infoblock__t {
  margin: 0 0 8px;
  font-size: var(--text-small);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.sw-infoblock__b p { margin: 0 0 8px; font-size: 16px; line-height: 1.5; color: var(--text-secondary); }
.sw-infoblock__b p:last-child { margin-bottom: 0; }

.sw-infolist { margin: 0; padding-left: 1.15em; }
.sw-infolist li { font-size: 16px; line-height: 1.5; color: var(--text-secondary); }
.sw-infolist li + li { margin-top: 6px; }
.sw-infolist li::marker { color: var(--text-muted); }

/* The list as a surface of its own. Bare bullets on a white band sat in open
   space with nothing holding them, so the right-hand column read as an
   afterthought beside a headline rather than as its counterpart.

   Paper Ivory, not white: a white tile on a white band has no edge at all, and
   the shadow that normally stands in for one is the wrong answer where the
   thing being marked out is a block of text. Ivory gives it a real edge and no
   extra ink. Generous padding, because the tile has to earn the space it now
   occupies — the bullets set tighter than the column they replaced. */
.sw-infolist--tile {
  padding: 30px 32px 30px calc(32px + 1.15em);
  border-radius: var(--radius-lg);
  background: var(--paper-ivory);
}

.sw-infolist--tile li { color: var(--text-primary); }
.sw-infolist--tile li + li { margin-top: 10px; }

/* "A new chapter for Welsh education" with the Curriculum for Wales Skills Suite
   badge beside it. Copy takes the wider column: the badge is an endorsement mark,
   not an illustration, and blowing it up to match the text would give a partner's
   logo more weight on the page than Sweet's own statement.

   Sized by WIDTH with a max, and the intrinsic width/height are on the <img> so
   the row does not reflow when it loads. `align-items: center` because the badge
   is a single short object against three paragraphs — hung at the top it reads as
   floating, and at the bottom as fallen. */
.sw-chapter {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
}

/* Centred in its own column rather than pushed to the band edge, and sized to
   most of that column's width — hung right at 300px it sat closer to the edge
   than to the copy and read as a stray mark rather than the section's endorsement. */
/* An accreditation badge inside a card body, above the card's buttons. The
   artwork is 985x325 with a transparent ground, so it needs no plate — it is
   an outlined lockup that sits straight on the white card.

   Sized by width with height auto rather than by a fixed box: the file is a
   fixed 3:1 and anything else would distort it. 168px is about half the card's
   content width at desktop, which reads as a mark rather than as a picture.

   More space above it than below. The badge belongs to the card's claim rather
   than to the button under it, so it sits closer to the buttons than to the
   meta line, but clearly above them. */
/* SIZED TO A BUTTON, 13 October 2026 (Rebecca: "i want the curriculum for
   wales logo to be in the same position as the top ctas in the other tiles").

   Measured before: the three sibling cards put their first button at 2706 and
   their second at 2758; this badge sat at 2676 — 30px high — and was 55px tall
   where a button is 44.

   Those two facts fight each other. Pushing the badge down 30px to meet the
   first row pushes "Explore Wave 3" down with it, out of the second row, and a
   55px badge cannot sit in a 44px row and still leave the 52px the second row
   needs. So the badge takes the BUTTON'S HEIGHT instead and the row arithmetic
   resolves itself: 44px tall, 133px wide at the file's own 985x325, which also
   makes it about the width of the button underneath it.

   Sized by height here, where everything else on this site that holds a ratio
   is sized by width — because what has to match is the button's height, and
   the width is whatever the ratio makes it. */
.sw-card-badge {
  /* INSIDE .sw-card-actions, taking a full row of it. That container is
     margin-top: auto — pinned to the bottom of the card — so a badge sitting
     above it floated free of the row arithmetic and came out 22px high however
     its own margins were set. As a flex child of the same wrapping row it is
     simply the first item, and the container's existing gap puts the button on
     the row below, exactly as the two buttons do in the three sibling cards.

     42px because that is what a .sw-btn--md actually measures, not the 44
     its hint-size suggests. flex-basis is 100% to force the button onto the
     next row, so the element is full width and object-fit: contain draws the
     artwork at its own 985x325 inside it, pinned left. Setting width instead
     would have been overridden by the basis and stretched it. */
  flex: 0 0 100%;
  /* 60px, up from the 42px that matched a .sw-btn--md (Rebecca, 15 October
     2026: "on the home page make this icon bigger"). It no longer has to
     match the button's height — it sits on its own row above it — and at 42px
     the dragon and two lines of type were squeezed into something you had to
     lean in to read. At the file's own 985x325 ratio, 60px tall draws it
     182px wide, still narrower than the card. */
  height: 60px;
  object-fit: contain;
  object-position: left center;
  margin: 0;
}

.sw-chapter__badge {
  width: 100%;
  max-width: 420px;
  height: auto;
  justify-self: center;
}

/* Stacked, the badge goes above the heading rather than under three paragraphs,
   where it would read as a footnote to the copy instead of a mark on the section. */
@media (max-width: 1000px) {
  .sw-chapter { grid-template-columns: minmax(0, 1fr); gap: 22px; }
  .sw-chapter__badge { justify-self: start; order: -1; max-width: 250px; }
}

/* Not an error state — the live site's own wording explains e-Sweet moved to
   IQA ordering, so it reads as information rather than a stock warning. */
.sw-notice {
  padding: 16px 20px;
  border-radius: var(--radius-md);
  background: var(--surface-brand);
  font-size: 16px;
  line-height: 1.5;
}

.sw-spec {
  margin-top: 6px;
  display: flex;
  flex-direction: column;
  gap: 0;
  width: 100%;
}

.sw-spec > div {
  display: flex;
  gap: 20px;
  padding: 12px 0;
  border-top: 1px solid var(--border-subtle);
  font-size: 16px;
}

.sw-spec dt { flex: 0 0 40%; margin: 0; color: var(--text-muted); }
.sw-spec dd { margin: 0; }

/* ---- A fully managed service (About Sweet) -------------------------------
   Rebecca chose this from three layouts, 15 October 2026: "i think the 'A
   fully managed service' layout section on about us is too long ... i want to
   see the whole photo and not scroll". 887px down to 706px, with no word
   changed.

   TWO THINGS MADE IT TALL and only one of them was the layout.

   THE PHOTOGRAPH IS SQUARE IN THE FILE, 869x869, and .sw-herophoto bled it
   into a 631x887 strip down the right of the band — enlarged past its own
   size and cropped hard top and bottom, so you never saw more than her face.
   A square panel shows the whole picture at the shape it was taken.

   AND THE FIVE ITEMS ran down a half-width column carrying .sw-diff--rows,
   which puts each heading BESIDE its sentence rather than above it: two narrow
   columns inside an already narrow one, so every line wrapped. The heading
   goes on its own line here and the items run two across.

   Not .sw-bleedband any more, so the band keeps its own inset and corners. */
.sw-managed {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: clamp(32px, 4vw, 56px);
  align-items: center;
}

.sw-managed__media {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-2xl);
  overflow: hidden;
  background: var(--simple-white);
}

/* Same clearing as .sw-face and .sw-loginsplit__media: shape="rect" puts
   aspect-ratio 3/2 on the slot's own :host, which would beat the square. */
.sw-managed__media image-slot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  display: block;
}

.sw-managed__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 22px 32px;
  margin-top: 26px;
}

.sw-managed__list .sw-diff__item { padding-top: 12px; min-width: 0; }
.sw-managed__list .sw-diff__k { font-size: 17px; line-height: 1.2; margin-bottom: 4px; }
.sw-managed__list .sw-body { margin: 0; font-size: 15px; line-height: 1.45; }

@media (max-width: 1000px) {
  /* Stacked, the photograph leads and keeps its square — but CAPPED. Full
     width it came out 722x722 on an iPad and pushed the band to 1,365px,
     taller than the 887px this change exists to cut. 460px keeps it the
     biggest thing in the section without being the whole of it. */
  .sw-managed { grid-template-columns: minmax(0, 1fr); }
  .sw-managed__media { max-width: 460px; }
}

@media (max-width: 620px) {
  .sw-managed__list { grid-template-columns: minmax(0, 1fr); }
}

/* ---- The shop's page title ----------------------------------------------
   The filter sidebar that used to be documented here was removed on
   14 October 2026 when the shop became one wall of cards — see
   .sw-shop-grid--wall. Seventeen rules went with it, all verified first as
   matching no element on any page or in any script:

     .sw-shoplayout, .sw-filters, .sw-filters-col, .sw-filters__head and the
     .sw-filter__group / __label / __meta / __count set — the sidebar itself;
     .sw-shop-sec — the four product groups it filtered;
     .sw-soon-sec and the .sw-soon__card / __ph / __body set — the Coming soon
     section, now one panel below the grid (.sw-signupbox--soon).

   .sw-chip and .sw-filter__clear STAYED: the chips are a single row above the
   grid now (.sw-filterbar) and the clear button is in the empty state.
   -------------------------------------------------------------------- */
/* The shop title runs the full width of the coloured ground, above both the
   filter column and the grid — not tucked into the sidebar. Sized as a real
   page title (the same scale as an .sw-h2 section head) rather than the
   column-width label it was: it is the only H1 on the page and it should
   read like it. */
.sw-shoptitle {
  margin-bottom: clamp(28px, 3vw, 44px);
}

.sw-shoptitle__h {
  margin: 10px 0 0;
  font-size: clamp(34px, 4vw, 56px);
  line-height: 1.04;
  letter-spacing: -0.035em;
  font-weight: var(--weight-regular);
  text-wrap: balance;
}

.sw-shoptitle__p {
  margin: 16px 0 0;
  max-width: 52ch;
  font-size: 18px;
  line-height: 1.45;
  color: var(--text-secondary);
}

.sw-chip {
  font: inherit;
  font-size: 15px;
  font-weight: var(--weight-medium);
  text-align: left;
  padding: 7px 15px;
  border-radius: var(--radius-pill);
  border: var(--border-width) solid var(--border-subtle);
  background: var(--simple-white);
  color: var(--text-primary);
  cursor: pointer;
  transition: background var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out);
}

/* 44px MINIMUM on anything you tap. Rebecca, 16 October 2026, from the
   pre-launch check: the chips measured 34px and the medium buttons 42px, both
   under the 44x44 touch-target standard. Comfortable with a mouse, fiddly with
   a thumb.

   min-height with inline-flex centring rather than more padding, because
   padding has to be recalculated every time the font size changes and this does
   not — and the DS bundle sets the buttons' padding at runtime, so a padding
   override here would be fighting it. The horizontal padding goes up with the
   height so the pills keep their proportion. */
.sw-chip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: 18px;
}

.sw-chip:hover { border-color: var(--focus-black); }
.sw-chip.is-on { background: var(--focus-black); border-color: var(--focus-black); color: var(--text-on-dark); }

.sw-filter__clear {
  font: inherit;
  font-size: 15px;
  font-weight: var(--weight-medium);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--text-primary);
  border-bottom: var(--border-width) solid var(--focus-black);
}

.sw-empty { margin-top: 40px; font-size: 17px; color: var(--text-secondary); }

/* ---- Checkout ---------------------------------------------------------- */
.sw-formsec { margin-bottom: clamp(30px, 3.4vw, 46px); }
.sw-formsec .sw-h3 { margin-bottom: 16px; }

.sw-formgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px 20px;
}

/* Delivery options */
.sw-radios { display: flex; flex-direction: column; gap: 12px; }

.sw-radio {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 18px 20px;
  border-radius: var(--radius-md);
  border: var(--border-width) solid var(--border-subtle);
  cursor: pointer;
  transition: border-color var(--duration-base) var(--ease-out),
              background var(--duration-base) var(--ease-out);
}

.sw-radio:hover { border-color: var(--border-strong); }
.sw-radio:has(input:checked) { border-color: var(--focus-black); background: var(--paper-ivory); }
.sw-radio input { margin-top: 3px; accent-color: var(--focus-black); width: 18px; height: 18px; flex: 0 0 auto; }
.sw-radio__body { flex: 1; min-width: 0; }
.sw-radio__t { display: block; font-size: 17px; font-weight: var(--weight-medium); }
.sw-radio__d { display: block; margin-top: 5px; font-size: 15px; line-height: 1.45; color: var(--text-secondary); }
.sw-radio__pack {
  display: block;
  margin-top: 8px;
  font-size: 14px;
  font-weight: var(--weight-medium);
  color: var(--violet-2);
}

.sw-radio__price { flex: 0 0 auto; font-size: 16px; font-weight: var(--weight-medium); }

/* Promo code: field and button share a baseline, so the button lines up with
   the input rather than the label above it. */
/* The Total row draws its own top border. Without space beneath the promo
   field that border sits tight under the input and reads as underlining the
   promo code rather than separating the total. */
.sw-promo {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  margin: 18px 0 20px;
}

.sw-promo__go {
  font: inherit;
  font-size: 15px;
  font-weight: var(--weight-medium);
  padding: 12px 18px;
  /* Pill, like every other control on the site. This was --radius-md (16px) on
     a 44px-tall button, which is the one place the rounded-rectangle shape
     survived — measured across all 34 pages, nothing else renders a
     non-pill control. */
  border-radius: var(--radius-pill);
  border: var(--border-width) solid var(--focus-black);
  background: var(--simple-white);
  color: var(--text-primary);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--duration-base) var(--ease-out), color var(--duration-base) var(--ease-out);
}

.sw-promo__go:hover { background: var(--focus-black); color: var(--text-on-dark); }

/* ---- Cookie notice -----------------------------------------------------
   A small corner card, not a bar and not a modal: no overlay, nothing
   blocked, and it covers as little of the page as possible. Accept and
   Reject are visually identical, because refusing has to be as easy as
   agreeing.
   -------------------------------------------------------------------- */
.sw-cookie {
  position: fixed;
  left: var(--page-inset);
  bottom: var(--page-inset);
  z-index: 90;
  width: min(400px, calc(100vw - 2 * var(--page-inset)));
  padding: 18px 20px;
  border-radius: var(--radius-lg);
  background: var(--simple-white);
  box-shadow: 0 10px 30px rgba(11, 30, 47, 0.16);
  opacity: 0;
  transform: translateY(12px);
  transition: opacity var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-out);
  box-shadow: var(--shadow-soft);
}

.sw-cookie.is-in { opacity: 1; transform: translateY(0); }
.sw-cookie.is-going { opacity: 0; transform: translateY(12px); }

.sw-cookie__text {
  margin: 0 0 14px;
  font-size: 15px;
  line-height: 1.45;
  color: var(--text-secondary);
}

.sw-cookie__text a {
  color: var(--text-primary);
  font-weight: var(--weight-medium);
  border-bottom: 1.5px solid var(--border-strong);
}

.sw-cookie__text a:hover { color: var(--violet-2); border-bottom-color: currentColor; }

.sw-cookie__actions { display: flex; gap: 10px; }

.sw-cookie__btn {
  flex: 1;
  font: inherit;
  font-size: 15px;
  font-weight: var(--weight-medium);
  padding: 10px 16px;
  border-radius: var(--radius-pill);
  border: var(--border-width) solid var(--focus-black);
  background: var(--simple-white);
  color: var(--text-primary);
  cursor: pointer;
  transition: background var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out);
}

.sw-cookie__btn:hover { background: var(--paper-ivory); }
.sw-cookie__btn--yes { background: var(--focus-black); color: var(--text-on-dark); }
.sw-cookie__btn--yes:hover { background: var(--surface-brand); color: var(--focus-black); }

@media (prefers-reduced-motion: reduce) {
  .sw-cookie { transition: none; }
}

/* ---- Login chooser ------------------------------------------------------
   Two equal cards. Teacher and student are different audiences, not a
   primary and a secondary action, so neither is styled as the default —
   picking the wrong portal is the failure mode this page exists to prevent.
   -------------------------------------------------------------------- */
.sw-choice {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
}

.sw-choice__card {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 30px 32px;
  border-radius: var(--radius-lg);
  background: var(--simple-white);
  transition: background var(--duration-base) var(--ease-out),
              transform var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out);
  box-shadow: var(--shadow-soft);
}

.sw-choice__card:hover {
  background: var(--surface-brand);
  transform: translateY(-3px);
  box-shadow: var(--shadow-pop);
}

.sw-choice__icon {
  flex: 0 0 auto;
  width: 62px;
  height: 62px;
  border-radius: var(--radius-pill);
  background: var(--paper-ivory);
  display: grid;
  place-items: center;
}

.sw-choice__card:hover .sw-choice__icon { background: var(--simple-white); }

.sw-choice__body { flex: 1; min-width: 0; }
.sw-choice__t { display: block; font-size: clamp(20px, 1.9vw, 25px); font-weight: var(--weight-semibold); letter-spacing: -0.02em; }
.sw-choice__d { display: block; margin-top: 6px; font-size: 16px; line-height: 1.45; color: var(--text-secondary); }
.sw-choice__go { flex: 0 0 auto; font-size: 24px; }

.sw-helpbar {
  margin-top: clamp(44px, 5vw, 72px);
  padding: 28px 32px;
  border-radius: var(--radius-lg);
  background: var(--paper-ivory);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

/* ---- Basket ------------------------------------------------------------ */
.sw-basket {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 360px);
  gap: clamp(28px, 4vw, 56px);
  align-items: start;
}

.sw-line {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr) 104px auto auto;
  gap: 20px;
  align-items: center;
  padding: 20px 0;
  border-top: 1px solid var(--border-subtle);
}

.sw-line:last-of-type { border-bottom: 1px solid var(--border-subtle); }

.sw-line__media {
  width: 96px;
  height: 96px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--paper-ivory);
}

.sw-line__name { display: block; margin-top: 3px; font-size: 19px; font-weight: var(--weight-medium); }
.sw-line__name:hover { color: var(--violet-2); }
.sw-line__unit { margin-top: 3px; font-size: 15px; color: var(--text-muted); }
.sw-line__total { font-size: 19px; font-weight: var(--weight-semibold); white-space: nowrap; }

.sw-line__remove {
  font: inherit;
  font-size: 15px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--text-muted);
  text-decoration: underline;
}

.sw-line__remove:hover { color: var(--danger); }

.sw-summary {
  padding: 26px;
  border-radius: var(--radius-lg);
  background: var(--paper-ivory);
  position: sticky;
  top: calc(var(--page-inset) + 150px);
}

.sw-summary__row {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 11px 0;
  font-size: 16px;
  border-top: 1px solid var(--border-subtle);
}

.sw-summary__row:first-of-type { margin-top: 14px; }

.sw-summary__row--total {
  font-size: 20px;
  font-weight: var(--weight-semibold);
  border-top: var(--border-width) solid var(--focus-black);
}

/* Basket link in the nav */
/* Just the trolley and the number (Rebecca, 15 October 2026). The word
   "Basket" came out of the markup on all 35 pages; the aria-label still reads
   "View basket, 2 items", so what a screen reader announces has not changed —
   only what is drawn. Padding and gap come in with it, or the pill keeps the
   width the word used to need. */
.sw-basketlink {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 12px;
  border-radius: var(--radius-pill);
  border: var(--border-width) solid var(--focus-black);
  font-size: 16px;
  font-weight: var(--weight-medium);
  white-space: nowrap;
}

.sw-basketlink:hover { background: var(--focus-black); color: var(--text-on-dark); }

.sw-basketlink__n {
  min-width: 21px;
  height: 21px;
  padding: 0 5px;
  border-radius: var(--radius-pill);
  background: var(--focus-black);
  color: var(--text-on-dark);
  font-size: 13px;
  display: grid;
  place-items: center;
}

.sw-basketlink:hover .sw-basketlink__n { background: var(--spark-lime); color: var(--focus-black); }

/* ---- The faces (Pearson page) -------------------------------------------
   This section's whole argument is "there is an experienced person at the end
   of the phone". Before these went in, the entire Pearson page contained one
   photograph — a stack of books — and not a single human being. It said people
   and showed none.

   They sit in the space to the RIGHT OF THE HEADING, which was empty: the turn
   runs to 44ch and the band is full width, so there was a band-width column of
   nothing beside it doing no work.

   Photographs reused by SLOT ID from the team page — team-amy, team-lindsay
   and so on already exist in the image library, so nothing is duplicated and a
   new headshot replaces itself in both places at once.

   All four accents, one per tile. The team grid runs a three-cycle because it
   has rows that could line up; one row of four cannot, so green comes back
   here — it is the only place on this page it appears.
   -------------------------------------------------------------------- */
.sw-worktop {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(32px, 5vw, 72px);
  align-items: center;
}

.sw-faces {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
}

.sw-faces__row { display: flex; gap: 14px; }

/* Four faces at their 78px floor plus three 14px gaps need 354px, and a 375px
   phone offers 311px inside the band padding — so the fourth face was being cut
   off the right edge. Live on the Pearson page and on every page that has since
   borrowed .sw-faces. Two-up below 560px instead: the row wraps and each face
   takes half, which fits any phone and keeps a tidy 2x2 rather than a ragged
   3-then-1. */
@media (max-width: 560px) {
  .sw-faces { align-self: stretch; }
  .sw-faces__row { flex-wrap: wrap; }
  .sw-face { width: calc((100% - 14px) / 2); }
}

/* ── Eight faces, two rows of four ────────────────────────────────────────
   The Pearson page only (Rebecca, 25 September 2026). Wave 3 and Sweet in
   England keep the four-face row — this is a variant, not a change to the
   shared block.

   A GRID of four rather than a wrapping flex row, because four across has to
   hold at every width to give two tidy rows. Left to wrap, eight faces at
   their 78–112px clamp break at five-then-three in the copy column, which is
   the ragged last row the shop grid and the winners grid both exist to avoid.

   The faces shrink below the 78px floor on a phone — about 67px at 375px —
   and that is Rebecca's call, made knowing the number. The floor exists
   because four at 78px plus gaps need 354px against the 311px a phone offers;
   the grid simply divides what there is. They are decorative (the block is
   aria-hidden and the names are not given), so legibility of a face is not
   carrying any meaning here.

   max-width keeps them from growing past the 112px ceiling on a wide column.

   THE CYCLE DROPS TO THREE, and that is the point of this block rather than a
   detail. `.sw-face` cycles four accents, which is coprime with nothing when
   the grid is four wide: every face would sit directly under one of its own
   colour and the block would read as four flat columns. Three against four is
   coprime, so no face matches the one above it — exactly the reasoning already
   recorded for `.sw-collage` and `.sw-team`. Violet, lime and blue: the band
   is the white page ground, so none of the three disappears into it.

   The tracks are the FACE's own clamp, not `1fr`. `1fr` with `width: 100%`
   looked right and was not: `.sw-faces` sits in a narrow column here, so the
   percentage resolved against about 138px and every face came out 24px on a
   1440px screen. Intrinsic tracks keep the faces at the size they are
   everywhere else and let the block be as wide as it needs to be. */
.sw-faces--eight .sw-faces__row {
  display: grid;
  grid-template-columns: repeat(4, clamp(78px, 7.4vw, 112px));
  gap: 14px;
}

.sw-faces--eight .sw-face { width: auto; }

/* Only on a phone do they give up their floor and divide the width instead. */
@media (max-width: 560px) {
  .sw-faces--eight { align-self: stretch; }
  .sw-faces--eight .sw-faces__row {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    width: 100%;
  }
}

.sw-faces--eight .sw-face:nth-child(3n + 1) { background: var(--accent-violet); }
.sw-faces--eight .sw-face:nth-child(3n + 2) { background: var(--spark-lime); }
.sw-faces--eight .sw-face:nth-child(3n + 3) { background: var(--accent-blue); }

.sw-face {
  position: relative;
  width: clamp(78px, 7.4vw, 112px);
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-lg);
  overflow: hidden;
}

/* The slot carries aspect-ratio 3/2 from shape="rect" and height:100% on its
   own :host — the first beats a square tile, the second is what we want. Fill
   the tile and let the tile hold the shape. */
.sw-face image-slot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  display: block;
}

/* THE 4% INSET THAT USED TO BE HERE IS GONE, and it was mine.

   The story, because it matters for whatever is tried next. Rebecca reported
   "gaps at the bottom of the images" in the Contact hero twice, and both times
   the photos measured FULL — every one filled its tile to the pixel. What she
   was seeing was the bottom of the PHOTOGRAPH: Amy's navy jumper, Kris's dark
   top and Jordan's navy jacket all reach the tile's last row, and on a navy
   band a navy jumper IS the band, so the eye reads the remainder as empty.

   A hairline ring fixed that and she did not like the frames. So the photo was
   inset 4% instead, on the reasoning that the strip revealed underneath would
   be the tile's own colour and the tile's colour matches the photo's flat
   background exactly — measured, all seventeen.

   THAT REASONING WAS WRONG AT THE ONE EDGE THAT MATTERED. The colours match at
   the top and the sides, where the photo's background is what shows. At the
   BOTTOM the photo contains the sitter's clothing, so the strip was not an
   invisible continuation of the background — it was a band of flat violet or
   lime under a navy jacket. Measured: 10px under Jordan, 5px under each small
   tile. Rebecca: "the contact page hero photos have big gaps under them". She
   was right, and it was a gap I had added.

   The photos fill their tiles again. The original dark-clothing-against-a-navy
   -band effect may read as a gap again; it is a contrast problem, not a layout
   one, and the two things tried so far were a visible frame and a visible
   strip. */

.sw-face:nth-child(4n + 1) { background: var(--accent-violet); }
.sw-face:nth-child(4n + 2) { background: var(--spark-lime); }
.sw-face:nth-child(4n + 3) { background: var(--accent-blue); }
.sw-face:nth-child(4n + 4) { background: var(--accent-green); }

@media (max-width: 1000px) {
  /* Stacked, the faces lead the section rather than sitting beside it. */
  .sw-worktop { grid-template-columns: minmax(0, 1fr); gap: 28px; }
}

/* ---- The statement, and what follows it (Pearson page) -------------------
   "The qualification is Pearson. The Sweet experience is ours." is the page's
   hinge: everything above it is the credential, everything below it is the
   reason to choose Sweet rather than anyone else holding the same credential.

   The emphasis is WEIGHT, not colour. This briefly set the first clause in
   secondary grey to stage the turn, which is a device the brand does not have
   — guidelines p21 and p24 both show one heading in Regular with the key words
   in Medium/Bold, and nothing else. `.sw-em` is that, and it is what every
   other heading on the site already uses.
   -------------------------------------------------------------------- */

.sw-diff {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 34px 56px;
}

.sw-diff__item {
  min-width: 0;
  border-top: 2px solid var(--focus-black);
  padding-top: 14px;
}

/* On a dark band both of these are Focus Black on Focus Black — the 2px rule
   above the item and the key itself, each invisible. Added 13 October 2026
   when Wave 3's Support band went from violet to navy. */
.sw-on-dark .sw-diff__item { border-top-color: var(--text-on-dark); }
.sw-on-dark .sw-diff__k { color: var(--text-on-dark); }

.sw-diff__k {
  font-size: 20px;
  font-weight: var(--weight-bold);
  line-height: 1.25;
  color: var(--focus-black);
  margin-bottom: 6px;
}

@media (max-width: 720px) {
  .sw-diff { grid-template-columns: minmax(0, 1fr); gap: 26px; }
}

/* Row variant, for a list that is not an even number. Five items in the
   two-column .sw-diff is 2 / 2 / 1 — the last row half empty, the same ragged
   hole the shop grid and the winners grid both exist to avoid.

   As rows it takes any count. Each item puts its claim in a left column and the
   supporting line beside it, so the five claims read as one scannable stack
   rather than five paragraphs: someone skimming gets the whole answer to "why
   Sweet" off the left-hand column alone. The rule above each item is the same
   2px Focus Black as the two-column version, so both read as one device. */
.sw-diff--rows {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
}

.sw-diff--rows .sw-diff__item {
  display: grid;
  grid-template-columns: minmax(0, 0.4fr) minmax(0, 1fr);
  gap: 8px 48px;
  align-items: baseline;
  padding: 18px 0 22px;
}

.sw-diff--rows .sw-diff__k { margin-bottom: 0; }
.sw-diff--rows .sw-body { max-width: 62ch; }

/* The last item keeps a rule under it, so the list closes rather than trailing
   off — with rules only above, the final row has no bottom edge and the block
   looks unfinished. */
.sw-diff--rows .sw-diff__item:last-child { border-bottom: 2px solid var(--focus-black); }

@media (max-width: 1000px) {
  .sw-diff--rows .sw-diff__item { grid-template-columns: minmax(0, 1fr); gap: 6px; }
}

/* ---- Proof panel (Pearson page) -----------------------------------------
   The one piece of third-party evidence on a page whose whole job is "this is
   genuinely accredited": Pearson listing Sweet on their own qualifications
   site. It began as a bare 17px text link under the service cards — the most
   persuasive thing on the page and the quietest — and is now the feature it
   should have been.

   Focus Black, and the only dark surface between the hero and the footer, so
   it reads as a different KIND of thing from the three service cards above it:
   those are claims Sweet makes, this is a claim someone else makes. The white
   Pearson mark sits opposite the copy rather than above it, so the panel is
   read as one statement attributed to them.

   It briefly carried two more statements lifted out of the FAQ. Those came
   back out on 11 September 2026: "no awarding-body paperwork" says what the
   "We deal with Pearson" card directly above already says, and a claim
   repeated two inches apart reads as padding rather than emphasis.

   --radius-xl, not --radius-2xl: the ladder reserves 40px for full-width
   section bands, and this is a feature surface sitting inside one.
   -------------------------------------------------------------------- */
.sw-proofpanel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(24px, 4vw, 56px);
  background: var(--surface-inverse);
  border-radius: var(--radius-xl);
  padding: clamp(26px, 3.2vw, 44px);
}

.sw-proofpanel__body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}

/* A statement, not a heading — it is marked up as a <p> at heading scale so it
   does not claim a level in the document outline it has no business holding. */
.sw-proofpanel__body .sw-h3 { max-width: 38ch; margin: 0; }

/* Sized by HEIGHT so the ratio cannot drift, same rule as every other mark. */
.sw-proofpanel__mark img {
  display: block;
  height: clamp(30px, 3.4vw, 46px);
  width: auto;
}

@media (max-width: 720px) {
  .sw-proofpanel {
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
  }
}

/* ---- Team grid ---------------------------------------------------------
   Every card identical: on a page of colleagues, a card that is bigger than
   its neighbours reads as a statement about the person in it. So uniform
   cards, laid out with flex rather than grid — flex lets the final, partial
   row CENTRE instead of stranding whoever is last against the left edge.
   Centring is what makes a part-filled last row look deliberate rather than
   broken, at any team size — which is the point, because the size keeps
   changing: 13 people when this was written, 11 from 10 September 2026. Never
   swap this for a fixed column count that happens to divide today's headcount.
   -------------------------------------------------------------------- */
.sw-team {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 24px;
}

/* min-width: 0 is not optional here. A flex item defaults to `min-width: auto`,
   which refuses to go below its own min-content width — and an image-slot
   reports the headshot's intrinsic width as its minimum. Safari honours that
   and Chrome does not, so the basis below was quietly overridden in Safari
   only: cards came out 511px instead of 427px, one per row instead of three,
   and the page ran to 16,397px against Chrome's 6,406px. Found 10 September
   2026 while testing the standalone build across browsers; the same house rule
   is in CLAUDE.md under Layout and was simply missed on this one grid. */
.sw-team > * { flex: 0 0 calc((100% - 3 * 24px) / 4); min-width: 0; }

/* Square, matching the headshots themselves. At a fixed 240px the tile was a
   wide letterbox and a square photo lost the top and bottom of the frame —
   which on a headshot means the top of the head. A 1:1 tile shows the whole
   photograph with no crop at all. */
.sw-team .sw-tile { height: auto; aspect-ratio: 1 / 1; }

/* Photo tiles cycle brand accents. The cycle length MUST stay coprime with the
   number of cards per row, or the pattern lines up and every card matches the
   one directly above it — four colours in four columns is four solid stripes.

   The team went to FOUR across on 11 September 2026, so the cycle went from
   four to THREE: violet, lime, blue. Coprime with 4, with the 2 below 1180px
   and with the 1 below 720px, so no column ever repeats a colour and no two
   neighbours match at any width. It lays out as diagonal bands.

   That is also why the layout drops straight from four to two at 1180px with
   no three-across step: three columns against a three-cycle is the very clash
   this note is about.

   Green leaves the tiles as a result — three colours, not four. It is still on
   the page, in the hero pills, so the full palette still reads.

   Rebecca lands on violet, which matters: the purple absorbs a faint cool cast
   around her hair that reads as a halo against white. Alphabetically she is
   tenth, and 10 = 3(3) + 1, so the first colour in the cycle is hers. Re-derive
   this if the list changes — it is tied to her POSITION, not her name.
   Focus Black stays out of the rotation.

   The rotation only became visible once the headshots were cut out. Before that
   every photo carried a baked-in white background, so the tile colour was covered
   up and the cycle did nothing. */
.sw-team > *:nth-child(3n + 1) .sw-tile { background: var(--accent-violet); }
.sw-team > *:nth-child(3n + 2) .sw-tile { background: var(--spark-lime); }
.sw-team > *:nth-child(3n + 3) .sw-tile { background: var(--accent-blue); }

/* `.sw-team--core` carried a cycle offset here until 10 September 2026. It
   existed to keep Rebecca on Future Violet — the purple absorbs the faint cool
   cast around her hair that read as a halo against white — because she sat
   THIRD in the merged group (Simon, Cherie, Rebecca, Jordan) where the base
   rotation gives Curious Blue.

   Cherie came off the page, so the group is Simon, Rebecca, Jordan and Rebecca
   is second, which is violet in the base rotation already. The old note said
   to delete the block if she ended up first or second, so it is deleted rather
   than re-derived. `.sw-team--core` stays on the markup as the group's name,
   the same way `.sw-team--quality` does, but it no longer changes any colour.

   Three cards, one row: lime, violet, blue. No two alike. */

/* The three ways to reach Sweet, as cards on the brand colours rather than three
   columns of grey text on white. They are the reason most people open this page
   and they had the least weight on it — a label, a line and a link, floating.

   Email and phone are whole-card links; the address is not, because there is
   nothing to click. That is why the markup mixes <a> and <div>: a card that does
   nothing must not be styled as though it does.

   Body copy goes to full Focus Black on all three — these are the mid-tones
   `.sw-on-mid` exists for, and 66% black on Future Violet measures 3.6:1. */
/* Two routes, not three: the phone came off on 11 September 2026 — nobody was
   ringing it. A three-track grid would have left an empty cell, which is the
   same hole the shop grid and the blog grid both exist to avoid. */
.sw-routes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
}

.sw-route {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
  padding: 28px;
  border-radius: var(--radius-lg);
  color: var(--focus-black);
  text-decoration: none;
  transition: transform var(--duration-base) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out);
}

a.sw-route:hover { transform: translateY(-3px); box-shadow: var(--shadow-pop); }

.sw-route--blue { background: var(--accent-blue); }
.sw-route--green { background: var(--accent-green); }
.sw-route--violet { background: var(--accent-violet); }

/* White tags, not tags in the card's own colour. Blue-on-blue, green-on-green and
   violet-on-violet each nearly vanished — a Highlight Tag is a label that has to
   separate from what it sits on. White does that on all three and keeps the row
   consistent. */
.sw-route__tag { align-self: flex-start; }

.sw-route__t {
  margin: 4px 0 0;
  font-size: 22px;
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: var(--weight-semibold);
  overflow-wrap: anywhere;
}

.sw-route__d { margin: 0; font-size: 15px; line-height: 1.5; }

/* Pushed to the foot of the card so the three arrows line up whatever the copy
   above them runs to. */
.sw-route__go { margin-top: auto; padding-top: 12px; font-size: 16px; font-weight: var(--weight-medium); }

@media (max-width: 720px) {
  .sw-routes { grid-template-columns: minmax(0, 1fr); }
}

/* The person's own email, under their role.

   Taken from sweet.education/meet-the-team rather than built from the
   firstname.surname pattern the addresses happen to follow — a guessed address
   for a real colleague bounces, or worse, reaches someone else. Michael is
   published there against the general info@ address rather than a personal one,
   so that is what he gets here.

   Sized between the role and the body copy, and allowed to break mid-word:
   lindsay.donovan-lacey@sweet.education is 37 characters against a 373px card
   at four across, and an unbreakable string that long would push the whole
   grid wider. */
.sw-person__mail {
  display: inline-block;
  margin-top: 6px;
  font-size: 14px;
  line-height: 1.35;
  color: var(--text-secondary);
  text-decoration: underline;
  text-underline-offset: 3px;
  overflow-wrap: anywhere;
}

.sw-person__mail:hover { color: var(--focus-black); }

/* A person whose photograph has not arrived yet. NOT an empty image-slot: an
   unfilled slot renders the component's grey drop-target wash and its
   "Drop a photo" prompt, which is editor furniture and cannot be published.

   The tile keeps its brand colour from the rotation and carries the Sweet symbol
   instead, so the card reads as deliberate rather than broken, and the row still
   scans as four cards of equal weight. The caption says plainly what is missing —
   better than a silhouette that pretends to be a person. */
.sw-person__await {
  display: grid;
  place-items: center;
  align-content: center;
  gap: 14px;
  height: 100%;
  padding: 24px;
  text-align: center;
}

.sw-person__await img { width: 34%; max-width: 88px; height: auto; opacity: 0.55; }

.sw-person__await span {
  font-size: 14px;
  font-weight: var(--weight-medium);
  line-height: 1.35;
  color: var(--focus-black);
  opacity: 0.7;
}

/* Language badge, pinned to the photo tile. The supplied SVG carries its own
   pale rounded background, so it needs no plinth to sit on a photo. */
.sw-person__badge {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 2;
  width: clamp(62px, 6.4vw, 84px);
  display: block;
  filter: drop-shadow(0 2px 6px rgba(11, 30, 47, 0.22));
}

.sw-person__role {
  font-size: 15px;
  font-weight: var(--weight-medium);
  line-height: 1.35;
  color: var(--violet-2);
}

/* Individual staff email addresses have been removed from Meet the Team — every
   enquiry routes through Contact Us and info@sweet.education instead, so there is
   one address to keep right and no personal inbox published for scrapers. The
   `.sw-person__email` rules went with the markup rather than being left behind as
   dead CSS. */

/* ============================================================
   Banner — a full-width tile that sits under a card row as if it
   were a fourth item. Same radius as the cards above it so it reads
   as part of the same set, but inverted so it carries more weight.
   ============================================================ */

/* Photo slot under the CTA copy. --radius-xl: it's a large feature surface,
   matching the hero and the quote card rather than a card-sized tile. */
/* The books are the right-hand half of the CTA, not a postscript under the copy.

   They used to sit beneath the heading and lede in the left column, which left
   the right column holding two buttons and one line of text above a large empty
   area, and squeezed the cut-out into a 300px-tall letterbox where `contain`
   shrank it to almost nothing. Copy and buttons now live together on the left —
   they belong together anyway, since the buttons are what the copy is asking you
   to press — and the books get a column of their own. */
/* Slanted bottom edge, built the house way (brand guidelines p.43): a straight,
   rounded, overflow-hidden container holding a skewed fill that bleeds past the
   OPPOSITE edge, so exactly one slanted edge shows and the silhouette stays a
   clean rounded rectangle. No clip-path — it would intersect the border-radius
   and leave razor-sharp vertices. The image-slot is a SIBLING of the fill, never
   a child, so nothing it contains is skewed. */
/* The sample books sitting directly in the band, not on a white tile inside it.

   It used to be a white, rounded, slanted-bottom surface holding the cut-out —
   a tile within a tile, which put two nested rounded rectangles and a second
   diagonal into a block that is really just "here are the books". Removing the
   surface removes the slant with it: a slant existed to give that white shape an
   edge, and there is no white shape now.

   Same call as the course heroes — a cut-out on the band's own colour rather than
   staged on white. No padding either: the whole point is that it bleeds.

   Wide and short, so the block is as thin as its copy needs. At 4/3 the photo set
   the height of the entire band and left the buttons floating in the middle of a
   tall column. */
.sw-cta-photo {
  position: relative;
  width: 100%;
  aspect-ratio: 2 / 1;
}

@media (max-width: 1000px) {
  .sw-cta-photo { aspect-ratio: 16 / 9; }
}

.sw-banner {
  margin-top: 24px;      /* matches .sw-grid-3's gap */
  padding: 30px 34px;
  border-radius: var(--radius-lg);
  background: var(--surface-inverse);
  color: var(--text-on-dark);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 30px;
  flex-wrap: wrap;
}

.sw-banner__text {
  margin: 0;
  max-width: 46ch;
  font-size: clamp(19px, 1.9vw, 26px);
  line-height: 1.25;
  letter-spacing: -0.01em;
  font-weight: var(--weight-regular);
  text-wrap: pretty;
}
.sw-grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 40px; }

/* ============================================================
   Stats
   ============================================================ */

.sw-stat__figure {
  font-size: clamp(40px, 4.6vw, 68px);
  font-weight: var(--weight-semibold);
  letter-spacing: -0.03em;
  line-height: 1;
}

/* The survey credit now lives inside the first stat rather than under the
   whole grid, so it needs its own spacing and, on a phone, its own colour —
   the tiles there are saturated accents where .sw-on-dark's white-at-55% is
   unreadable. Full Focus Black, the .sw-on-mid rule: 66% black measures
   3.6 : 1 on Future Violet and this is 14px text. */
.sw-stat .sw-source { margin-top: 10px; }

.sw-stat__label {
  margin-top: 12px;
  font-size: 17px;
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.7);
}

/* ============================================================
   Hero — a straight rounded container with a skewed fill inside.
   The container's silhouette stays a clean rounded rectangle; the
   oversized skewed fill shows exactly ONE slanted edge. This is
   the technique the project rules prescribe for slanted shapes.
   ============================================================ */

.sw-hero {
  position: relative;
  margin: var(--page-inset) var(--page-inset) 0;
  border-radius: var(--radius-xl);
  overflow: hidden;
  background: var(--simple-white);
  min-height: min(560px, 68vh);
  display: flex;
  align-items: center;
}

.sw-hero__media { position: absolute; inset: 0 0 0 42%; }

/* The left half of the hero. It was white on a white page, so it had no shape
   of its own — all you saw was a photograph with a slanted edge floating in
   nothing. Paper Ivory gives it a surface, and because the fill is skewed at
   --slant-v the two halves now read as one shape divided by a single diagonal,
   rather than a rectangle with a photo pasted into it.

   Skewed fill inside a straight, overflow:hidden, rounded container — the same
   technique as every other slanted shape on the site. Only one slanted edge
   shows; the container's silhouette stays a clean rounded rectangle. */
.sw-hero__fill {
  position: absolute;
  top: -10%;
  bottom: -10%;
  left: -10%;
  width: 66%;
  /* The brand's blue-to-white surface gradient. This is one of the secondary
     surface gradients, which the design system reserves for backgrounds — it
     must never stand in for Open Sky, which is the core brand gradient. */
  background: var(--gradient-curious-blue);
  transform: skewX(calc(-1 * var(--slant-v)));
  /* A rule along the diagonal, so the blue side and the photograph read as two
     panels meeting rather than one surface fading into another.

     It goes on the fill's own right edge, which IS the diagonal — the element is
     skewed, so its border is skewed with it and follows the slant exactly. No
     second skewed element, and nothing to keep in sync if --slant-v ever changes.

     White, matching the hero's container colour, so it reads as the gap between
     two panels rather than as a fourth colour drawn across them. box-sizing is
     border-box site-wide, so this eats 8px of the fill's width instead of pushing
     the diagonal outwards. */
  border-right: 16px solid var(--simple-white);
}

.sw-hero__content {
  position: relative;
  z-index: 2;
  width: 50%;
  padding: 80px 56px 80px 52px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 26px;
}

.sw-hero__partner {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 15px;
  color: var(--text-muted);
}

/* ============================================================
   Highlight tags
   The design system ships sm and md only. The page previously faked
   a large tag with transform: scale(1.8), which also scaled the
   signature 4px/6px offset shadow to 7px/11px — so no two tags on
   the page shared the brand's shadow. This is the real lg size:
   the shadow stays exactly as the brand defines it.
   ============================================================ */

.sw-tag--pill.sw-tag--lg { padding: 15px 30px; font-size: clamp(18px, 1.7vw, 26px); }

/* Display-scale pill for page headings. The offset shadow deliberately stays
   at the brand's 4px/6px — it is a fixed offset, not a proportion, so every
   tag on the site casts the same shadow whatever its size. */
.sw-tag--pill.sw-tag--xl {
  padding: clamp(12px, 1.7vw, 26px) clamp(30px, 3.9vw, 62px);
  font-size: clamp(40px, 8.4vw, 108px);
  font-weight: var(--weight-semibold);
  letter-spacing: -0.02em;
}

/* A heading built from Highlight Tags, scattered rather than set in a row:
   each word carries its own angle and a small offset so they overlap and read
   as hand-placed. The offsets are transforms, so they take no layout space —
   hence the bottom margin, which stops the lowest pill colliding with the
   paragraph beneath. */
.sw-pill-head {
  margin: 0 0 clamp(30px, 4vw, 54px);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* Row and column gaps set separately. Since the Meet the Team hero became two
     columns the pills wrap to two lines, so the vertical gap is what separates
     "Meet" from "team" — and at the shared 12px they were nearly touching. The
     horizontal gap stays tight, because the per-pill --dx offsets are tuned to
     overlap slightly and a wide column gap would undo that. */
  column-gap: clamp(4px, 0.8vw, 12px);
  row-gap: clamp(14px, 2vw, 30px);
}

.sw-pill-head > * { position: relative; }
.sw-pill-head > *:nth-child(2) { z-index: 2; }
.sw-pill-head > *:nth-child(3) { z-index: 1; }

.sw-tag-float { position: absolute; z-index: 6; }

/* The tags drop in and settle when the figure scrolls into view. The rotation
   and the stagger are per-tag, set as --rot and --drop-delay on the instance.
   Rotation lives on this inner layer, never on the [data-reveal] element
   itself, which animates its own transform. The overshoot in --ease-drop is
   what makes them read as landing rather than sliding. */
[data-drop] {
  display: inline-flex;
  opacity: 0;
  transform: translateY(-70px) rotate(0deg);
  transition: transform 700ms var(--ease-drop) var(--drop-delay, 0ms),
              opacity 240ms ease-out var(--drop-delay, 0ms);
}

[data-drop].is-dropped {
  opacity: 1;
  transform: translate(var(--dx, 0px), var(--dy, 0px)) rotate(var(--rot, 0deg));
}

/* ============================================================
   Photo frame with floating tags
   ============================================================ */

.sw-figure {
  position: relative;
  /* Room for the tags to hang outside the photo without being
     clipped by the section's padding at any viewport width. */
  margin: 0 0 0 clamp(0px, 3vw, 34px);
}

.sw-figure__photo {
  position: relative;
  height: clamp(280px, 34vw, 440px);
  border-radius: var(--radius-xl);
  overflow: hidden;
}

/* ============================================================
   Testimonials
   ============================================================ */

/* NO TILE AT ALL as of 9 October 2026. This went white-card-on-a-violet-band,
   then violet card, then nothing — Rebecca's calls, in that order, and the
   last one is right: a testimonial is the one thing on the page that gains
   from having no frame. The words are the content and a box around them makes
   them look like an advertisement for themselves.

   No background, no radius, and the padding drops to vertical only — the band
   already supplies the horizontal rhythm, and a box's worth of inset with no
   box reads as the text having drifted off its own left edge.

   The homepage is the only page with .sw-quote-card, so no variant is needed.
   What this leaves is the .sw-quote-card__stage machinery, which is still
   doing its job: the card holds an invisible copy of the longest quote in the
   same grid cell, so the block does not jump height as you cycle through.

   LEFT-ALIGNED TO THE BAND, not centred in it (9 October 2026). With the tile
   gone, `margin: 0 auto` on a 1040px measure put the quote 200px in from the
   band edge while every other band's content starts at 40px — it lined up
   with nothing above or below it and read as a paragraph that had escaped.
   A measure is still a measure; it just starts where the page starts. */
.sw-quote-card {
  position: relative;
  width: 100%;
  max-width: 1040px;
  margin: 0;
  padding: clamp(8px, 1.5vw, 20px) 0;
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.sw-quote-card__text {
  margin: 0;
  font-size: clamp(20px, 2.2vw, 30px);
  line-height: 1.4;
  letter-spacing: -0.01em;
  text-wrap: pretty;
}

.sw-quote-card__foot {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

.sw-quote-card__by { display: flex; align-items: center; gap: 16px; min-width: 0; }

/* Slot for the centre's own logo. --radius-md is the ladder's rung for logo
   tiles. Ivory rather than white so it still reads as a tile on the white
   quote card, and object-fit: contain so wide and tall logos both sit
   correctly without being cropped or stretched. */
.sw-quote-logo {
  width: 58px;
  height: 58px;
  flex: 0 0 auto;
  padding: 8px;
  border-radius: var(--radius-md);
  background: var(--paper-ivory);
  display: grid;
  place-items: center;
  overflow: hidden;
}

.sw-quote-logo img { max-width: 100%; max-height: 100%; object-fit: contain; }

.sw-quote-nav { display: flex; align-items: center; gap: 10px; }

.sw-iconbtn {
  width: 52px;
  height: 52px;
  border-radius: var(--radius-pill);
  border: none;
  background: var(--focus-black);
  color: var(--text-on-dark);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform var(--duration-fast) var(--ease-out),
              background var(--duration-base) var(--ease-out);
}

.sw-iconbtn:hover { transform: translateY(-2px); background: var(--violet-2); }

/* Sweet symbols behind the quote, placed individually rather than tiled.
   A repeating background always gets sliced by the band's edges; positioning
   a handful by hand means every one of them sits whole inside the band.
   They are decorative only, so they sit behind the quote card and are
   hidden from assistive tech by the container's aria-hidden. */
.sw-pattern {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0.17;
}

.sw-pattern i {
  position: absolute;
  display: block;
  aspect-ratio: 155.05 / 180;
  background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20155.05%20180%22%3E%3Cpath%20fill%3D%22%23fff%22%20d%3D%22M77.58.27L.75%2C44.62c-.72.41-.96%2C1.33-.55%2C2.05l36.39%2C62.83c.43.75%2C1.3%2C1.13%2C2.15.95l39.84-8.47-22.59%2C39c-.36.62-.36%2C1.38%2C0%2C2l20.85%2C36c.77%2C1.33%2C2.69%2C1.33%2C3.46%2C0l57.31-98.98c.66-1.14.05-2.6-1.22-2.94l-57.82-15.2%2C75.28-15.71c1.35-.28%2C1.64-2.08.44-2.77L79.58.27c-.62-.36-1.38-.36-2%2C0Z%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
}

/* A pattern nested in a slanted fill must discount the fill's overhang, or
   its percentage positions resolve against a box hundreds of pixels taller
   than the band and the lower symbols fall out through the bottom edge. */
.sw-band__fill--top > .sw-pattern    { bottom: var(--fill-overhang); }
.sw-band__fill--bottom > .sw-pattern { top: var(--fill-overhang); }

/* ============================================================
   Logo wall
   ============================================================ */

/* No edge fade: a hard-edged white tile caught in a gradient mask reads
   as a broken tile, not as a fade. The band's own rounded corners clip it. */
/* A fade at both ends. Without it the track is simply clipped and a crest is
   sliced down the middle at the band edge — which reads as a broken image
   rather than as something scrolling past. The mask is 56px, about a third of
   a 178px tile, so a logo is well into the fade before it is cut.

   Both the prefixed and unprefixed properties: Safari still wants
   -webkit-mask-image, and this is a Welsh school audience on a lot of iPads. */
.sw-marquee {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 56px, #000 calc(100% - 56px), transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 56px, #000 calc(100% - 56px), transparent 100%);
}
.sw-marquee__track { display: flex; width: max-content; gap: 24px; animation: sweet-marquee 34s linear infinite; }

/* Real school logos are a mix of portrait shields (roughly 1:1.1) and
   landscape wordmarks (roughly 3:1). This tile is proportioned so both fill a
   decent share of it: object-fit: contain means the shields are limited by
   height and the wordmarks by width. */
.sw-logo-tile {
  width: 178px;
  height: 116px;
  flex: 0 0 auto;
  border-radius: var(--radius-md);
  background: var(--simple-white);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}

.sw-logo-tile img { max-width: 100%; max-height: 100%; object-fit: contain; }

@keyframes sweet-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ============================================================
   Footer — same container-plus-skewed-fill technique as the hero,
   so the navy has one slanted top edge and the footer's silhouette
   stays a clean rounded rectangle.
   ============================================================ */

/* Full width. The footer used to be inset like the hero, as a matching
   bookend; it now runs edge to edge and grounds the page.

   Only the top corners are rounded. The bottom two sit on the bottom of the
   document and the sides run off-screen, so those corners are never visible —
   rounding them would leave ivory notches at the screen edges, the same reason
   .sw-band--bleed squares its corners. The no-sharp-corners rule is about
   visible corners, and every visible corner here is still --radius-2xl.

   The inner padding picks up --page-inset so footer content stays in line with
   the inset bands above it. */
.sw-footer {
  position: relative;
  /* FULL WINDOW, like the bar at the other end (Rebecca, 16 October 2026):
     "lets keep the footer full width". Same negative --page-gutter the header
     uses, and for the same reason — the two of them are the page's top and
     bottom edges, and a capped footer under a full-width bar left the page
     looking unfinished at the bottom.

     No gutter added back as padding here, unlike the header. The footer's
     --page-inset is what holds the navy block off the edge, and the two things
     inside it that have to line up with the page — the wordmark and the link
     grid — both centre a fixed-width box on the viewport midline, which is the
     same place whether the box they sit in is 1760px or the whole window.

     `100vw` with `50% - 50vw`, NOT a negative --page-gutter, and that is not a
     style preference. THE FOOTER IS NOT INSIDE .sw-page ON EVERY PAGE — on
     Contact Us and Personal Growth and Wellbeing it is a direct child of the
     runtime's own host element, so its starting width is the window, not the
     capped page. A fixed -400px margin made those two 3360px wide and put a
     horizontal scrollbar on them. This formula asks the parent how wide it is
     and cancels exactly that much, so it lands on the window edges either way.
     Caught by a spot check of eight pages at 2560px; the first four happened
     to all be the well-behaved kind. */
  width: 100vw;
  margin: var(--band-gap) calc(50% - 50vw) 0;
  border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
  overflow: hidden;
  background: var(--simple-white);
  padding-inline: var(--page-inset);
}

.sw-footer__wordmark { display: flex; justify-content: center; padding: clamp(48px, 6vw, 88px) var(--band-pad-x) clamp(28px, 4vw, 56px); }
.sw-footer__wordmark img { width: 100%; max-width: 1300px; }

.sw-footer__dark { position: relative; color: var(--text-on-dark); }

/* Oversized on three sides; only the top edge slants. */
.sw-footer__fill {
  position: absolute;
  inset: 0 -10% -10%;
  background: var(--focus-black);
  transform: skewY(calc(-1 * var(--slant-h)));
  transform-origin: center top;
}

.sw-footer__grid {
  position: relative;
  max-width: var(--content-max);
  margin: 0 auto;
  /* Top padding clears the deepest point of the slant, so the claim
     line is never sliced by the diagonal. */
  padding: clamp(84px, 9vw, 132px) var(--band-pad-x) 40px;
  display: grid;
  /* The two link columns are sized to their content and pushed right, so the
     last one's right edge lands on the same margin as the legal row's links
     below it. The brand block takes the remaining space on the left. */
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: clamp(40px, 7vw, 104px);
}

.sw-footer__col { display: flex; flex-direction: column; gap: 12px; font-size: 16px; color: rgba(255, 255, 255, 0.72); }
.sw-footer__col a { color: inherit; }
.sw-footer__col a:hover { color: var(--spark-lime); }
.sw-footer__coltitle { font-weight: var(--weight-medium); color: var(--text-on-dark); margin-bottom: 4px; }

/* Grouped so the label sits tight to the tile rather than inheriting the
   footer column's link spacing. */
.sw-footer__partner { margin-top: 8px; }

.sw-footer__partner-label {
  margin-bottom: 8px;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.6);
}

.sw-footer__pearson {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
  padding: 10px 16px;
  border-radius: var(--radius-md);
  background: var(--simple-white);
}

/* ============================================================
   Prose — long-form reading (legal pages, policies)
   A measured column and --lh-relaxed. Legal copy is read carefully
   and in full, so it gets a narrower measure and more leading than
   marketing copy.
   ============================================================ */

.sw-prose { max-width: 72ch; }

.sw-prose > * + * { margin-top: 1.1em; }

.sw-prose p,
.sw-prose li {
  font-size: 17px;
  line-height: var(--lh-relaxed);
  color: var(--text-secondary);
  text-wrap: pretty;
}

.sw-prose h2 {
  margin: 0;
  padding-top: 1.6em;
  font-size: clamp(21px, 2vw, 27px);
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}

.sw-prose h2:first-child { padding-top: 0; }

.sw-prose h3 {
  margin: 0;
  padding-top: 0.6em;
  font-size: 18px;
  line-height: 1.3;
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}

.sw-prose ul { margin: 0; padding-left: 1.3em; }
.sw-prose li + li { margin-top: 0.5em; }
.sw-prose li::marker { color: var(--text-muted); }

.sw-prose strong { font-weight: var(--weight-medium); color: var(--text-primary); }

.sw-prose a {
  color: var(--text-primary);
  font-weight: var(--weight-medium);
  border-bottom: 1.5px solid var(--border-strong);
}

.sw-prose a:hover { color: var(--violet-2); border-bottom-color: currentColor; }

.sw-prose address { font-style: normal; line-height: var(--lh-relaxed); color: var(--text-secondary); font-size: 17px; }

/* Numbered contents list that links down the page. */
.sw-toc { margin: 0; padding: 0; list-style: none; counter-reset: toc; }

.sw-toc li {
  counter-increment: toc;
  font-size: 17px;
  line-height: 1.4;
}

.sw-toc li + li { margin-top: 10px; }

.sw-toc a {
  display: flex;
  gap: 12px;
  color: var(--text-primary);
  font-weight: var(--weight-medium);
  /* These are flex, so the prose link underline would run the full column
     width and read as a table rule rather than a link. */
  border-bottom: none;
}

.sw-toc a span { border-bottom: 1.5px solid transparent; }
.sw-toc a:hover span { border-bottom-color: currentColor; }

.sw-toc a::before {
  content: counter(toc) ".";
  flex: 0 0 1.6em;
  color: var(--text-muted);
  font-weight: var(--weight-regular);
}

.sw-toc a:hover { color: var(--violet-2); }

/* Anchored headings shouldn't land under the sticky header. */
.sw-prose h2[id] { scroll-margin-top: 104px; }

/* ============================================================
   Textarea
   The design system's Input renders a hard <input>, so a multi-line
   field has to be composed here. It reuses the system's own
   .sw-field / .sw-input classes so it is the same control, not a
   lookalike — only the multi-line specifics are added.
   ============================================================ */

.sw-textarea {
  min-height: 128px;
  line-height: 1.45;
  resize: vertical;
}

/* ---- Social ------------------------------------------------
   Circular brand buttons carrying the platform letterform only —
   not each platform's own coloured badge, which would drop two
   foreign brand shapes into a Sweet layout. Pill, like every other
   control in the system.
   ---------------------------------------------------------- */

.sw-social { display: flex; gap: 10px; margin-top: 4px; }

.sw-social__btn {
  width: 42px;
  height: 42px;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.14);
  color: var(--text-on-dark);
  display: grid;
  place-items: center;
  transition: background var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out),
              transform var(--duration-fast) var(--ease-out);
}

.sw-social__btn svg { width: 19px; height: 19px; display: block; fill: currentColor; }

.sw-social__btn:hover {
  background: var(--spark-lime);
  color: var(--focus-black);
  transform: translateY(-2px);
}

.sw-footer__legal {
  position: relative;
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 24px var(--band-pad-x) 40px;
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  justify-content: space-between;
  border-top: 1px solid var(--border-on-dark);
  font-size: 15px;
  color: rgba(255, 255, 255, 0.55);
}

/* UK trading disclosure — registered name, place of registration, company
   number and registered office must appear on the website. */
.sw-footer__copy { display: flex; flex-direction: column; gap: 6px; max-width: 62ch; }
.sw-footer__reg { font-size: 13px; line-height: 1.45; color: rgba(255, 255, 255, 0.4); }

.sw-footer__legal a { color: inherit; }
.sw-footer__legal a:hover { color: var(--spark-lime); }

/* ============================================================
   Header
   ============================================================ */

.sw-header {
  position: sticky;
  top: var(--page-inset);
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  /* Full width, sitting flush against the top of the window — a bar, not a
     floating tile. It pairs with the full-width footer at the other end. */
  top: 0;
  margin: 0;
  /* Held in variables so the dropdown panel can cancel them out and line its
     edges up with the header's, rather than with the inside of its padding.
     --page-inset is added on each side so the lockup and the nav still line up
     with the inset bands below. */
  /* FULL WIDTH, past the page cap (Rebecca, 15 October 2026): "can we keep the
     yellow nav bar to go the full width of the screen still". A negative margin
     of the gutter on each side puts the bar's edges back on the window's, while
     everything below it stays in the 1760px column — which is the look she
     picked. Below the cap --page-gutter is 0 and these resolve to exactly what
     they were.

     Same container-agnostic formula as the footer — see the note there. The
     header happens to sit inside .sw-page on all 36 pages today, but nothing
     guarantees that, and a layout that breaks on a page being nested one level
     differently is a trap for whoever adds the next page. */
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  /* ...and the same gutter goes back on as padding, so the lockup and the nav
     still line up with the bands underneath rather than sliding out to the
     window edges with the colour. */
  padding: var(--header-pad-y) calc(var(--header-pad-r) + var(--page-inset) + var(--page-gutter));
  padding-left: calc(var(--header-pad-l) + var(--page-inset) + var(--page-gutter));
  /* No radius at all. Full width and flush to the top, the bar has no visible
     corners: the top two and both sides run off-screen, and rounding the
     bottom two made it read as a floating tile again rather than a bar fixed
     to the window. Not a breach of the no-sharp-corners rule for the same
     reason as .sw-band--bleed — there is no visible corner to soften. */
  border-radius: 0;
  background: var(--spark-lime);
}

.sw-header__logo { display: flex; align-items: center; flex: 0 0 auto; }

/* ============================================================
   Brand marks
   Official files from the brand asset pack, in assets/Logos/SVG/.
   The claim in the `_claim_` lockups is outlined vector, not live
   text, so these are safe to use in an <img> — no webfont needed.

     Sweet_logo_claim_black|white|color   wordmark + claim  7.06 : 1
     Sweet_logo_black|white|color        wordmark only     4.33 : 1
     Sweet_symbol_black|white|color      symbol only

   Size marks by HEIGHT and let the width follow, so the aspect ratio
   can never drift. The `_safe_zone_` variants carry the brand's clear
   space baked in — don't use those here, spacing is handled in CSS.
   ============================================================ */

.sw-mark { display: block; width: auto; flex: 0 0 auto; }

/* The claim sits at roughly a sixth of the lockup height, so it stops
   being legible below about a 40px lockup. Under that, the brand falls
   back to the wordmark on its own. */
.sw-mark--lockup { height: 46px; }

/* ============================================================
   Co-brand lockup — Sweet x Pearson
   Both marks are sized from --cobrand-h so they stay in proportion
   to each other whatever the scale. Pearson's wordmark is a much
   wider, shorter shape than Sweet's, so it is set to a fraction of
   the Sweet mark's height rather than matching it — matched heights
   would leave Pearson visually twice the size.
   ============================================================ */

/* Stacked, not side by side. In a quarter-width hero column the horizontal
   lockup was squeezed to a 30px cap height to fit; stacked, both marks can be
   half again bigger in the same space.

   Sized by WIDTH here, not height. Side by side the marks were matched on cap
   height (Pearson at 0.70) so they read as equal partners on one baseline.
   Stacked there is no shared baseline — what makes them read as a pair is
   occupying the same column width. Their ratios are close enough (Sweet 4.32:1,
   Pearson 4.99:1) that equal width also keeps the letterforms similar in size. */
.sw-cobrand {
  --cobrand-w: clamp(120px, 14vw, 210px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(10px, 1.4vw, 20px);
}

.sw-cobrand__sweet,
.sw-cobrand__partner { width: var(--cobrand-w); height: auto; }

/* Side by side, the two marks need about 7.8x the Sweet mark's height in
   width. Below roughly 720px that forces the Sweet mark down to ~30px to
   fit, which is too small to read, so the lockup stacks instead. */
@media (max-width: 720px) {
  .sw-cobrand {
    --cobrand-h: clamp(40px, 14vw, 70px);
    flex-direction: column;
    align-items: flex-start;
    gap: 14px;
  }
}

.sw-cobrand__x {
  font-size: calc(var(--cobrand-h) * 0.42);
  font-weight: var(--weight-regular);
  color: var(--text-secondary);
  line-height: 1;
  user-select: none;
  /* Centred between the two marks, whatever the column is doing. Below 720px
     `.sw-cobrand` switches to `align-items: flex-start`; the two logos share
     one width so they still read as a centred stack, but the × is a short text
     node and went hard left of them. `align-self` rather than changing the
     column's alignment, because that flex-start is doing a job for the marks
     themselves. Stacked this centres it horizontally; in the row variant
     (`.sw-shopmarks`) the cross axis is vertical and it centres it against the
     marks' height, which is also what it should do. */
  align-self: center;
}

/* ============================================================
   Partnership page hero
   Mark and intro both sit left, stacked, in the deep part of the lime.
   ============================================================ */

.sw-cobrand-hero {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.sw-cobrand-hero__mark {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 22px;
}

/* Sits under the mark, still on the left. The top margin drops it into the
   deep part of the lime, clear of the rising slant on the right. */
.sw-cobrand-hero__intro {
  margin-top: clamp(48px, 10vw, 130px);
  max-width: 54ch;
  display: flex;
  flex-direction: column;
  gap: 18px;
  align-items: flex-start;
}

.sw-nav { display: flex; align-items: center; gap: 6px; font-size: 16px; font-weight: var(--weight-medium); }

/* ============================================================
   Full-width nav dropdown
   A <details> rather than a hover menu: it works on touch, is
   keyboard operable and exposes its open state to screen readers
   for free. A little JS in the page closes it on outside click,
   Escape, or after following a link.

   The panel is deliberately NOT positioned against .sw-dd — it
   stays static so the panel resolves against .sw-header (which is
   sticky, therefore a containing block) and can span its full
   width. The negative offsets cancel the header's padding so the
   panel's edges align with the header's, not with the inside of it.
   ============================================================ */

.sw-dd { position: static; }

.sw-dd > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
}

.sw-dd > summary::-webkit-details-marker { display: none; }
.sw-dd > summary::marker { content: ''; }
/* The hover/open treatment for these lives with the nav links above, so the
   two behave identically. */

.sw-dd__chev { transition: transform var(--duration-base) var(--ease-out); }
.sw-dd[open] .sw-dd__chev { transform: rotate(180deg); }

.sw-dd__panel {
  position: absolute;
  /* An absolutely positioned box resolves against its ancestor's PADDING box,
     which for a border-box element with no border is the same rectangle as its
     border box — so 0/0 already lines the panel up with the header's edges.
     Offsetting by the header padding on top of that overshoots. */
  /* 0 was right while the header WAS the page width. Now the bar runs the full
     window and the page does not, so 0 would throw the panel out to the window
     edges and stretch its items across a 2560px screen. The gutter puts it back
     on the page column, under the nav it belongs to. 0 below the cap. */
  left: var(--page-gutter);
  right: var(--page-gutter);
  /* FLUSH TO THE BAR, no 12px gap (Rebecca, 15 October 2026): "i want it to be
     seamless like the original nav is growing". The panel is the same Spark
     Lime as the header, so with no gap and no top corners the two read as one
     shape that grows downward rather than a card floating under a bar. */
  top: 100%;
  /* 32px panel with 16px padding gives 16px items — the concentric rule */
  padding: 16px;
  /* Square at the top, where it meets the header; rounded only where it ends. */
  border-radius: 0 0 var(--radius-xl) var(--radius-xl);
  /* Spark Lime, 13 October 2026, so the panel reads as the header continuing
     rather than as a white sheet dropped over the page. It was --simple-white,
     and the items inside it were --surface-page (Paper Ivory) — two near-whites
     stacked, which is why the tiles barely showed. */
  background: var(--surface-brand);
  /* NOT --shadow-pop. That is `0 12px 32px`, and a 32px blur against a 12px
     offset spreads 20px UPWARDS — which painted a faint dark line along the
     join and undid the seamlessness. Measured at the seam: lime at
     (234,248,111) above it against (238,252,112) below. A negative spread
     keeps the whole shadow below the panel's top edge. */
  box-shadow: 0 18px 30px -10px rgba(11, 30, 47, 0.22);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}

/* Each destination is a tile you can see, not just a hover target. */
.sw-dd__item {
  display: block;
  /* .sw-nav a sets nowrap for the top-level links; these tiles hold sentences
     and must be allowed to wrap or the descriptions get clipped. */
  white-space: normal;
  padding: 20px 22px;
  border-radius: var(--radius-md);
  /* White on the lime panel. Paper Ivory was right against a white panel and
     is far too close to lime to separate from it. */
  background: var(--simple-white);
  /* NO STROKE (Rebecca, 15 October 2026): "in the nav i dont like the grey
     strokes on tiles". White tiles on a Spark Lime panel have all the edge
     they need from the colour underneath them. A transparent border of the
     same width keeps the tile's size identical, so nothing shifts by 3px when
     the navy hover state swaps the colour in. */
  border: var(--border-width) solid transparent;
  transition: background var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out),
              transform var(--duration-fast) var(--ease-out);
}

/* Hover used to fill the item with Spark Lime. That is the panel's own colour
   now, so a hovered item would have dissolved into the background behind it —
   the exact inversion of what a hover is for. Paper Ivory instead: a quiet
   shift off white, with the Focus Black stroke and the lift still doing the
   work. */
.sw-dd__item:hover,
.sw-dd__item:focus-visible {
  background: var(--focus-black);
  border-color: var(--focus-black);
  transform: translateY(-2px);
}

/* Both lines declare their own colour, so neither inherits the white. */
.sw-dd__item:hover .sw-dd__title,
.sw-dd__item:focus-visible .sw-dd__title { color: var(--text-on-dark); }

.sw-dd__item:hover .sw-dd__desc,
.sw-dd__item:focus-visible .sw-dd__desc { color: rgba(255, 255, 255, 0.78); }

.sw-dd__title { font-size: 18px; font-weight: var(--weight-medium); color: var(--text-primary); }

.sw-dd__desc {
  margin-top: 5px;
  font-size: 15px;
  line-height: 1.4;
  font-weight: var(--weight-regular);
  color: var(--text-secondary);
}

/* Sub-links inside the mobile disclosure menu */
.sw-menu__sub {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: -2px 0 2px 14px;
  padding-left: 14px;
  border-left: 1.5px solid var(--border-subtle);
  font-size: 16px;
  font-weight: var(--weight-regular);
}
/* Direct children only. `.sw-nav a` would also catch the dropdown tiles,
   whose descriptions are sentences that must wrap — and it would win on
   specificity over any class-only override on the tile itself. */
.sw-nav > a { white-space: nowrap; }

/* NAVY ON HOVER AND CLICK (Rebecca, 15 October 2026), replacing a violet text
   colour on both the links and the dropdown summaries.

   It had to become a navy PILL rather than navy text: the nav sits on Spark
   Lime and its rest colour is already Focus Black, so "hover goes navy" as a
   text colour would have been no visible change at all.

   That means padding and a radius at rest, with the background transparent
   until it is needed — a pill cannot appear on hover if there is no box to
   fill. The nav's gap drops from 26px to 8px to pay for it; the padding now
   does the separating, and without that trade the bar would have grown by
   roughly 130px at a width where it already has very little room.

   [open] is in the list so an open menu stays marked while you read it. */
.sw-nav > a,
.sw-dd > summary {
  /* 10px, not 13px. Three links replaced one dropdown on 15 October 2026, and
     the pills this padding creates are what the bar can least afford: at 13px
     the flat nav needed a 1,546px window, at 10px it needs 1,468px. */
  padding: 8px 10px;
  border-radius: var(--radius-pill);
  transition: background var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out);
}

.sw-nav > a:hover,
.sw-nav > a:active,
.sw-nav > a:focus-visible,
.sw-dd > summary:hover,
.sw-dd > summary:active,
.sw-dd > summary:focus-visible,
.sw-dd[open] > summary {
  background: var(--focus-black);
  color: var(--text-on-dark);
}
.sw-nav__cta { display: inline-flex; }

/* Mobile disclosure menu — no JS, keyboard accessible. */
.sw-menu { display: none; position: relative; }
.sw-menu > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 8px; padding: 10px 18px; border-radius: var(--radius-pill); background: var(--focus-black); color: var(--text-on-dark); font-size: 15px; font-weight: var(--weight-medium); }
.sw-menu > summary::-webkit-details-marker { display: none; }
.sw-menu__panel {
  position: absolute;
  right: 0;
  top: calc(100% + 12px);
  min-width: 240px;
  padding: 18px;
  border-radius: var(--radius-lg);
  background: var(--simple-white);
  box-shadow: var(--shadow-pop);
  display: flex;
  flex-direction: column;
  gap: 14px;
  font-size: 17px;
  font-weight: var(--weight-medium);
}

/* ============================================================
   Reveal on scroll
   [data-reveal] must never carry a transform of its own — the
   reveal animates transform, so it would fight anything the element
   already had. Wrap, don't combine.
   ============================================================ */

[data-reveal] {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out);
}

[data-reveal].is-revealed { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1; transform: none; transition: none; }
  /* Tags sit at their final angle immediately — no drop. */
  [data-drop] {
    opacity: 1;
    transform: translate(var(--dx, 0px), var(--dy, 0px)) rotate(var(--rot, 0deg));
    transition: none;
  }
  .sw-marquee__track { animation: none; }
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ============================================================
   Responsive
   ============================================================ */

@media (max-width: 1180px) {
  .sw-team > * { flex: 0 0 calc((100% - 24px) / 2); }
  :root { --band-pad-k: 0.74; --band-gap: 48px; }
  .sw-grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px; }
}

/* THE BURGER TAKES OVER AT 1470px, not 1000px, and that is the real cost of
   flattening "Where you are" into the bar.

   Measured by bisection — the width at which the last item's right edge stops
   crossing the header's padding:

     Sweet in Wales / Sweet in England / Alternative Provision   1,684px
     Wales / England / Alternative Provision                     1,468px
     ...with Contact off the bar as well                         1,450px
     ...with News and Contact both off                           1,375px
     ...at 9px pills, 4px gaps and 15px type                     1,398px

   So there is no arrangement of nine top-level items that fits a 1,280 or
   1,366 laptop. Below 1470px the burger holds every link, the three new ones
   included, so nothing is lost — but a 1440 MacBook now gets the menu button
   where it used to get the full bar. Dropping Contact from the bar would buy
   back about 20px of that, and News and Contact together about 90px.

   It is its OWN query rather than part of the 1000px block below, which does a
   great deal of unrelated layout work that must stay where it is. */
@media (max-width: 1470px) {
  .sw-nav { display: none; }
  .sw-menu { display: block; }
}

@media (max-width: 1000px) {

  /* Splits stack here, and the stacked column is capped. Both numbers are
     measured, and the first attempt at this got the diagnosis backwards.

     TRUE characters per line (text length / rendered line count — NOT a
     width/0.5em estimate, which overstates by about half because the CSS `ch`
     unit is the width of "0" and running text averages far narrower):

                        before    stacking at 1180 instead
         desktop         48/77         48/77
         iPad landscape  42/68         42/77   page 4,894 -> 6,431px
         iPad portrait   64/82         63/77
         mobile          34/39         34/39

     Moving the breakpoint to 1180 so iPad landscape stacked made it worse on
     every count: a longer max measure and a third more page. Two columns at
     1024 were never the problem — 68 is inside the comfortable 45-75 band and
     that view was already the shortest on the site. Only iPad portrait was out
     of range, at 82, and only because the stacked column ran the full band
     width. So the breakpoint stays at 1000 and the cap does the work.

     56ch, not 62ch: 62ch measured 630px, which is 77 real characters, still
     over. 56ch lands around 70. */
  .sw-split { grid-template-columns: minmax(0, 1fr); gap: 40px; }
  .sw-split > .sw-stack { max-width: 56ch; }

  /* Stacked, each panel ran the full band width — on iPad portrait the book
     panel and the evidence photo were 656x492 apiece, 48% of the viewport
     each, which is most of why that view was nearly as long as the phone
     despite twice the width. */
  .sw-splitphoto,
  .sw-bookcycle,
  .sw-coverfan { max-width: 520px; }
  .sw-collage { max-width: 560px; }
  .sw-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .sw-team > * { flex: 0 0 calc((100% - 24px) / 2); }
  .sw-shop-grid > * { flex: 0 0 calc((100% - 24px) / 2); }
  .sw-basket { grid-template-columns: minmax(0, 1fr); }
  .sw-choice { grid-template-columns: minmax(0, 1fr); }
  .sw-formgrid { grid-template-columns: minmax(0, 1fr); }
  .sw-summary { position: static; }
  .sw-footer__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 36px; }

  /* Hero stacks: copy first, photo below. The signature slant moves from the
     vertical edge to a horizontal one along the bottom of the copy block, so
     the photo gets a slanted top edge.

     The absolutely-positioned desktop fill IS still dropped — it spans the
     whole hero and would paint over the photo, which now sits in normal flow.
     What carries the blue instead is the copy block's own background, which
     is in flow and stops where the copy stops.

     The blue matters and was missing until 25 September 2026 (Rebecca: "the
     blue background has gone from the hero"). On desktop the left half is
     --gradient-curious-blue and the diagonal reads as one shape divided in
     two; stacked on a phone the copy block was plain white on a white page,
     which is the exact "shapeless" failure the desktop fill exists to fix —
     all you saw was a photograph with a slanted edge floating in nothing. */
  .sw-hero { display: flex; flex-direction: column; min-height: 0; background: var(--surface-page); }
  .sw-hero__fill { display: none; }

  .sw-hero__content {
    order: 1;
    position: relative;
    z-index: 2;
    width: auto;
    background: var(--gradient-curious-blue);
    padding: 56px 26px 68px;
  }

  /* The slanted edge: a strip straddling the bottom of the copy block,
     oversized left and right so only its BOTTOM edge is a diagonal.

     IT MUST NOT CARRY --gradient-curious-blue ITSELF. That was tried and the
     seam is plainly visible: the token is a 45° ramp, so it recomputes against
     the strip's own box, and a 370x72 strip runs that ramp almost horizontally
     where the 370x400 block above runs it diagonally. Two different ramps
     meeting is a hard edge straight across the hero.

     So the strip is painted to MATCH THE ROW IT JOINS. Sampled off the rendered
     block's bottom edge at 375px: rgb(230,243,250) at the left, rgb(212,241,251)
     at the right — the left end is the token's own start colour (#E7F3FA) and
     the right end is about 43% along the ramp to #BBEFFD. The strip is only
     72px deep, so it has no meaningful vertical variation of its own and a 90°
     gradient across it is the honest reproduction.

     Same class of thing as --slant-rise's 0.041914 written beside "tan(2.4°)":
     a value CSS cannot derive, so it is written down next to how to re-derive
     it. If --gradient-curious-blue ever changes, re-sample the block's bottom
     row and update both stops.

     border-bottom is the white rule along the diagonal, the same device as the
     desktop fill's border-right and for the same reason — the blue panel and
     the photograph read as two panels meeting rather than one fading into the
     other. box-sizing is border-box site-wide, so it eats into the strip
     rather than deepening it. */
  .sw-hero__content::after {
    content: '';
    position: absolute;
    left: -12%;
    right: -12%;
    top: calc(100% - 34px);
    height: 72px;
    background: linear-gradient(90deg, #E7F3FA, #D4F1FB);
    border-bottom: 16px solid var(--simple-white);
    transform: skewY(calc(-1 * var(--slant-h)));
  }

  .sw-hero__media {
    order: 2;
    position: relative;
    inset: auto;
    width: 100%;
    min-width: 0;
    overflow: hidden;
    height: clamp(300px, 56vw, 460px);
  }
}

@media (max-width: 720px) {
  :root {
    --band-pad-k: 0.54;
    --band-pad-x: 22px;
    --band-gap: 44px;
    --radius-2xl: 30px;
    --page-inset: 10px;
  }
  .sw-grid-2, .sw-grid-3, .sw-grid-4 { grid-template-columns: minmax(0, 1fr); }

  /* The homepage stat band becomes four coloured rectangles (Rebecca,
     25 September 2026).

     At desktop the four figures sit in four columns and the COLUMNS are what
     separate them. Stacked on a phone that separation is gone: four figures
     and four labels run down one navy column as a single block, and "250+"
     ends up with no more standing than the "2" under it. A tile each gives
     every figure its own edge back.

     The band stays navy. Four accent tiles on a dark ground is what
     .sw-team, .sw-collage and .sw-topics already do on light ones, so it is
     not the "box inside a box" that keeps .sw-leif flush — and the navy is
     carrying this section's slant, so dropping it would take the diagonal
     and .sw-band--clear-slant's whole job with it. The tiles measure
     7.07–15.14 : 1 against the navy, so all four read as separate shapes.

     Type goes to full Focus Black, the .sw-on-mid rule: .sw-stat__label is
     explicitly rgba(255,255,255,.7) for the navy and is invisible on lime.
     Measured on the four grounds — 7.07, 15.14, 12.50 and 8.67 : 1.

     The survey credit is a <p>, not a .sw-stat, so it stays plain text on
     the navy and keeps `.sw-on-dark .sw-source`. It is a credit, not a
     fifth statistic, and a tile would make it look like one.

     Scoped with :has() because .sw-grid-4 also lays out PGW's four book
     units, which keep their own gap. Same device as
     .sw-heroband:has(.sw-herophoto). */
  .sw-grid-4:has(> .sw-stat) { gap: 12px; }
  .sw-stat {
    padding: 24px 22px 26px;
    border-radius: var(--radius-lg);
    color: var(--focus-black);
  }
  .sw-stat .sw-stat__label,
  .sw-stat .sw-source { color: var(--focus-black); }
  .sw-stat:nth-child(4n + 1) { background: var(--accent-violet); }
  .sw-stat:nth-child(4n + 2) { background: var(--spark-lime); }
  .sw-stat:nth-child(4n + 3) { background: var(--accent-blue); }
  .sw-stat:nth-child(4n + 4) { background: var(--accent-green); }

  .sw-team > * { flex: 0 0 100%; }
  .sw-shop-grid > * { flex: 0 0 100%; }
  .sw-line { grid-template-columns: 72px minmax(0, 1fr); gap: 14px; }
  .sw-line__media { width: 72px; height: 72px; }
  .sw-line__qty, .sw-line__total, .sw-line__remove { grid-column: 2; justify-self: start; }
  .sw-spec dt { flex: 0 0 46%; }
  .sw-footer__grid { grid-template-columns: minmax(0, 1fr); }
  .sw-header { --header-pad-y: 12px; padding: var(--header-pad-y) 12px var(--header-pad-y) 18px; }
  /* Below this the claim inside the lockup is too small to read, so the
     header swaps to the wordmark-only mark. The `content` swap keeps it to
     one element rather than shipping two images and hiding one. */
  .sw-mark--lockup {
    height: 30px;
    content: url('../Logos/SVG/Sweet_logo_color.svg');
  }
  .sw-figure { margin-left: 0; }
  .sw-quote-card__foot { flex-direction: column; align-items: flex-start; }
}

/* ============================================================
   Course information pages
   Personal Growth and Wellbeing, WorkSkills, Wave 3.
   ============================================================ */

/* A unit of the qualification: number, title, strand, what it covers. The
   number is the thing people scan for ("which book is the money one?"), so it
   leads. */
.sw-unit {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 24px;
  border-radius: var(--radius-lg);
  background: var(--simple-white);
  box-shadow: var(--shadow-soft);
}

.sw-unit__n {
  align-self: flex-start;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--accent-blue);
  font-size: var(--text-small);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* A .sw-unit used to ROUTE rather than to list: heading, one line, a way
   through. The link is pinned to the bottom so two of them square off however
   long the copy above it runs — the pair is read across, not down. */
.sw-unit--route { gap: 10px; }
.sw-unit--route .sw-btnrow { margin-top: auto; padding-top: 16px; }

/* ---- A route card that carries a photograph -----------------------------
   Rebecca, 15 October 2026, on the Alternative Provision qualification cards:
   "these can be coloured and have photos of the books". The colour is the
   course colour the rest of the site already uses — Curious Blue for PGW,
   Progress Green for WorkSkills — set inline on the card, so this class only
   has to make room for the picture.

   The media panel is WHITE, not the card colour: these are cut-out book fans
   with no background of their own, and on a saturated card they sat in a tint
   that fought their covers. fit="contain" keeps the whole fan.

   Negative margins pull the panel out to the card's edges, since .sw-unit
   carries its own padding and a photo inset by it reads as a stamp rather
   than part of the card. */
.sw-unit--photo { padding-top: 0; overflow: hidden; }

.sw-unit__media {
  position: relative;
  margin: 0 calc(-1 * var(--unit-pad, 26px)) clamp(18px, 2vw, 24px);
  aspect-ratio: 5 / 2;
  background: var(--simple-white);
}

.sw-unit__media image-slot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  display: block;
}

/* Mid-tones, so body copy goes to full Focus Black exactly as .sw-on-mid
   would do it — the cards are coloured but the band around them is not. */
.sw-unit--photo .sw-unit__t,
.sw-unit--photo .sw-unit__d { color: var(--focus-black); }

.sw-unit__t { margin: 4px 0 0; font-size: 20px; line-height: 1.15; letter-spacing: -0.02em; }
.sw-unit__s { font-size: var(--text-small); font-weight: var(--weight-semibold); color: var(--violet-2); }
.sw-unit__d { font-size: 15px; line-height: 1.5; color: var(--text-secondary); }

/* Price list. Two columns, rule between rows — a real table would be heavier
   than this needs to be, and these are name/price pairs, not tabular data. */
.sw-prices { display: flex; flex-direction: column; }

.sw-prices__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 20px;
  padding: 14px 0;
  border-bottom: var(--border-width) solid var(--border-subtle);
}

.sw-prices__row:last-child { border-bottom: 0; }
.sw-prices__row span:last-child { font-weight: var(--weight-semibold); white-space: nowrap; }

/* One column: label above value, not label-left / value-right.
   `.sw-prices` is shared with four pricing tables where the two-column form is
   right — a figure belongs hard right of its label. Contact's company details
   are not figures, they are facts of varying length, and set as a price table
   they read as a bill. A variant rather than a change to the shared rule.

   The value also loses `white-space: nowrap`: stacked it has the full width and
   should wrap normally rather than push the column. */
.sw-prices--stack .sw-prices__row { display: block; padding: 12px 0; }

.sw-prices--stack .sw-prices__row span:first-child {
  display: block;
  font-size: 14px;
  color: var(--text-secondary);
}

.sw-prices--stack .sw-prices__row span:last-child {
  display: block;
  margin-top: 2px;
  white-space: normal;
}

/* FAQ. A <details> for the same reasons as the nav dropdown: works on touch,
   keyboard operable, and its open state is exposed to screen readers. */
/* One module holding all the questions, rather than a stack of separate
   floating cards. A list of questions is a single object — as loose cards each
   answer looked like its own topic, and the gaps between them competed with
   the gaps inside an opened answer. */
/* Common questions — every answer visible, no accordion.

   The questions and answers used to be <details>/<summary>. Collapsed content is
   not hidden from Google, but a flat list of real <h3> headings each followed by a
   <p> is markedly easier for answer engines and AI crawlers to lift a clean
   question-and-answer pair out of, and it needs no interaction to read. Nothing is
   behind a click now.

   THE NEXT STEP, and the bigger lever for AEO, is FAQPage JSON-LD generated from
   these same pairs — the markup below is already the right shape for it. */
/* Two columns. With every answer now visible the list runs long, and a single
   column of six left the page scrolling past a tall white slab; two columns halve
   that and let someone take the whole set in at a glance.

   A grid, not CSS `columns`: multi-column would let a question break away from its
   own answer across the column boundary. Reading order stays top-to-bottom then
   across, which is also the DOM order, so a screen reader and an answer engine get
   the pairs in the same sequence they appear. */
/* NO TILE (Rebecca, 11 October 2026: "Common questions - take out of tile").
   It was a white card with a soft shadow. Two things were wrong with that:

   On the six bands where the FAQ sits on the Curious Blue gradient, a white
   slab the full width of the band covered the colour the band was chosen for —
   the gradient only ever showed as a frame around the card. On the two white
   bands (Pearson, Which Course) the card was white on white, so all it
   contributed was a shadow around nothing.

   The rules between the questions are what organised this, not the card. They
   stay. The copy now sits straight on the band, which is the same move as
   #units further up this page — ".sw-band--flush, straight on the page
   ground". */
.sw-faq {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.sw-faq__item { padding: 22px 26px 24px; min-width: 0; }

/* Without a card there is no inner edge to sit off, so the left column goes
   flush to the band and lines up with the heading above it. The right column
   keeps its padding, which is now the gap after the centre rule rather than
   the card's inset. */
.sw-faq__item:nth-child(2n + 1) { padding-left: 0; }

/* Rules between rows and one down the middle, drawn per cell rather than with a
   single divider, because the grid has no element to hang a centre line on.
   `:nth-child(2n)` is the right-hand column, so it gets the left-hand rule. */
.sw-faq__item:nth-child(n + 3) { border-top: var(--border-width) solid var(--border-subtle); }
.sw-faq__item:nth-child(2n) { border-left: var(--border-width) solid var(--border-subtle); }

/* One column below the site's single-column breakpoint — two columns of prose in
   half a phone screen is unreadable. The centre rule has to go with it, and the
   row rule goes back to every item after the first.
   Every item is now a left-hand item too, so every item goes flush. */
@media (max-width: 720px) {
  .sw-faq { grid-template-columns: minmax(0, 1fr); }
  .sw-faq__item { padding-left: 0; padding-right: 0; }
  .sw-faq__item:nth-child(2n) { border-left: 0; }
  .sw-faq__item:nth-child(n + 2) { border-top: var(--border-width) solid var(--border-subtle); }
}

.sw-faq__q {
  margin: 0 0 8px;
  font-size: 18px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}

.sw-faq__a { margin: 0; font-size: 16px; line-height: 1.6; color: var(--text-secondary); }

/* ---- The Wave 3 framework bento — REMOVED 16 September 2026 --------------
   `.sw-bento` / `.sw-pillar` drew the four Learner Entitlement Indicators
   pillars in Sweet's brand colours, with pillar 3 given a tall cell because
   that is where Sweet sits.

   Replaced by `.sw-leif`, a faithful reproduction of the Welsh Government's own
   infographic in ITS colours and columns, on Rebecca's instruction: teachers
   need to recognise the document. The bento was the right answer to "summarise
   this framework" and the wrong answer to "show this framework".

   Do not bring it back here. If a summary of the four pillars is ever wanted
   elsewhere, know that the bento's colouring only worked because the layout
   already marked pillar 3 out by AREA — four equal coloured bars destroy the
   one signal saying which pillar is Sweet's, which is why `.sw-leif` uses a
   flag beside the framework instead of altering it. */

/* Qualification size tables (PGW). On sweet.education these are flat images,
   which cannot be read by a screen reader, searched, translated or resized.
   Rebuilt here as real tables from those images.

   Level 2 takes Curious Blue and Level 1 Future Violet — the source images use
   blue and orange, and orange is not in the Sweet palette. Both clear AA
   against Focus Black. */
/* 26px, once, in the stylesheet. Six tables carried this as an inline
   margin-top in three different values — 24, 26 and 28px — which is the same
   ad-hoc spacing the band padding modifiers were written to end. It has to
   live here anyway, because the swipe hint needs to be able to take the gap
   over when it appears, and an inline style cannot be overridden by a
   selector. */
.sw-qtable-wrap {
  margin-top: 26px;
  /* Clips the table's own square corners to the wrapper's radius, so every
     corner of every table is curved — including the dark header's top two.
     overflow-x also gives the table a scroll of its own at narrow widths
     instead of pushing the page sideways. */
  overflow: hidden;
  overflow-x: auto;
  border-radius: var(--radius-lg);
  background: var(--simple-white);
  box-shadow: var(--shadow-soft);
}

/* table-layout: fixed makes every table exactly the width of its container,
   whether it has five columns or six. Left to size themselves, the Wales table
   (six columns) ran wider than the England one (five) and the pair looked
   misaligned down the page. */
/* min-width is what the table is allowed to shrink to before .sw-qtable-wrap
   scrolls it sideways, and 640px was too tight for the six-column Wales table.
   Measured at 390px: the level column takes its 8.5em and the other five get
   107px each, where the longest row header ("L2: Extended Certificate") needs
   211px. With nowrap it overflowed by 104px and painted over the next column;
   without it, it folded to FIVE lines and made a row four times taller than
   its neighbours. Neither is a table anyone can read.

   920px gives those five columns about 160px, so the longest label sets in two
   lines — the same two lines it takes at desktop, where the column is 221px.
   The cost is more sideways scroll on a phone, which the wrapper was already
   doing at 640px; the gain is that what you scroll to is legible. */
.sw-qtable {
  width: 100%;
  min-width: 920px;
  table-layout: fixed;
  border-collapse: collapse;
  font-size: 15px;
}

/* The level column only ever holds "Level 1" or "Level 2", so it does not need
   an equal share of a fixed layout. */
.sw-qtable .sw-qtable__lvl { width: 8.5em; }

.sw-qtable th,
.sw-qtable td { overflow-wrap: break-word; }

/* The table's caption is its heading — a table is the one element that has a
   proper heading slot of its own, so it does not need an <h3> above it as
   well. */
/* The caption is the table's title bar: Focus Black, running the full width, so
   the table reads as one object from its heading down rather than as a heading
   floating above a separate slab. */
.sw-qtable caption {
  caption-side: top;
  padding: 18px 16px 16px;
  text-align: left;
  font-size: clamp(19px, 1.8vw, 24px);
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-weight: var(--weight-semibold);
  background: var(--focus-black);
  color: var(--text-on-dark);
}

.sw-qtable th,
.sw-qtable td {
  padding: 12px 16px;
  text-align: left;
  vertical-align: middle;
}

/* Paper Ivory, not Focus Black — the caption above is now black, and two dark
   rows stacked would merge into one slab and lose the column headings inside it.
   Ivory is also the only neutral available: Curious Blue and Future Violet are
   already doing a job here, marking Level 2 and Level 1, and reusing either for
   the header would blur what the colour means. */
/* No nowrap on the column headings either, and for the same reason as the row
   headers. Uppercase with 0.06em tracking is wide: at the 920px min-width the
   five content columns are 153px and "Performance Points" needs 191px,
   "Qualification code" 179px, "Units to complete" 172px, "GCSE equivalency"
   168px — all four painted over their neighbours. They set on one line at
   desktop, where the column is 221px, and fold to two only where they must. */
.sw-qtable thead th {
  background: var(--paper-ivory);
  color: var(--text-primary);
  font-size: var(--text-small);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.sw-qtable tbody tr + tr td { border-top: var(--border-width) solid rgba(11, 30, 47, 0.12); }
/* NOT `white-space: nowrap`. It was, and on a phone it broke the table
   (Rebecca, 25 September 2026: "for mobile this isn't rendering right").

   `table-layout: fixed` at the 640px min-width gives six columns about 107px
   each once the level column takes its 8.5em. "L2: Extended Certificate" needs
   211px set on one line, so it overflowed its cell by 104px and painted across
   the next column — six of the eight row headers did, by 27 to 104px. A fixed
   cell does not grow for content it cannot break.

   Letting it wrap costs nothing at desktop, where the same column is about
   234px and the longest label fits on one line anyway — so the labels still
   read as single lines wherever there is room, and only fold where the
   alternative was overlapping text. */
.sw-qtable tbody th[scope="row"] { font-weight: var(--weight-semibold); }

/* The level column is a rowspan, so the tint groups the four sizes under it. */
.sw-qtable .sw-qtable__lvl { font-size: 17px; font-weight: var(--weight-semibold); }
/* Tints, not the accents at full strength. A table is read, not looked at, and
   two saturated bands running the full width put more colour on screen than any
   other element on the page — the banding was shouting louder than the figures
   it was there to organise. Held back to a wash, the level grouping still reads
   at a glance and the numbers come forward.

   Violet takes a lower alpha than blue for the same apparent weight: it is the
   darker of the two, so matched alphas leave the Level 1 block visibly heavier.
   Focus Black stays well clear of AA on both — 13.4:1 and 10.9:1. */
.sw-qtable tbody .l2 { background: rgba(144, 235, 255, 0.55); }
.sw-qtable tbody .l1 { background: rgba(150, 160, 255, 0.42); }
.sw-qtable tbody .l2--soft { background: rgba(144, 235, 255, 0.22); }
.sw-qtable tbody .l1--soft { background: rgba(150, 160, 255, 0.17); }

/* On the navy curriculum band. --shadow-soft is Focus Black at 8% — invisible
   on a Focus Black band — and the table's caption bar is Focus Black too, so
   the white card appeared to begin at the column headings with its title
   floating loose on the band above it. That is exactly the "heading floating
   above a separate slab" the caption was built to prevent.

   A white hairline around the whole wrapper fixes it without touching the
   caption: the card is already white, so the ring shows up ONLY where the card
   is dark — around the caption — and is invisible everywhere else. */
/* On the navy curriculum band the caption goes WHITE (Rebecca, 11 October
   2026: "use white for the table headers", "i dont want strokes on the
   table").

   Those two are the same fix. The caption is Focus Black, which on a Focus
   Black band is the band — so the white card appeared to start at the column
   headings with its title floating loose above it, and the stroke was there to
   draw the missing edge. Whiten the caption and the card is white all the way
   to its top corners: it defines itself against the navy, and the stroke has
   nothing left to do. Taken off rather than kept, which is what she asked for.

   Scoped to .sw-on-dark, which as of 11 October 2026 is the PGW curriculum
   band and the three pricing bands. The same table on WorkSkills Wales and
   WorkSkills England still sits on a light band, where the dark caption bar is
   doing its original job and still reads. */
.sw-on-dark .sw-qtable caption {
  background: var(--simple-white);
  color: var(--text-primary);
}

/* LIGHT SURFACES INSIDE A DARK BAND TAKE THEIR TEXT COLOUR BACK.
   `.sw-on-dark` sets white on the SECTION and colour inherits — straight
   through any white card sitting on that band and into anything inside it that
   declares no colour of its own. The result is white text on a white card.

   It has bitten twice now, both times on the element the band existed for:

     .sw-qtable tbody — every figure in the qualification table measured 1.07:1
     to 1.41:1 against the 13.4:1 and 10.9:1 its tints were chosen for.

     .sw-unit — the pricing tiles are white cards, and neither .sw-unit__t nor
     the name/price spans in .sw-prices set a colour. Every price would have
     gone white on white.

   Anything else given a light background inside a dark band belongs on this
   list. Children that DO declare a colour (.sw-unit__d, .sw-unit__s, thead th,
   caption) are unaffected either way. */
.sw-on-dark .sw-qtable tbody,
.sw-on-dark .sw-iqa,
.sw-on-dark .sw-unit { color: var(--text-primary); }

/* ...and the rule above is NOT enough on its own, because inheritance only
   reaches what declares nothing. Every `.sw-on-dark X { color: ... }` rule in
   this stylesheet is an explicit declaration aimed at a dark GROUND, and it
   keeps firing inside a white card sitting on that ground.

   Found the moment the pricing bands went navy: three .sw-textlinks inside the
   white WorkSkills pricing cards — "Wales", "England", "info@sweet.education" —
   were white on white. 1:1. Not low contrast, gone.

   So a light card is treated as a light CONTEXT and the dark-ground overrides
   are handed back their light-ground values. Add to this block whenever a new
   `.sw-on-dark X` colour rule is written, or the next white card on a dark band
   loses that element too. */
.sw-on-dark .sw-iqa .sw-textlink,
.sw-on-dark .sw-unit .sw-textlink { color: var(--text-primary); }
.sw-on-dark .sw-iqa .sw-textlink:hover,
.sw-on-dark .sw-unit .sw-textlink:hover { color: var(--violet-2); }
.sw-on-dark .sw-iqa .sw-body,
.sw-on-dark .sw-unit .sw-body,
.sw-on-dark .sw-unit .sw-lede,
.sw-on-dark .sw-unit .sw-source { color: var(--text-secondary); }
.sw-on-dark .sw-unit .sw-eyebrow { color: var(--text-muted); }
.sw-on-dark .sw-iqa .sw-iqa__h,
.sw-on-dark .sw-unit .sw-h2,
.sw-on-dark .sw-unit .sw-h3,
.sw-on-dark .sw-unit .sw-display { color: var(--text-primary); }

/* "Swipe to see every column", injected by assets/js/table-scroll.js and only
   where the table genuinely runs off the side. The arrow points the way the
   table moves and is a ::after rather than a character in the string, so it
   cannot be read aloud as "right arrow" after the sentence.

   Sits above the table, not below it: an instruction underneath something is
   an instruction you find after you needed it. */
/* The hint takes the table's top gap over and keeps only a small space under
   itself, so it reads as attached to the TABLE rather than to whatever
   paragraph happens to end above it. Stacked on a phone it was doing the
   opposite: glued to the prose, 26px clear of the thing it describes. */
.sw-qtable-hint + .sw-qtable-wrap { margin-top: 0; }

.sw-qtable-hint {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 26px 0 10px;
  font-size: var(--text-small);
  font-weight: var(--weight-medium);
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}

.sw-qtable-hint::after {
  content: "\2192"; /* → */
  font-size: 17px;
  line-height: 1;
  /* Nudged along and back, so it reads as the table moving rather than as a
     static arrow glyph. Stops entirely for anyone who has asked it to. */
  animation: sw-qtable-nudge 1.8s var(--ease-out) infinite;
}

@keyframes sw-qtable-nudge {
  0%, 60%, 100% { transform: translateX(0); }
  30%           { transform: translateX(5px); }
}

@media (prefers-reduced-motion: reduce) {
  .sw-qtable-hint::after { animation: none; }
}

.sw-on-dark .sw-qtable-hint { color: rgba(255, 255, 255, 0.72); }

/* The scroller becomes a keyboard-reachable region when it overflows, so it
   needs a focus ring that is visible on both the light bands and the navy one. */
.sw-qtable-wrap:focus-visible {
  outline: var(--border-width) solid var(--focus-black);
  outline-offset: 3px;
}

.sw-on-dark .sw-qtable-wrap:focus-visible { outline-color: var(--simple-white); }

/* Course page enquiry form. Reuses the checkout's field styles; what is added
   here is the submit button, the invalid state and the error line. */
.sw-enquiry { display: flex; flex-direction: column; }

.sw-submit {
  margin-top: 18px;
  padding: 17px 28px;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--focus-black);
  color: var(--simple-white);
  font: inherit;
  font-weight: var(--weight-semibold);
  cursor: pointer;
  transition: transform var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out);
}

.sw-submit:hover { transform: translateY(-2px); box-shadow: var(--shadow-pop); }

/* Every field that failed is marked, not just the first — see enquiry-form.js. */
.sw-enquiry .is-invalid { box-shadow: 0 0 0 2px var(--focus-black); }

/* Class-based, not the `hidden` attribute: the DC runtime strips bare boolean
   attributes on re-render. Same reason as the level-required error. */
.sw-enquiry .sw-inlineerr { display: none; margin-top: 14px; }
.sw-enquiry .sw-inlineerr.is-shown { display: block; }
.sw-formok:empty { display: none; }
.sw-formok { margin-top: 10px; font-size: 15px; font-weight: var(--weight-semibold); }

/* Full width, so the six short fields go three across and the whole form is
   two tidy rows plus the message — rather than a narrow column beside the
   intro text, which made every field unnecessarily cramped. */
.sw-enquiry .sw-formgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); }

@media (max-width: 1180px) {
  .sw-enquiry .sw-formgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
  .sw-enquiry .sw-formgrid { grid-template-columns: minmax(0, 1fr); }
}

/* The button should not stretch to the full width of a very wide form. */
.sw-enquiry .sw-submit { align-self: flex-start; }

/* ============================================================
   Log in — the two portals
   This page exists to send people to one of two places, so the two
   destinations ARE the page. The previous version led with a slanted violet
   statement band, which spent the whole top of the screen and the loudest
   colour on the words "Which are you?", and left the actual routing to two
   quiet white rectangles below it — the visual weight was the wrong way round.
   ============================================================ */
.sw-portals {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--band-gap, 20px);
}

.sw-portal {
  display: flex;
  flex-direction: column;
  min-height: 420px;
  padding: clamp(28px, 3vw, 44px);
  border-radius: var(--radius-2xl);
  color: var(--focus-black);
  transition: transform var(--duration-base) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out);
}

.sw-portal:hover { transform: translateY(-4px); box-shadow: var(--shadow-pop); }
.sw-portal:focus-visible { outline: 3px solid var(--focus-black); outline-offset: 3px; }

.sw-portal--staff { background: var(--accent-blue); }
.sw-portal--student { background: var(--accent-violet); }

.sw-portal__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px; height: 64px;
  border-radius: var(--radius-md);
  background: var(--simple-white);
}

.sw-portal__t {
  margin: 22px 0 0;
  font-size: clamp(30px, 3.4vw, 46px);
  line-height: 1.02;
  letter-spacing: -0.03em;
  font-weight: var(--weight-regular);
}

.sw-portal__d { margin: 12px 0 0; font-size: 18px; line-height: 1.45; max-width: 34ch; }

/* What is actually behind the door. On a signpost the useful thing is not
   "log in" but what you will find when you get there. */
.sw-portal__list {
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 16px;
}

.sw-portal__list li { padding-left: 22px; position: relative; }
.sw-portal__list li::before {
  content: '';
  position: absolute;
  left: 2px; top: 8px;
  width: 8px; height: 8px;
  border-radius: var(--radius-pill);
  background: var(--focus-black);
}

/* The action sits at the foot of the panel, so both cards' buttons line up
   however much description each carries. */
.sw-portal__go {
  margin-top: auto;
  padding-top: 26px;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: 18px;
  font-weight: var(--weight-semibold);
}

.sw-portal__go span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px; height: 46px;
  border-radius: var(--radius-pill);
  background: var(--focus-black);
  color: var(--simple-white);
  transition: transform var(--duration-base) var(--ease-out);
}

.sw-portal:hover .sw-portal__go span { transform: translateX(4px); }

@media (max-width: 1000px) {
  .sw-portals { grid-template-columns: minmax(0, 1fr); }
  .sw-portal { min-height: 0; }
}

/* ---- The log-in page as one split panel ----------------------------------
   Rebecca picked this from seven mock-ups, 14 October 2026. The page used to
   be a white band with a small heading and two tall cards, and it left a
   screen of empty white between the cards and the footer. This is the whole
   page: a photograph down one side, and down the other the heading, the two
   destinations as compact rows, and the help line. It fits one screen.

   A photograph on the LEFT is normally not allowed — see the note by
   .sw-bleedband, where a photograph-left variant was built and removed the
   same day because a bleeding panel and a two-column grid produce different
   copy columns, so a page alternating between them has text edges out of
   line. That argument is about a page of flowing bands. This band has no
   other content to line up with: it IS the page. So it is its own component
   rather than a mirrored .sw-bleedband, which is exactly what that note says
   to do if a photograph ever has to go left. */
.sw-loginsplit {
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  min-height: 640px;
  align-items: stretch;
}

/* The band's own padding is spent here, because the photograph has to reach
   the panel's edges. The rounded corners come from .sw-band's radius with
   overflow hidden on the section. */
.sw-loginsplit__wrap { max-width: none; padding: 0; }

.sw-loginsplit__media { position: relative; overflow: hidden; }

/* Same clearing as .sw-face and .sw-collage__tile: shape="rect" puts
   aspect-ratio 3/2 on the slot's own :host, which would beat the panel. */
.sw-loginsplit__media image-slot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  display: block;
}

.sw-loginsplit__body {
  padding: clamp(32px, 4vw, 64px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(18px, 2vw, 24px);
}

/* The row form of .sw-portal. The card form is 420px tall and stacks its
   parts; beside a photograph there is one column to play with, so the parts
   go across instead: icon, what it is, where it goes. */
.sw-portals--rows { grid-template-columns: minmax(0, 1fr); grid-auto-rows: 1fr; gap: 14px; }

.sw-portal--row {
  min-height: 0;
  padding: 22px 26px;
  border-radius: var(--radius-xl);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(16px, 1.8vw, 26px);
}

.sw-portal--row .sw-portal__icon { width: 54px; height: 54px; flex: none; }
.sw-portal--row .sw-portal__t { margin: 0; font-size: clamp(22px, 2.1vw, 27px); }
.sw-portal--row .sw-portal__d { margin: 4px 0 0; font-size: 16px; max-width: 38ch; }

/* NO .sw-portal__list in the row form. The card form lists what is behind each
   door, which is right when a card is 420px tall and has the room. Across a
   row there is about 380px for it beside the icon and the destination, and the
   two lists are different lengths, so one wrapped to three lines and the other
   to two and the rows came out 194px and 172px. The descriptions already say
   what each portal is. The cards keep their lists wherever they are used. */

.sw-portal--row .sw-portal__go { margin: 0; padding: 0; white-space: nowrap; }
.sw-portal--row .sw-portal__go span { width: 42px; height: 42px; }

@media (max-width: 1000px) {
  /* Stacked, the photograph leads. 16:10 rather than its own ratio so it never
     takes more than a third of a tablet screen on a page whose job is two
     links.

     align-items: start is REQUIRED, not tidiness. Stretched, the media panel's
     height comes from the grid row, and a height set by stretch alignment
     beats aspect-ratio — and the panel has no in-flow content, because the
     slot inside it is absolutely positioned. So the row collapsed and the
     photograph disappeared completely at 834px and 390px. */
  .sw-loginsplit { grid-template-columns: minmax(0, 1fr); min-height: 0; align-items: start; }
  .sw-loginsplit__media { aspect-ratio: 16 / 10; width: 100%; }
}

@media (max-width: 620px) {
  /* The destination drops below the description rather than squeezing a third
     column onto a phone. The icon STAYS beside the text: putting it on its own
     line was tried on 14 October 2026 and made each row 254px instead of 219px
     — the line the icon gains costs more than the wider text column saves. */
  .sw-portal--row { grid-template-columns: auto minmax(0, 1fr); row-gap: 14px; }
  .sw-portal--row .sw-portal__icon { width: 48px; height: 48px; }
  .sw-portal--row .sw-portal__go { grid-column: 1 / -1; }
}

/* The quote card must not change size as you cycle through testimonials — a
   card that grows and shrinks makes the whole band jump under the reader's
   cursor, and the violet module is a fixed shape in the layout.

   Only one quote is rendered at a time, so there is nothing to size against.
   The stage holds an invisible copy of the *longest* quote in the same grid
   cell as the live one: the card is then always as tall as the longest quote
   needs at that width, at every breakpoint, with no hard-coded height to go
   stale. The ghost is computed in renderVals, so adding a longer quote later
   sizes the card correctly on its own. */
.sw-quote-card__stage { display: grid; }
.sw-quote-card__stage > * { grid-area: 1 / 1; }
.sw-quote-card__ghost { visibility: hidden; }

.sw-quote-card__ghost,
.sw-quote-card__text {
  margin: 0;
  font-size: clamp(20px, 2.2vw, 30px);
  line-height: 1.4;
  letter-spacing: -0.01em;
  text-wrap: pretty;
}

/* The attribution shifts for the same reason — one centre's name is long
   enough to wrap onto an extra line. Same technique as the quote: an invisible
   copy of the longest name and centre sits in the same grid cell, so the block
   is always sized for the worst case at whatever width it is being read at.
   Both ghosts are computed in renderVals, so neither goes stale. */
.sw-quote-card__foot { align-items: center; }
.sw-quote-card__who { display: grid; min-width: 0; }
.sw-quote-card__who > * { grid-area: 1 / 1; align-self: center; min-width: 0; }
.sw-quote-card__who > .sw-quote-card__ghost { visibility: hidden; }

/* Course page hero: copy on the left, a photograph on the right.

   The media column is a sibling of the band's slanted fill, never a child of
   it, so nothing is skewed — same rule as every other slanted band. The band
   keeps its deep bottom padding so the photo clears the slant's lowest point
   rather than being cut by it. */
/* Every page hero is at least as tall as the About Sweet one, which is the
   shortest that still feels like a hero rather than a wide banner. The Pearson
   hero was 80px shorter, because its right-hand column is a co-brand lockup
   rather than a media box, and it read as squashed next to the others.

   --hero-min is the media box's own height, used in both places, so the floor and
   the thing that sets it cannot drift apart. Change one number and every hero
   follows. */
.sw-coursehero {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: clamp(28px, 3.6vw, 56px);
  align-items: center;
  min-height: var(--hero-min);
}

/* Restored 14 August 2026 after an over-wide deletion removed this whole span.
   Recovered from the browser's CSSOM, so the original explanatory comments in
   this section are gone — the rules themselves are byte-identical. */

.sw-feature {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: clamp(24px, 3vw, 44px);
  align-items: center;
  padding: clamp(20px, 2.2vw, 28px);
  border-radius: var(--radius-2xl);
  /* The gradient sits on the CARD, and the tile inside it stays white. The other
     way round put the colour on the inner box, which made the tile the loudest
     thing on the page and left the card itself as a beige surround — a frame
     around a picture rather than one featured post. Contrast on this gradient is
     4.98–5.24:1 for `--text-secondary`, clear of AA across the whole ramp. */
  background: var(--gradient-curious-blue);
  /* The card now carries a category colour, and Spark Lime on the white page
     is 1.12:1 — a card with no edge. The shadow defines it whatever colour the
     category gives it, the same answer the shop's product cards use. */
  box-shadow: var(--shadow-soft);
  transition: transform var(--duration-base) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out);
}

.sw-feature:hover { transform: translateY(-3px); box-shadow: var(--shadow-pop); }

.sw-feature__media {
  height: clamp(240px, 30vw, 380px);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--simple-white);
  min-width: 0;
}

.sw-badgepair {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(6px, 1.2vw, 16px);
  padding: clamp(10px, 1.6vw, 20px);
  background: var(--simple-white);
}

/* The badges are square, so in a landscape box it is the row width that limits
   them, never the height. `flex: 1 1 0` gives each exactly half the row and
   `contain` keeps them round. */
.sw-badgepair img { flex: 1 1 0; min-width: 0; height: 100%; object-fit: contain; }

.sw-feature__body { display: flex; flex-direction: column; gap: 14px; min-width: 0; }

.sw-feature__t {
  margin: 0;
  font-size: clamp(26px, 3vw, 42px);
  line-height: 1.06;
  letter-spacing: -0.03em;
  font-weight: var(--weight-regular);
  text-wrap: balance;
}

.sw-feature__flag {
  align-self: flex-start;
  padding: 5px 14px;
  border-radius: var(--radius-pill);
  background: var(--spark-lime);
  font-size: var(--text-small);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.sw-post {
  display: flex;
  flex-direction: column;
  height: 100%;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--paper-ivory);
  transition: transform var(--duration-base) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out);
}

.sw-post:hover { transform: translateY(-3px); box-shadow: var(--shadow-pop); }
/* Post cards carry their CATEGORY as colour --------------------------------
   Every card was Paper Ivory, which made the grid below the featured post read
   as one flat block — the feature had the Curious Blue gradient and everything
   under it was beige.

   Keyed off `data-category`, the same attribute the filter chips are built
   from, so the colour is not decoration: filter to Wave 3 and the violet cards
   are the Wave 3 cards. Same principle as the shop, where card colour carries
   the course and a position-based rotation was tried first and dropped for
   being only decoration.

   Two of the four already mean something elsewhere on the site and keep that
   meaning here: Wave 3 is Future Violet, which is the Wave 3 course colour, and
   Awards is Spark Lime, which is what the finalist badges and the "Latest post"
   flag sit on.

   An unlisted category falls through to Paper Ivory rather than to nothing, so
   a new post without a colour is still a finished card.

   Body copy goes to FULL Focus Black on all of them. These are the mid-tones
   `.sw-on-mid` exists for — 66% black on Future Violet is 3.6:1, under AA — and
   the meta line is the smallest text on the card, so it is the first thing to
   fail.

   THE FEATURE CARD TAKES THE SAME COLOURS since 15 October 2026. It used to
   keep the Curious Blue gradient, and that was the page's biggest design
   problem: a pale gradient card holding a WHITE tile on a WHITE page, three
   near-white layers, so the one post the page most wants clicked had the least
   presence on it. It is also now coloured by the same attribute as everything
   else, so filtering to Awards leaves a lime feature above lime cards.
   -------------------------------------------------------------------- */
/* Wave 3 is NAVY, not Future Violet (Rebecca, 15 October 2026). It is the only
   one of the four that is a DARK surface rather than a mid-tone, so it does not
   take the Focus Black body copy the other three do — its block is below. */
.sw-post[data-category="Wave 3"]         { background: var(--surface-inverse); }
.sw-post[data-category="Awards"]         { background: var(--spark-lime); }
.sw-post[data-category="Events"]         { background: var(--accent-green); }
.sw-post[data-category="Back to school"] { background: var(--accent-blue); }

.sw-feature[data-category="Wave 3"]         { background: var(--surface-inverse); }
.sw-feature[data-category="Awards"]         { background: var(--spark-lime); }
.sw-feature[data-category="Events"]         { background: var(--accent-green); }
.sw-feature[data-category="Back to school"] { background: var(--accent-blue); }

/* Mid-tones, so body copy goes to full Focus Black exactly as it does on the
   post cards. */
.sw-feature[data-category]:not([data-category="Wave 3"]) .sw-lede,
.sw-feature[data-category]:not([data-category="Wave 3"]) .sw-meta,
.sw-feature[data-category]:not([data-category="Wave 3"]) .sw-post__go { color: var(--focus-black); }

/* The dark one. The card carries the colour so the read-link inherits it, but
   THE TITLES HAVE TO BE NAMED. The design system's base.css sets
   `h1, h2, h3, h4, h5, h6 { color: var(--text-primary) }`, and a rule that
   matches an element directly always beats a colour inherited from its parent
   however specific that parent's rule is. Measured without this: the card's
   own colour computed as white, the body wrapper's computed as white, and the
   h3 between them came out rgb(11,30,47) on rgb(11,30,47) — 1.0:1. */
.sw-post[data-category="Wave 3"],
.sw-feature[data-category="Wave 3"] { color: var(--text-on-dark); }

.sw-post[data-category="Wave 3"] .sw-post__t,
.sw-feature[data-category="Wave 3"] .sw-feature__t { color: var(--text-on-dark); }

.sw-post[data-category="Wave 3"] .sw-body,
.sw-feature[data-category="Wave 3"] .sw-lede { color: rgba(255, 255, 255, 0.82); }

.sw-post[data-category="Wave 3"] .sw-meta,
.sw-feature[data-category="Wave 3"] .sw-meta { color: rgba(255, 255, 255, 0.72); }

/* THE FLAG WENT NAVY THIS MORNING, so on a navy Wave 3 feature it would be the
   card — the third time today the same fault has turned up in a different
   place. Spark Lime on navy instead: 15.1:1. */
.sw-feature[data-category="Wave 3"] .sw-feature__flag {
  background: var(--spark-lime);
  color: var(--focus-black);
}

/* THE FLAG HAD TO CHANGE WITH THE CARD. .sw-feature__flag is Spark Lime, and
   the current featured post is an Awards post — so it would have been lime on
   lime, invisible, the same fault as the four buttons fixed yesterday. Navy
   works on all four category colours and on the old gradient: 15.1:1 on lime,
   and no worse than 8.6:1 on any of them. */
.sw-feature[data-category] .sw-feature__flag {
  background: var(--focus-black);
  color: var(--text-on-dark);
}

.sw-post[data-category="Awards"] .sw-body,
.sw-post[data-category="Awards"] .sw-meta,
.sw-post[data-category="Awards"] .sw-post__go,
.sw-post[data-category="Events"] .sw-body,
.sw-post[data-category="Events"] .sw-meta,
.sw-post[data-category="Events"] .sw-post__go,
.sw-post[data-category="Back to school"] .sw-body,
.sw-post[data-category="Back to school"] .sw-meta,
.sw-post[data-category="Back to school"] .sw-post__go { color: var(--focus-black); }

.sw-post__media { aspect-ratio: 3 / 2; background: var(--simple-white); }

/* THE SLOT HAS TO FILL THE TILE, and this is not tidiness. shape="rect" puts
   `aspect-ratio: 3/2` on the image-slot's own :host, which resolves against the
   slot's width — narrower than the tile, because .sw-tile is inset by
   --tile-inset on each side. Measured: a 399x284 tile holding a 399x266 slot,
   so 19px of the tile's background showed under every photograph.

   That strip is why three post tiles carried a hardcoded background colour. I
   first read those as dead markup, because a bounding-box test said the photo
   covered its tile; it did not, and removing them exposed a white band under
   every card. They were masking this, and on the "Back to school" card they
   masked it in GREEN on a blue card, which is how it was noticed at all.

   Letting the SLOT own the ratio was the first attempt and it was wrong: it
   closed the gap but left the award badge tile, which holds two plain <img>s
   and no slot, 284px beside photo tiles of 266 — a row of cards with
   mismatched pictures, which is what Rebecca saw next.

   So the TILE owns the ratio and the slot fills it absolutely. Two things make
   that work:

   min-height: 0, because .sw-post__media is a flex item of a column flex
   container, so its min-height: auto is a content floor — the badge images'
   natural height pushed the tile from 284 to 319 on the article pages whatever
   the aspect-ratio said.

   inset: 0 on the slot rather than `height: 100%`, which did nothing at all: a
   percentage height against a parent sized by aspect-ratio resolves as auto,
   so it fell back to the img's own ratio and stayed short.

   Measured after: every tile 284px, 0px gap, 0% distortion. */
/* TRANSPARENT, not white, and this is the white line Rebecca reported three
   times. The photo overflows this tile on both sides and meets it exactly top
   and bottom, so geometrically there is no gap — and at deviceScaleFactor 2
   and 3 a scan of 16,000 pixels around every edge found not one near-white
   pixel, which is why it was wrongly called cached twice.

   At deviceScaleFactor 1 it is there: 381 near-white pixels in the outermost
   2px ring. A tile clipped by a border-radius against a child that overflows
   it antialiases its edge, and on a 1x display that half-covered pixel blends
   the child with whatever is BEHIND it — which was --simple-white. On a 1.25x
   or denser screen the same edge lands on a whole device pixel and nothing
   shows, so it is invisible to anyone on a Retina display.

   With no background behind it, that blend happens against the card's own
   category colour instead, where it cannot read as a line. The award badge
   pair keeps its white, which it sets itself — those are logos that need a
   white ground, not photographs. */
.sw-post__media { min-height: 0; position: relative; background: transparent; }

/* ...EXCEPT the award badge pair, which has no photograph and needs a tile of
   its own. .sw-badgepair sets --simple-white, but that is (0,1,0) and so is
   .sw-post__media, an exact tie the later rule wins — and the transparent rule
   above is later. So the badges lost their white ground while every photograph
   kept its tile, and the card read as two logos floating on the card colour
   with no tile at all. Doubled selector, the same trap as
   .sw-collage__tile--mark and .sw-shop-grid--three. */
.sw-post__media.sw-badgepair { background: var(--simple-white); }

.sw-post__media image-slot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  display: block;
}
.sw-post__body { display: flex; flex-direction: column; gap: 10px; padding: 22px; flex: 1; }
.sw-post__t { margin: 0; font-size: 21px; line-height: 1.2; letter-spacing: -0.02em; }
.sw-post__go { margin-top: auto; padding-top: 14px; font-size: 16px; font-weight: var(--weight-medium); }

@media (max-width: 1000px) {
  .sw-feature { grid-template-columns: minmax(0, 1fr); }
  .sw-feature__media { height: clamp(200px, 44vw, 300px); }
}

/* Blog category filter. Chips are only written in once the script runs, so
   without JS the page is the full unfiltered list. `.is-filtered-out` rather than
   the `hidden` attribute — `[hidden]` is a user-agent rule and loses to
   `.sw-post { display: flex }`. */
/* Right-aligned (Rebecca, 15 October 2026). The count used to be pushed to the
   far edge with margin-left: auto, which left it 762px from the chips it
   describes on a 1,328px row — the label-hard-left, value-hard-right fault that
   came off the shop's help bar for the same reason. `order` rather than
   row-reverse: the DOM order stays chips-then-count for a screen reader, and
   only the painting order changes. */
.sw-blogfilter { display: none; align-items: center; justify-content: flex-end; gap: 14px; flex-wrap: wrap; }
.sw-blogfilter.is-ready { display: flex; }
.sw-blogfilter__chips { display: flex; gap: 8px; flex-wrap: wrap; }
.sw-blogfilter__count { order: -1; font-size: 15px; color: var(--text-muted); }
.is-filtered-out { display: none !important; }

@media (max-width: 720px) {
  /* Back to the left on a phone, where a right-aligned row of wrapped chips
     reads as ragged rather than aligned. */
  .sw-blogfilter { justify-content: flex-start; }
  .sw-blogfilter__count { order: 0; }
}

/* FLEX, NOT GRID, and the reason is the last row. As `repeat(auto-fit,
   minmax(300px, 1fr))` the grid packed as many cards as would fit and then left
   the remainder stranded at one track's width with the rest of the row empty.
   Measured before the change, with the posts cloned to test each count:

                1440px        1100px        860px
     3 posts    3             3             2 + 1   <- already happening
     4 posts    4             3 + 1         2 + 2
     5 posts    4 + 1         3 + 2         2 + 2 + 1
     7 posts    4 + 3         3 + 3 + 1     2 + 2 + 2 + 1

   So this was not a problem waiting for a fifth post: three posts already
   stranded one at 860px.

   `flex: 1 1 300px` reproduces what auto-fit was doing — as many cards as fit
   at 300px, each growing to share the row — and then does the thing a grid
   cannot: a short last row grows its cards to fill, up to the 640px cap, and
   justify-content centres whatever is left over. Full rows are unaffected,
   because they have no free space to distribute.

   The 640px cap is still for the one-result case: without it a single filtered
   post blew up to the full band, twice the size of the same card a click
   earlier. min-width: 0 because these cards hold an image-slot — the same
   floor that made the shop grid render four-up as two. */
.sw-postgrid {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  justify-content: center;
}

.sw-postgrid > * { flex: 1 1 300px; min-width: 0; max-width: 640px; }

/* ---- Tiles side by side are the same height ------------------------------
   Rebecca, 15 October 2026: "can you make a rule that all tiles on the site
   that are side by side are the same height". It is one line of CSS and one
   piece of knowledge.

   A row stretches its items to match each other ONLY while none of them
   declares a height of its own. `height: 100%` on a card is harmless in a GRID
   and defeats the stretch in a FLEX row — which is exactly how the blog cards
   came out 499, 521 and 525px after .sw-postgrid changed from grid to flex
   earlier today, and what Rebecca reported as a bug on the homepage. Setting
   align-items on the CONTAINER does not help; the declaration has to come off
   the CHILD. Tested all four candidate fixes; only this one works.

   Surveyed all 35 pages at 1600px for rows of side-by-side cards differing by
   more than 2px.

   ONE ROW IS DELIBERATELY EXEMPT and must stay that way: .sw-facegrid in the
   Contact hero measures 255px beside three 122px tiles, because its first tile
   spans 2x2. That unevenness is the mosaic, not a fault. */
.sw-postgrid > .sw-post { height: auto; }

.sw-shop-grid > *,
.sw-postgrid > *,
.sw-grid-2 > *,
.sw-grid-3 > *,
.sw-grid-4 > *,
.sw-topics > * { align-self: stretch; }

/* Small label pills carry the brand's hard offset shadow, so they read as the
   same family as the Highlight Tags rather than as flat chips. Interactive pills
   are excluded on purpose — they already signal "press me" through stroke and
   hover, and a shadow on top is a second, conflicting affordance. */
.sw-feature__flag,
.sw-unit__n,
.sw-product__flag { box-shadow: var(--shadow-tag-sm); }

.sw-carousel { position: relative; }

.sw-carousel__track {
  display: flex;
  gap: 20px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  margin-inline: calc(-1 * (var(--band-pad-x) + var(--page-inset)));
  padding-inline: calc(var(--band-pad-x) + var(--page-inset));
  padding-bottom: 4px;
  scrollbar-width: none;
}

.sw-carousel__track::-webkit-scrollbar { display: none; }

@media (prefers-reduced-motion: reduce) {
  .sw-carousel__track { scroll-behavior: auto; }
}

.sw-carousel__slide {
  --slide-pad: clamp(24px, 2.6vw, 40px);
  flex: 0 0 min(78%, 860px);
  scroll-snap-align: start;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
  gap: clamp(20px, 2.6vw, 40px);
  align-items: stretch;
  padding: var(--slide-pad);
  border-radius: var(--radius-2xl);
  overflow: hidden;
  background: var(--paper-ivory);
}

.sw-carousel__body { display: flex; flex-direction: column; justify-content: center; gap: 12px; min-width: 0; }

.sw-carousel__t {
  margin: 0;
  font-size: clamp(24px, 2.6vw, 36px);
  line-height: 1.08;
  letter-spacing: -0.03em;
  font-weight: var(--weight-regular);
}

/* The media bleeds to the slide's top, right and bottom edges rather than
   floating inside its padding — a rounded box inside a rounded box is the
   "bubble in a box" the radius rules exist to prevent, and it made every slide
   read as a card containing a card.

   Negative margins of exactly the slide's own padding, which is why that padding
   is now a variable rather than repeated. The slide clips, so the photograph
   picks up its rounded corners on the right and stays square where it meets the
   copy. `align-items: stretch` on the slide is what lets it fill the height; the
   body column centres itself instead. */
.sw-carousel__media {
  /* Taller than the old fixed 300px. Bleeding the media removed the slide's own
     padding from the height, so the slide lost about 80px the moment the photo
     went edge to edge — this puts it back and then some. */
  min-height: clamp(260px, 31vw, 430px);
  margin: calc(-1 * var(--slide-pad)) calc(-1 * var(--slide-pad)) calc(-1 * var(--slide-pad)) 0;
  border-radius: 0 var(--radius-2xl) var(--radius-2xl) 0;
  overflow: hidden;
  background: var(--simple-white);
  min-width: 0;
}

/* A carousel step whose media is a CUT-OUT rather than a photograph. Three of
   PGW's four steps are `fit="cover"` photographs that fill the box, so the
   media background never shows; step 3 is the set of books on `fit="contain"`,
   floating on white with white all round it.

   Focus Black behind it, which is what the course heroes already do with the
   same books (.sw-herophoto--stage) — an object reads as an object on a dark
   ground and as a cut-out that forgot its background on a white one.

   A modifier rather than a change to .sw-carousel__media, because WorkSkills
   has the same situation at ws-step-2 and has not been asked for. */
.sw-carousel__media--dark { background: var(--focus-black); }

/* The school-crest stage was Focus Black too. With "Who we are" now navy a
   little further up the page, two navy blocks in one scroll stop reading as two
   sections and start reading as one device used twice. Future Violet keeps it a
   brand surface and keeps the white crest tile reading as a tile, and it leaves
   navy doing exactly one job on this page.

   Scoped to the stage, not to --dark: the PGW page uses --dark for its tag
   panel, where lime, blue and violet tags genuinely need a dark ground. */
.sw-carousel__media--dark.sw-logostage { background: var(--accent-violet); }

/* A cluster of Highlight Tags instead of a photograph, for the carousel step that
   asks people to follow Sweet. Focus Black, not the course colour: the tags are
   lime, blue and violet and each needs a dark ground. */
.sw-tagpanel {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  place-content: center;
  gap: 20px 16px;
  height: 100%;
  padding: 24px;
  background: var(--focus-black);
}

/* Dots and arrows centred as one group under the slides, rather than the dots
   left and the arrows pushed to the right edge. Split apart they read as two
   unrelated controls at opposite ends of a very wide band; together they read as
   one control for the thing above them. */
.sw-carousel__nav { display: flex; align-items: center; justify-content: center; gap: 18px; margin-top: 24px; }
.sw-carousel__dots { display: flex; gap: 8px; }

.sw-carousel__dot {
  width: 9px;
  height: 9px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: rgba(11, 30, 47, 0.22);
  cursor: pointer;
  transition: width var(--duration-base) var(--ease-out),
              background var(--duration-base) var(--ease-out);
}

.sw-carousel__dot.is-on { width: 26px; background: var(--focus-black); }
.sw-carousel__arrows { display: flex; gap: 8px; }

.sw-carousel__arrow {
  width: 46px;
  height: 46px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: var(--border-width) solid var(--focus-black);
  border-radius: var(--radius-pill);
  background: transparent;
  font: inherit;
  font-size: 18px;
  cursor: pointer;
  transition: background var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out);
}

.sw-carousel__arrow:hover:not(:disabled) { background: var(--focus-black); color: var(--simple-white); }
.sw-carousel__arrow:disabled { opacity: 0.3; cursor: default; }

@media (max-width: 1000px) {
  .sw-carousel__slide { grid-template-columns: minmax(0, 1fr); flex-basis: min(88%, 560px); }
}

/* On a phone the 88% peek costs more than it earns. Measured on About Sweet's
   history carousel at 390px: a 287px slide, less 24px of padding each side,
   left 239px for the copy — about 25 characters a line, narrower than a
   newspaper column, in a card you also have to swipe. The peek is what
   normally tells you there is another card, but this carousel already has dots
   AND arrows directly beneath it, so the affordance is not load-bearing here.

   Full width plus tighter padding takes the copy to roughly 36 characters,
   which is where the rest of the page sits on a phone (median 34).

   NOT stacked into three blocks, which was the first idea: the three slides
   carry media as well as copy, so unrolling them would have added around 400px
   each to a view that is already the longest on the site. Widening the card
   fixes the reading without that. */
@media (max-width: 560px) {
  .sw-carousel__slide {
    --slide-pad: 20px;
    flex-basis: 100%;
  }
  /* Stacked, it bleeds across the top instead of down the right. */
  .sw-carousel__media {
    height: clamp(180px, 40vw, 240px);
    min-height: 0;
    order: -1;
    margin: calc(-1 * var(--slide-pad)) calc(-1 * var(--slide-pad)) 0;
    border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
  }
}

/* Announced to screen readers, invisible on screen. Used for the basket's
   "X removed" message: removing a line is a silent DOM change otherwise. */
.sw-visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip-path: inset(50%);
}

.sw-basket__empty { display: none; }
.sw-basket__empty.is-shown { display: block; }
.sw-summary.is-hidden { display: none; }

.sw-unit--ws .sw-unit__n { background: var(--accent-green); }
/* BOTH UNUSED as of 11 October 2026, and kept for the same reason as
   .sw-infotile--draft: they still work. PGW's units band went flush on
   25 September and WorkSkills' followed today, so the two course pages now
   stack their units straight on the page ground. Write either class on a band
   and it gets its gradient back. */
.sw-units-band--pgw { background: var(--gradient-curious-blue); }
.sw-units-band--ws { background: var(--gradient-progress-green); }

.sw-coursehero .sw-cobrand { --cobrand-h: clamp(34px, 4.4vw, 62px); }

.sw-teamgroup { margin: 0; display: flex; }

/* A post sits on one white sheet on a Paper Ivory ground. Everything shares one
   980px column: the inline padding grows to meet the column instead of the column
   centring inside a fixed padding — otherwise a wide screen shows three left edges
   (sheet, padding, content) instead of two. */
.sw-postsheet {
  background: var(--simple-white);
  border-radius: var(--radius-2xl);
  padding-block: clamp(30px, 4.5vw, 68px);
  padding-inline: max(clamp(20px, 3.5vw, 40px), (100% - 980px) / 2);
}

.sw-postsheet > * { max-width: 980px; margin-inline: auto; }
.sw-postsheet > * + * { margin-top: 44px; }

/* A flex row ignores the wrapper's `text-align: center` — it needs
   `justify-content`. This was silently left-aligned inside a centred post head. */
.sw-postsheet .sw-crumb { justify-content: center; }

/* Encode a photo at the aspect ratio of the box it goes in. A fixed pixel height
   came out near 16:9, `cover` trimmed a 4:3 image top and bottom, and heads were
   cut off — and the trim moved with the viewport. */
.sw-postmedia { height: auto; aspect-ratio: 16 / 9; }

.sw-post-head { max-width: 900px; margin-inline: auto; text-align: center; }

/* ONE rule block, not two. It had two: the first set `margin-inline: auto`, the
   second used the `margin` shorthand, which reset left and right to 0 — so the
   24ch box sat flush left and its centred text landed 23px left of everything
   else. Never give one selector two rule blocks. */
.sw-post-head__t {
  max-width: 24ch;
  margin: 8px auto 0;
  font-size: clamp(30px, 4vw, 56px);
  line-height: 1.06;
  letter-spacing: -0.03em;
  font-weight: var(--weight-regular);
  text-wrap: balance;
}

.sw-post-head__lede {
  margin: 14px auto 0;
  max-width: 54ch;
  font-size: 18px;
  line-height: 1.5;
  color: var(--text-secondary);
  text-wrap: balance;
}

.sw-post-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 10px 16px;
  margin-top: 16px;
  font-size: 15px;
  color: var(--text-secondary);
}

/* Do not widen this measure. Line length is the biggest single lever on reading
   comfort — narrow what sits around it instead. */
.sw-article { max-width: 68ch; margin-inline: auto; }
.sw-article > * + * { margin-top: 1.1em; }
.sw-article p { font-size: 18px; line-height: var(--lh-relaxed); color: var(--text-secondary); }

.sw-article h2 {
  margin: 0;
  padding-top: 1.2em;
  font-size: clamp(22px, 2.2vw, 30px);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}

.sw-article ul { margin: 0; padding-left: 1.25em; display: flex; flex-direction: column; gap: 8px; }
.sw-article li { font-size: 18px; line-height: 1.5; color: var(--text-secondary); }
.sw-article strong { color: var(--text-primary); font-weight: var(--weight-semibold); }

/* The Meet the Team hero is a pill headline and nothing else — no lede, no media
   column. Now every hero is floored at the same height, three small pills left
   most of a 480px violet band empty. Scaled up they fill the band they are in,
   which is the whole job of a hero with no other content. */
.sw-pill-head--md .sw-tag { font-size: clamp(40px, 7.6vw, 104px); }

/* THE RULE: content follows a slanted band closely. A slanted-bottom band above a
   straight one already reads as separated — the diagonal does that job — so the
   usual band gap on top of it opens a hole, and it is worse than the number
   suggests. The band's BOX ends at the deepest point of its diagonal, but the
   colour on the shallow side stops a full slant rise higher (about 59px at
   1408px), so whatever gap you set is that much bigger again across most of the
   width.

   This rule existed before and did nothing, because every page carried an inline
   `padding-top` on the section below the hero and inline always wins — measured
   96px, 150px and 76px against the 28px set here. Those inline values are gone.
   Do not reintroduce one: if a section under a hero needs different spacing,
   change it here so every page moves together. */
.sw-band--slant-bottom + .sw-band { margin-top: calc(var(--band-gap) - 44px); }
.sw-band--slant-bottom + .sw-band > .sw-band__inner { padding-top: 20px; }

/* ...unless the band under the hero has a SURFACE OF ITS OWN.
   The 20px above is right when the next band is white on a white page: there is
   no edge there, the diagonal IS the separation, and a full band gap on top of
   it opens a hole. It is wrong the moment that band is coloured, because then it
   has a real rounded top edge and 20px puts the content hard against it —
   Contact's form band read 20px top against 72px bottom, visibly lopsided.

   Put `.sw-band--own-top` on any coloured or gradient band that follows a
   slanted hero and it keeps whichever padding its --pad-* class gives it. Same
   (0,3,0) specificity as the rule above, later in the file, so it wins. */
.sw-band--slant-bottom + .sw-band--own-top > .sw-band__inner {
  padding-top: calc(var(--band-pad-top) * var(--band-pad-k));
}

.sw-awardbanner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(20px, 4vw, 56px);
  margin-top: 30px;
  padding: clamp(28px, 4vw, 56px);
  border-radius: var(--radius-xl);
  background: var(--paper-ivory);
}

.sw-awardbanner img { height: clamp(130px, 17vw, 220px); width: auto; }

/* Eight winners: four across, then two — never three. An odd track count leaves
   a ragged last row, invisible as flat text chips and obvious once each carried a
   photograph. */
.sw-winners { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }

.sw-winner { min-width: 0; border-radius: var(--radius-lg); overflow: hidden; background: var(--paper-ivory); margin: 0; }
.sw-winner__photo { aspect-ratio: 4 / 3; background: var(--paper-ivory); }

/* The two who were not there show their own crest on Focus Black. On white the
   crest tiles read as gaps in a grid of full-bleed photographs, as though the
   images had failed to load. */
.sw-winner__photo--crest { display: grid; place-items: center; padding: 20px; background: var(--focus-black); }
.sw-winner__photo--crest img { height: 100%; width: 76%; object-fit: contain; border-radius: var(--radius-md); }

.sw-winner__body { padding: 18px 20px 20px; }

.sw-winner__cat {
  font-size: var(--text-small);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--violet-2);
}

.sw-winner__name { margin-top: 8px; font-size: 19px; line-height: 1.2; letter-spacing: -0.02em; font-weight: var(--weight-semibold); }

@media (max-width: 1000px) {
  .sw-winners { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
  .sw-winner__photo { height: clamp(120px, 30vw, 170px); }
  .sw-winner__body { padding: 14px 16px 16px; }
  .sw-winner__name { font-size: 17px; }
}

/* Three photographs of equal standing, not one-large-plus-two — one photograph
   has to be either the room, the talk or the people, and three tells the day.
   Fixed 4/3 matching how the files are encoded, so `cover` never re-crops. */
.sw-triptych { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }

.sw-triptych__item {
  aspect-ratio: 4 / 3;
  min-width: 0;
  border-radius: var(--radius-xl);
  overflow: hidden;
  background: var(--paper-ivory);
}

@media (max-width: 720px) {
  .sw-triptych { grid-template-columns: minmax(0, 1fr); }
}

.sw-post-meta__author { color: var(--text-primary); font-weight: var(--weight-medium); }

/* Three question marks behind a "get in touch" band. `aria-hidden` on the wrapper
   and empty alts throughout — these say nothing the heading does not already say. */
.sw-qmarks { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
/* Rotation is applied HERE, from the per-child --qm-rot above, rather than in
   each nth-child rule. A transform is one property: writing `rotate()` per
   child and `scale()` in the animation means the second overwrites the first
   and all three marks snap upright the moment they animate. One rule holding
   both is the only way they compose. */
.sw-qmarks img {
  position: absolute;
  width: auto;
  opacity: 0.9;
  transform: rotate(var(--qm-rot, 0deg));
}

/* Sized in vw with a clamp rather than fixed pixels: a height that sits
   comfortably in a 1400px band starts crowding the copy by about 1100px. */
.sw-qmarks {
  --q1: clamp(78px, 9.5vw, 132px);
  --q2: clamp(112px, 13.5vw, 188px);
  --q3: clamp(158px, 19vw, 266px);
}

/* `right` places an element's RIGHT EDGE, so with three different sizes equal
   spacing is not equal `right` values. The violet's midpoint is written as a calc
   off the shared size variables so it stays centred at every width. */
.sw-qmarks img:nth-child(1) { height: var(--q1); right: 34%; top: 12%; --qm-rot: -14deg; }
.sw-qmarks img:nth-child(2) {
  height: var(--q2);
  right: calc(17.5% + (var(--q1) + var(--q3)) / 4 - var(--q2) / 2);
  top: 42%;
  --qm-rot: 10deg;
}
/* top: 6%, not 3%. Rotation grows the painted box beyond the element's own height
   — about 14px on the largest — so 3% put its topmost point above the band and
   `overflow: hidden` shaved it. */
.sw-qmarks img:nth-child(3) { height: var(--q3); right: 1%; top: 6%; --qm-rot: -6deg; }

/* The copy column widens as the band narrows, so below the two-column breakpoint
   the marks would sit behind the text. They go entirely rather than shrink. */
@media (max-width: 1000px) {
  .sw-qmarks { display: none; }
}

/* "Got more questions?" at the foot of a common-questions band, in place of the
   separate get-in-touch band the course pages used to end on.

   That band was a whole section — heading, lede, buttons, decoration — repeating
   an invitation the FAQ has already earned by the time you reach the bottom of
   it. One line and one button says the same thing in a tenth of the page, and it
   sits where the question actually occurs to someone: at the end of the answers.

   A row on one line, wrapping to two only when the copy and the button will not
   both fit. */
.sw-faqcta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 14px 22px;
  margin-top: 34px;
  text-align: center;
}

/* The same marks on a common-questions band. They cannot simply be laid over it:
   an FAQ fills its full width with two columns of text, and a 266px mark behind an
   answer is interference, not decoration. So they are scoped to the band's head,
   which reserves height on the right for them at full size. */
.sw-faqhead { position: relative; }

@media (min-width: 1001px) {
  .sw-faqhead { display: flex; align-items: center; min-height: clamp(210px, 22vw, 310px); }

  /* The min-height above exists ONLY to reserve room for .sw-qmarks--head at
     full size. A centred head has no marks (see the homepage FAQ markup), so
     the reservation would be 210-310px of empty band. Dropped with them. */
  .sw-faqhead--centred { display: block; min-height: 0; }
}

/* Re-set for this variant: the CTA percentages are measured against a whole band,
   and against a head a third of that height the lower marks fall out of the
   bottom. Each also clears the rotation overhang. */
/* The three marks pop in one at a time when the head scrolls into view.

   Driven by the page's own [data-reveal] observer — the container carries the
   attribute and the children animate off its .is-revealed, so this needs no
   new JavaScript and fires in step with every other reveal on the site.

   The container itself must NOT fade: [data-reveal] would otherwise give it
   opacity 0 and a 20px lift, and the marks would arrive as one block, which is
   the opposite of the point. It is reset to visible and the stagger lives
   entirely on the children.

   140ms apart, smallest first, so the eye is led from the little mark up to
   the big one. Back-out easing gives each a slight overshoot — they land
   rather than slide. 0.9 is the resting opacity set above, not 1.

   Falls back correctly: with no JS, no .is-revealed is ever added, so the
   marks would stay hidden — which is why the no-preference guard below is a
   media query on the ANIMATION rather than on the resting state. */
@media (prefers-reduced-motion: no-preference) {
  .sw-qmarks--head[data-reveal] { opacity: 1; transform: none; transition: none; }
  .sw-qmarks--head[data-reveal] img {
    opacity: 0;
    transform: rotate(var(--qm-rot, 0deg)) scale(0.3);
    transition: opacity 240ms var(--ease-out), transform 460ms var(--ease-drop);
  }
  .sw-qmarks--head[data-reveal].is-revealed img {
    opacity: 0.9;
    transform: rotate(var(--qm-rot, 0deg)) scale(1);
  }
  .sw-qmarks--head[data-reveal].is-revealed img:nth-child(1) { transition-delay: 0ms; }
  .sw-qmarks--head[data-reveal].is-revealed img:nth-child(2) { transition-delay: 140ms; }
  .sw-qmarks--head[data-reveal].is-revealed img:nth-child(3) { transition-delay: 280ms; }
}

.sw-qmarks--head img:nth-child(1) { top: 7%; }
.sw-qmarks--head img:nth-child(2) { top: 30%; }
.sw-qmarks--head img:nth-child(3) { top: 6%; }

/* A centred stack, for a CTA that has no second column to balance it. */
.sw-stack--centred { align-items: center; text-align: center; margin-inline: auto; }

/* Pearson is the only page still using this grid — copy left, the co-brand lockup
   right. The course pages moved to the About hero pattern, so `.sw-coursehero__media`
   and its cut-out stage went with them. */
@media (max-width: 1000px) {
  .sw-coursehero { grid-template-columns: minmax(0, 1fr); }
}

.sw-coursehero--even { grid-template-columns: minmax(0, 3fr) minmax(0, 1fr); }

@media (max-width: 1000px) {

}

@media (max-width: 1000px) {
}

/* An illustration on a colour, in place of a photograph. Used on the Pearson
   "fully managed service" cards.

   A direct <img>, not an image-slot: these are SVGs, so they stay crisp at any
   size and out of the state file, which is already several megabytes of base64
   and loads in full before any page paints.

   Fixed aspect rather than a pixel height, so the three cards line up whatever
   the row does, and generous padding because an illustration needs air around it
   in a way a full-bleed photograph does not. */
.sw-illus-tile {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  padding: clamp(12px, 1.4vw, 18px);
}

/* Sized well inside the tile rather than filling it — at full size they read as
   the tile itself; held back they read as an icon sitting on a colour, which is
   what they are.

   Sized by WIDTH, not height. A percentage height on a grid item resolves against
   the grid area, and where that area is auto-sized the percentage is treated as
   auto — so `height: 62%` silently fell back to the SVG's intrinsic 150px, which
   is both the wrong size and, being taller than the slack allowed, pushed the
   icon below centre and 3px out of the bottom of the tile. Width percentages
   always resolve. `max-height` is the guard, so the icon can never overflow even
   if a future illustration is portrait. */
/* SUPERSEDED 13 October 2026 (Rebecca: "increase the image used for step 4 so
   it almost fills tile"). The note above describes why these were held to 46%
   — an icon sitting on a colour rather than the tile itself — and that was a
   fair call while the tile was Focus Black. It is Progress Green now and she
   wants the artwork to carry it.

   100% both ways with object-fit: contain, so the illustration grows to
   whichever edge it meets first and keeps its 2850x3000 ratio. Measured: 29%
   of the tile's area before, about 80% after. The padding stays, so it still
   does not touch the corners.

   .sw-illus-tile has exactly one user on the site — WorkSkills step 4 — so
   this is changed in place rather than behind a modifier. Add one the moment
   a second tile wants the old restraint. */
.sw-illus-tile img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Optical balance, per illustration.

   Step one was to trim each SVG's artboard to its own content — the certificate
   in particular carried 26% empty margin baked into its viewBox, so no CSS width
   could make it match: half of every box it was given went to nothing. All three
   now fill 99% of their artboard, which is the same rule as the photography
   ("trim every image to its content"), just applied to line art.

   Step two is density. Equal boxes still do not read as equal marks: the tick is
   a solid disc at 67% ink coverage, the star 43%, the certificate 49% of a wider
   shape. A dense solid always reads heavier than line work, so the tick is held
   smallest and the certificate — the sparsest and the only landscape one — is
   given the most room. */
.sw-illus-tile img[src$="tick-navy.svg"] { width: 37%; }
.sw-illus-tile img[src$="star-lime.svg"] { width: 42%; }
.sw-illus-tile img[src$="certificate.svg"] { width: 54%; }

/* A hero photograph that is PART of the band rather than a card floating on it —
   bleeding to the top, right and bottom edges, clipped by the band's own rounded
   corners, and sharing the band's bottom diagonal.

   The container is FULL WIDTH with the same transform-origin as the band fill.
   That matters: skewY pivots about its origin, so a half-width box skewed about
   its own left edge lands its diagonal tens of pixels off the band's and leaves a
   visible step. Same trap as the Pearson panel.

   The inner wrapper counter-skews by the same amount about the same origin, so it
   cancels exactly and the photograph inside stays upright — the band rule that a
   content layer is never skewed. The clipping still comes from the skewed
   container, so the bottom edge follows the diagonal. */
.sw-herophoto {
  position: absolute;
  inset: calc(-1 * var(--fill-overhang)) 0 0 0;
  z-index: 1;
  overflow: hidden;
  border-bottom-right-radius: var(--radius-2xl);
  transform: skewY(calc(-1 * var(--slant-h)));
  transform-origin: left bottom;

  /* NOT CLICKABLE, and this is load-bearing. This element is absolutely
     positioned across the WHOLE band — 16 to 1424 at 1440px, not just the media
     half — and sits at z-index: 1, so it covers the copy column too. Every
     button in a hero was therefore unreachable: the click landed on
     .sw-herophoto__inner and nothing happened, silently.

     It had been true on Sweet in England since the page was built and on the
     Pearson hero since its country buttons went in; found 25 September 2026
     when PGW's new jump buttons did nothing. `elementFromPoint` over a hero
     button returns the photo layer, which is how to check this.

     Nothing inside .sw-herophoto is interactive — it is a photograph or a set
     of books — so switching pointer events off costs nothing and hands the
     clicks back to the copy underneath. */
  pointer-events: none;
}

.sw-herophoto__inner {
  position: absolute;
  inset: 0;
  transform: skewY(var(--slant-h));
  transform-origin: left bottom;
}


.sw-herophoto image-slot {
  position: absolute;
  /* The container starts --fill-overhang ABOVE the band so the skew has room to
     clip against. The photo must not: filling the container put the subject's
     head up in the hidden overhang and showed only their hands. */
  top: var(--fill-overhang);
  bottom: 0;
  /* image-slot sets height:100% on its own :host, which beats a height derived
     from top and bottom — so the photo was the container's full 636px inside a
     356px band, and `cover` scaled it to that. The whole lower third of the frame
     (the boy's hands, the book he is writing in) was rendered below the band and
     clipped away. With the insets governing, the photograph fits the band it is
     in and the whole frame is visible. */
  height: auto;
  /* ...and `shape="rect"` gives the component `aspect-ratio: 3/2`, which beats
     inset-derived heights the same way `height: 100%` did. The photo was
     sized from its WIDTH — 662px wide, so 442px tall — and simply stopped
     short of a 661px band, leaving a slab of bare colour underneath. The
     insets are the intent; this lets them govern. */
  aspect-ratio: auto;
  right: 0;
  width: 46%;
  display: block;
  /* NO white separator. A 16px white stripe used to sit between the copy and
     the photograph on all five photographic heroes — About Sweet's hero and its
     managed-service band, Meet the Team, Sweet in England and Wave 3. Rebecca
     had it removed on 16 September 2026: on a navy band it read as a gap where
     the band had failed to paint, rather than as a deliberate rule.

     It also undercut what .sw-herophoto is FOR. The photograph is meant to be
     part of the band, bleeding to its edges and clipped by its corners; a white
     stripe down its leading edge turns it back into a card sitting on the band,
     which is the thing the class exists to avoid.

     Pearson never showed the bug because .sw-pearson-marks sets the same 16px
     border in --surface-inverse, so its stripe was navy on navy. That one
     stays: it is a real inset between two SURFACES, not a line across a photo. */
}

@media (max-width: 1000px) {
  /* Stacked, a bleeding side panel has no side to bleed into — so it bleeds to
     the band's TOP instead (Rebecca, 25 September 2026: "no light blue padding
     around photo. I want the photo to be full bleed").

     It used to sit `margin-top: 28px` with `border-radius: var(--radius-xl)`,
     which made it a rounded card floating on the band with blue showing above
     it and in all four corners. That is precisely what `.sw-herophoto` exists
     to avoid — the class is for media that is PART of a band rather than a card
     sitting on it — so on a phone the class was doing the opposite of its own
     job. Same argument as the white separator that came off these heroes on
     16 September 2026.

     No radius of its own: `.sw-band` is `overflow: hidden` with
     `--radius-2xl`, so the band's own corners shape the media's top two and
     the bottom edge is straight against the copy. Nothing is left square —
     the same reasoning as `.sw-band--bleed` and the header. */
  .sw-herophoto {
    position: static;
    overflow: visible;
    height: clamp(200px, 40vw, 300px);
    margin-top: 0;
    transform: none;
    border-radius: 0;
  }
  .sw-herophoto__inner { position: static; transform: none; height: 100%; }
  .sw-herophoto image-slot {
    /* RELATIVE, never static — see the note on the straight/object rule below.
       The component's :host is what contains its own absolutely positioned
       .frame, and static let the frame escape to the nearest positioned
       ancestor. It was not visible as a broken layout, because the slot clips:
       what it did instead was size the photograph against the WRONG box. On
       this page at 1000px the hero photo was rendered 673px tall inside a
       300px slot and the team photo 1725px tall inside the same 300px, so both
       showed a thin middle slice of a frame scaled for something else.

       `inset: auto` IS REQUIRED and its absence was a real bug, found
       25 September 2026. The desktop rule sets `top: var(--fill-overhang)` for
       an ABSOLUTE slot; switching to `relative` here without clearing it does
       not neutralise that offset — a relatively positioned box with `top` is
       shoved bodily DOWN the page by that amount. Measured on About Sweet at
       390px: the container sat at y=72–272 and the photograph rendered at
       y=352–552, i.e. 280px below its own box and straight over the heading
       and lede.

       It had been wrong on every PHOTOGRAPHIC hero — About Sweet, Meet the
       Team, Sweet in England, Wave 3, Pearson, Which Course — and on none of
       the four course heroes, because `.sw-herophoto--stage` already sets
       `inset: auto` for its own reasons. That is why it survived: the pages
       most looked at on a phone were the ones that happened to be immune. */
    position: relative;
    inset: auto;
    width: auto;
    height: 100%;
    /* No radius: the media bleeds, so the band's own corners clip it. */
    border-radius: 0;
    overflow: hidden;
  }
}

/* The name, as the acrostic it is — for the 2009 slide, where the copy explains
   where "Sweet" came from.

   Real text, not an image of text: it scales, it is readable by a screen reader
   and by search, and it can be translated for the Welsh site. An image would be
   none of those. It sits in one <p> with the words as blocks, so it is read as a
   single phrase rather than five unrelated fragments.

   Spark Lime because this slide is the origin story and lime is the brand's
   dominant colour; Focus Black on it is the standard pairing. */
.sw-acrostic {
  display: grid;
  place-items: center;
  background: var(--gradient-curious-blue);
  padding: clamp(18px, 2.4vw, 32px);
}

.sw-acrostic__set { margin: 0; color: var(--focus-black); }

.sw-acrostic__w {
  display: block;
  font-size: clamp(19px, 2.4vw, 30px);
  line-height: 1.22;
  letter-spacing: -0.01em;
}

/* The initial carries the acrostic, so it is the thing that has to read first. */
.sw-acrostic__w b {
  font-weight: var(--weight-semibold);
  font-size: 1.5em;
  line-height: 1;
}

/* Same full-bleed photo, on a band with no slant — so no skew and no counter-skew,
   and it takes the band's rounded corners on both right-hand corners rather than
   just the bottom one. */
/* Anchor a group photograph to its BOTTOM edge.

   `cover` crops evenly top and bottom, which is right for a landscape or a
   single subject and wrong for a group: the people stand on the floor, so the
   bottom of the frame is where the feet are and the top is ceiling. The team
   photo lost about 21px off the bottom — enough to clip the two crouching at
   the front — while carrying 109px of spare ceiling above the tallest head.

   Anchoring to the bottom spends that ceiling instead. The component sets
   width/height/left/top inline but never object-position, so this needs no
   !important. It reaches the <img> through the component's own `image` part. */
.sw-herophoto--bottom image-slot::part(image) { object-position: center bottom; }

.sw-herophoto--straight {
  inset: 0;
  transform: none;
  border-radius: 0 var(--radius-2xl) var(--radius-2xl) 0;
}

/* Every page hero is the same height, whatever it happens to contain.

   Left to their content they ran 282px (Meet the Team, which has no media column
   at all) to 480px (About Sweet), so the top of the site changed size page to
   page — the one element a visitor sees on arrival every single time. `--hero-min`
   was only ever a floor for the MEDIA inside a hero, not for the band, so it did
   nothing on a hero without media.

   The floor is on the inner, so the band's own padding still applies on top and
   the number stays the height of the copy area rather than a magic band height. */
/* TRIAL (14 August 2026): the Meet the Team hero as a full-width band rather than
   an inset tile — flush to the window on both sides and at the top, with only the
   bottom two corners rounded so the slanted edge still reads as a shape.

   It follows the header, which is itself full width and flush at top: 0, so the
   two now read as one continuous block of the site's furniture instead of a bar
   with a floating violet card underneath it.

   Not a breach of the no-sharp-corners rule, for the same reason `.sw-band--bleed`
   and the header square theirs: the top corners and both sides run off-screen, so
   there are no visible corners to round. Its inner picks up --page-inset so the
   pills still line up with the bands below.

   Scoped to .sw-heroband--bleed, not to every hero, precisely because this is a
   trial — remove the class from the page and the hero is an inset tile again. */
.sw-heroband--bleed {
  margin-inline: 0;
  margin-top: 0;
  border-radius: 0 0 var(--radius-2xl) var(--radius-2xl);
}

.sw-heroband--bleed > .sw-band__inner {
  padding-inline: calc(var(--band-pad-x) + var(--page-inset));
}

.sw-heroband > .sw-band__inner {
  min-height: var(--hero-band-min);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Hero copy runs to the media column, not to a measure set for a full-width band.
   At 46ch it was 467px in a 1368px band whose media does not start until 739px —
   272px of the hero left unused while the headline broke over four lines. 62ch
   still sits inside a comfortable measure and leaves a ~70px gutter before the
   photograph.

   Scoped to heroes that HAVE media: Meet the Team's hero is a pill headline with
   no second column, and capping that would only make it wrap harder.

   Set here rather than inline on each page — the same trap that stopped the
   post-hero spacing rule working for weeks. */
.sw-heroband:has(.sw-herophoto) .sw-band__inner > .sw-stack { max-width: 62ch; }
.sw-heroband:has(.sw-herophoto) h1 { max-width: 26ch; }

/* The Pearson hero briefly had its own copy-column rules — a wider stack and a
   --hero-prose measure — to force its 53-character headline onto two lines.
   They are gone, and the reason is worth keeping: the media column is 46% on
   every hero on the site, which leaves 720px for copy at 1440, and two lines of
   that headline need 740px. The two are mutually exclusive. Rebecca chose the
   split on 16 September 2026 — it is the thing every About page shares, and a
   hero whose divide sits somewhere different is the odd one out at a glance,
   where a three-line headline is not. This hero now uses the standard rules.

   If two lines is ever wanted again, the lever is the HEADLINE, not the layout:
   about eight characters shorter would do it. */

/* A cut-out rather than a photograph in the hero shape — the four book heroes.
   A photograph fills its region edge to edge and the slant simply crops it; a
   cut-out is an object and must not be cut by anything, so it is inset far
   enough to sit clear of both the diagonal and the white divider.

   The bottom inset is the slant's rise across the panel (panel width × tan of
   --slant-h) plus a margin — without it the lowest book is sliced by the
   diagonal, which is the one thing a cut-out must never look like. No white
   stage: the books sit directly in the course colour, which is what makes the
   hero one shape rather than a card floating on it. */
.sw-herophoto--object image-slot {
  top: calc(var(--fill-overhang) + 18px);
  /* The slant's deepest point under this panel, not a guessed percentage: the
     bottom edge rises by (distance from the band's left) x tan(--slant-h), so at
     the panel's far right that is 97% of the band width. A percentage of the
     band's HEIGHT cannot express it — the rise is a function of width — which is
     why the first attempt sliced the lowest book clean off. */
  bottom: calc((min(100vw, var(--page-max)) - 2 * var(--page-inset)) * 0.0419);
  /* image-slot carries height:100% on its own :host, which wins over a height
     derived from top and bottom — so without this the panel is the container's
     full 636px and the insets above do nothing at all. */
  height: auto;
  width: 40%;
  right: 4%;
  border-left: 0;
}

/* A cut-out on a STRAIGHT band needs none of the slant compensation above — there
   is no diagonal to clear. Left with it, the books sat flush to the top of the
   panel with 120px of dead colour underneath, which is what made the block look
   top-heavy against copy that was properly centred. Even insets, so the cut-out
   centres in the panel the way the copy centres in its column. */
/* A cut-out on a STRAIGHT band needs none of the slant compensation above — there
   is no diagonal to clear — and it must sit centred in its panel.

   Done with a padded flex container rather than top/bottom insets. `image-slot`
   carries its own height rules, so an inset-derived height is unreliable: the
   same declarations produced 34/50 on one band and 18/97 on another. Giving the
   wrapper the padding and letting the slot fill it centres the cut-out on every
   page, whatever the band's height turns out to be. */
.sw-herophoto--straight.sw-herophoto--object .sw-herophoto__inner {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 34px 4%;
}

.sw-herophoto--straight.sw-herophoto--object image-slot {
  /* RELATIVE, never static. The component's own :host carries
     `position: relative`, and that is what contains its internal
     `.frame { position: absolute; inset: 0 }`. Set the host to static and the
     frame resolves against .sw-herophoto instead — which is absolutely
     positioned and the full width of the band — so the cut-out escaped its
     panel, spread across all 1408px and sat on top of the copy. It read as
     "the image is too big"; it was actually unconstrained.

     Relative flows identically in the flex row, so nothing else changes. */
  position: relative;
  width: 40%;
  height: 100%;
}

@media (max-width: 1000px) {
  /* `height: 100%` is NOT redundant with the stacked rule above. That rule is
     `.sw-herophoto image-slot`, (0,2,0); `.sw-herophoto--object image-slot` sets
     `height: auto` at the same specificity and LATER in the file, so it wins
     even in here. With position: relative, `top`/`bottom` set no height, and a
     slot whose contents are all absolutely positioned collapses to 0px.

     The books were invisible on every course hero below 1000px and nobody had
     noticed, because a collapsed slot on a coloured band just looks like more
     band. Surfaced the moment the white stage went in. Same family as the
     `width: auto` collapse recorded under "An image-slot must never be
     position: static". */
  .sw-herophoto--object image-slot { top: 0; bottom: 0; width: auto; right: auto; height: 100%; }
  /* `width: auto` on a FLEX CHILD is shrink-to-fit, and everything inside an
     image-slot is absolutely positioned — so the slot collapsed to 0px wide and
     the books vanished entirely below 1000px. It only ever looked fine because
     the static-position bug above was letting the frame escape and paint the
     whole band. Stacked, the cut-out gets the full width of its panel. */
  .sw-herophoto--straight.sw-herophoto--object image-slot {
    top: 0; bottom: 0; right: auto;
    width: 100%;
  }
  .sw-herophoto--straight.sw-herophoto--object .sw-herophoto__inner {
    padding: 0;
  }
}

.sw-herophoto--straight .sw-herophoto__inner { transform: none; }
.sw-herophoto--straight image-slot { top: 0; }

/* Keeps the copy clear of the photo bleeding in from the right. */
.sw-bleedband .sw-band__inner { padding-right: max(var(--band-pad-x), 50%); }

/* There is no photograph-left variant. One was built on 11 September 2026 and
   removed the same day: a bleeding photo panel and a two-column grid produce
   different copy columns, so alternating between them down one page left the
   text edges out of line. The page zig-zags by side of MEDIUM instead —
   photographs bleed right, colour blocks and collages sit inset in a .sw-split
   on the left. If a photograph ever has to go left, it wants .sw-split too, not
   a mirrored bleed. */

@media (max-width: 1000px) {
  .sw-bleedband .sw-band__inner { padding-right: var(--band-pad-x); }
  .sw-herophoto--straight { border-radius: var(--radius-xl); }
}

/* Rotating wall of centre logos, for the "Now" slide — a stand-in for 250+
   centres without shipping 250 files.

   ONE white tile holding six crests, not six tiles. Six bordered cells made a
   grid of six separate objects, and the reader counted them; one surface makes
   a single object — a wall of centres — that happens to change. It is also what
   lets all six swap together without reading as a flicker: one picture changes,
   not six things at once.

   White, because every crest carries its own colours and most are drawn for a
   white ground — a tinted surface behind them would fight. */
/* The dark ground the logo tile sits on (Rebecca, 9 October 2026). The wall
   used to be a bare white panel — the only place on the site showing school
   crests without a dark surround, where the homepage marquee has always put
   each crest on a white tile over the navy band. This brings the outlier into
   line rather than inventing a treatment.

   16px of inset, which is also the concentric arithmetic: .sw-carousel__media
   carries --radius-2xl (40px) on its outer corners, so the tile inside takes
   40 - 16 = 24px, which is --radius-lg exactly. Change the padding and the
   tile's radius has to move with it. */
.sw-logostage {
  display: flex;
  padding: 16px;
}

/* Now the white TILE rather than the whole panel. It keeps its own 20px of
   internal padding, which is the breathing room around the crests and a
   different job from the 16px inset above. */
.sw-logocycle {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: 1fr;
  gap: 18px;
  padding: 20px;
  background: var(--simple-white);
  border-radius: var(--radius-lg);
  flex: 1 1 auto;
  min-width: 0;
}

.sw-logocycle__cell {
  position: relative;
  min-width: 0;
  overflow: hidden;
}

/* Two stacked layers per cell, cross-faded — the incoming logo comes up as the
   outgoing one goes down, so the cell is never empty. Fading everything out and
   back in left six holes in the tile for the length of the fade, which is what
   made the rotation read as broken.

   `contain`, because the crests are every shape and ratio going and must not be
   cropped — a crest with its edge cut off reads as a mistake.

   The size caps are the other half of it. Left to fill their boxes, a square
   crest takes the cell's full height while a wide wordmark takes its full width,
   so the two land at completely different visual weights and every swap looks
   like a lurch in scale. Capping height harder than width pulls the two shapes
   towards the same optical size: tall crests are held back, wide marks are not.
   This is the standard fix for a logo wall and there is no substitute for it —
   the files are supplied at whatever size their school chose. */
.sw-logocycle__cell img {
  position: absolute;
  inset: 0;
  margin: auto;
  max-width: 88%;
  max-height: 54%;
  width: auto;
  height: auto;
  object-fit: contain;
  opacity: 0;
  transition: opacity 700ms var(--ease-out);
}

.sw-logocycle__cell img.is-on { opacity: 1; }

/* Pricing card headings as Highlight Tags. The tag brings its own type and
   padding, so the heading element is stripped back to a positioning wrapper —
   left-aligned rather than stretched, or the pill would fill the card. */
.sw-unit__t--pill {
  margin: 0 0 14px;
  display: flex;
  justify-content: flex-start;
}

/* The pricing grid's buttons are centred under it, not left-aligned like the
   ones inside a card. They answer the whole three-card block rather than any one
   card, so they belong on the block's axis — hung off the left edge they read as
   a fourth column that lost its heading. */
.sw-card-actions--centre {
  justify-content: center;
}

/* A near-square illustration needs far more of the tile's height than a wide one
   to read at the same visual size — 62% suits the Pearson cards, not this. */
/* A near-square illustration needs far more of the tile's height than a wide one
   to read at the same visual size — 42% suits the Pearson cards, not this. */
/* 62% until 13 October 2026, and it was what actually set the size — it comes
   after the base rule and beat the 100% set there, which is why the artwork
   stayed at 214px when the base rule was opened up. Both say 100% now; with
   object-fit: contain the illustration grows to whichever edge it reaches
   first and keeps its ratio either way. */
.sw-illus-tile--tall img { width: 100%; }

/* ============================================================
   Order resources — Pearson, signposting, and what's coming
   Added 11 September 2026.
   ============================================================ */

/* ── Pearson under the page title ───────────────────────────────────────────
   The page carried exactly ONE mention of Pearson before this, at a point most
   readers never reached. The endorsement is the single strongest thing Sweet
   can say about a workbook — it is why a head of department can put it in
   front of an exam board — so it goes where the eye already is: directly under
   the title, before the first product.

   A whole explanatory band was the alternative and was rejected: this page's
   job is ordering, and a section of prose between someone and the products
   they came for costs more than it earns. The lockup says it without words. */
.sw-shopmarks {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.6vw, 22px);
  flex-wrap: wrap;
  margin-top: 22px;
  padding: 14px 20px;
  border-radius: var(--radius-md);
  background: var(--paper-ivory);
}

/* The site's .sw-cobrand is STACKED and sized by width — built for a quarter-
   width hero column on the Pearson page. In a single slim row under a page
   title it has to lie down, so this puts it back side by side at a small,
   fixed width. Pearson stays narrower than Sweet: its wordmark is the wider,
   shorter shape and matching widths would make it look twice the size. */
.sw-shopmarks .sw-cobrand {
  --cobrand-w: 92px;
  --cobrand-h: 22px;
  flex-direction: row;
  align-items: center;
  gap: 12px;
}

.sw-shopmarks .sw-cobrand__partner { width: 76px; }

.sw-shopmarks__t {
  margin: 0;
  font-size: 15px;
  line-height: 1.4;
  color: var(--text-secondary);
}

/* ── The Pearson line on each product card ──────────────────────────────────
   The lockup above is seen once; this travels WITH the thing being bought, so
   it is still on screen at the moment someone is deciding on a price.

   Text only, no second Pearson mark. Seven copies of the logo down one grid
   would out-shout the products and turn an endorsement into wallpaper — and
   the mark is already stated once at full size above. It sits directly over
   the price rather than up by the course eyebrow, because that is where the
   decision happens. */
.sw-product__endorse {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  font-weight: var(--weight-medium);
  line-height: 1.3;
  color: var(--focus-black);
}

.sw-product__endorse::before {
  content: "";
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  border-radius: var(--radius-pill);
  background: var(--focus-black);
  /* A tick, drawn as a mask so it takes the ground colour of the card. */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm-1.2 14.3-4-4 1.4-1.4 2.6 2.6 5.6-5.6 1.4 1.4Z'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm-1.2 14.3-4-4 1.4-1.4 2.6 2.6 5.6-5.6 1.4 1.4Z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* ── Tell me when it lands ──────────────────────────────────────────────────
   Three fields, not the six of the full enquiry form: this is a person saying
   "keep me posted", not asking a question, and every extra box costs
   completions. Email, school and role are the three the first conversation
   actually needs.

   It sits UNDER the two Coming soon tiles, in the same column and at card
   scale. It was a full-width band of its own first and did not earn one —
   three fields announcing one thing does not carry a whole section of the
   page, and a band under the shop put a second heading hierarchy below a
   section that had just finished.

   Ivory, like the tiles above it, so the three of them read as one block about
   the same thing. The inputs are white and carry their own stroke, so the panel
   does not need an edge.

   Like every form on the site it has no backend and opens the visitor's own
   email app pre-filled. See Forms in CLAUDE.md — when an endpoint exists,
   change send() in assets/js/signup-form.js and only then show a
   confirmation. */
.sw-signupbox {
  margin-top: 24px;
  padding: clamp(20px, 2vw, 26px) clamp(22px, 2.2vw, 28px);
  border-radius: var(--radius-lg);
  background: var(--paper-ivory);
}

.sw-signupbox__h {
  margin: 0;
  font-size: 20px;
  line-height: 1.15;
  font-weight: var(--weight-regular);
  letter-spacing: -0.02em;
}

.sw-signupbox__p {
  margin: 6px 0 0;
  max-width: 58ch;
  font-size: 15px;
  line-height: 1.45;
  color: var(--text-secondary);
}

/* Three fields and the button on one row. The button is a grid child rather
   than a block under the fields, so the whole thing is one line deep — which
   is the point of shrinking it. It bottom-aligns to sit on the inputs' line,
   not the labels'. */
.sw-signup__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr)) auto;
  align-items: end;
  gap: 14px;
  margin-top: 18px;
}

.sw-submit--sm {
  margin-top: 0;
  padding: 13px 22px;
  font-size: 15px;
  white-space: nowrap;
}

/* Two rows before it stacks: three inputs across plus a button needs about
   840px, and the product column is narrower than the page. */
@media (max-width: 1180px) {
  .sw-signup__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sw-signup__grid .sw-submit--sm { grid-column: 1 / -1; justify-self: start; }
}

@media (max-width: 560px) {
  .sw-signup__grid { grid-template-columns: minmax(0, 1fr); }
}

.sw-signup .sw-inlineerr { display: none; margin-top: 14px; }
.sw-signup .sw-inlineerr.is-shown { display: block; }

/* ============================================================
   The two book sets, cycling — About Sweet "What we do"
   ============================================================ */
/* A colour block with a cut-out set on it, alternating between the two
   courses. It replaced a classroom photograph: the section's job is to say
   what Sweet actually is, and the two sets of books ARE the answer — a stock
   classroom is decoration by comparison. It also stopped the page reusing a
   photograph already placed on another page.

   The ground colour comes from the set on show, not from its position:
   Curious Blue for Personal Growth and Wellbeing, Progress Green for
   WorkSkills, the same rule that governs the shop's product cards. A single
   fixed colour would have made the change of books read as an accident.

   It lives inside .sw-herophoto so it bleeds and clips exactly as the
   photograph did, but the slot rules from that system have to be undone for
   the nested slots — see below. */
/* An INSET colour block in a .sw-split, not a panel bleeding to the band's
   edge. Two reasons. "Colour is inset, white is full-bleed" is the site's rule
   and a colour panel running off the band breaks it. And as a bleed panel its
   copy column was a different width from every other section's — a
   46%-panel-plus-padding and a two-column grid do not line up when stacked, so
   the text stopped 197px short of the right edge every other band reaches.

   4:3 so it stands the same height as the Who we are collage below it, which
   is the same construction. */
.sw-bookcycle {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-xl);
  background: var(--accent-blue);
  transition: background var(--duration-slow, 600ms) var(--ease-out);
  overflow: hidden;
}

.sw-bookcycle[data-course="pgw"] { background: var(--accent-blue); }
.sw-bookcycle[data-course="workskills"] { background: var(--accent-green); }

.sw-bookcycle__slide {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  /* Clear of the dots below and the block's edges: a cut-out that touches its
     panel edge reads as clipped, which is the one thing a set of books must
     never look like. */
  padding: 34px 8% 56px;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--duration-slow, 600ms) var(--ease-out);
}

.sw-bookcycle__slide.is-on { opacity: 1; visibility: visible; }

/* image-slot carries aspect-ratio 3/2 and height:100% on its own :host; both
   have to go or the books size themselves from their width and ignore the
   slide. */
.sw-bookcycle image-slot {
  position: relative;
  inset: auto;
  width: 100%;
  height: auto;
  flex: 1;
  min-height: 0;
  border: 0;
}

.sw-bookcycle__cap {
  flex: 0 0 auto;
  font-size: 14px;
  font-weight: var(--weight-medium);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: center;
  /* Full Focus Black: both grounds are the mid-tones .sw-on-mid exists for,
     and 66% black on Progress Green falls under AA. */
  color: var(--focus-black);
}

.sw-bookcycle__dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 22px;
  display: flex;
  justify-content: center;
  gap: 10px;
  z-index: 2;
}

.sw-bookcycle__dot {
  width: 10px;
  height: 10px;
  padding: 0;
  border: 2px solid var(--focus-black);
  border-radius: var(--radius-pill);
  background: transparent;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-out);
}

.sw-bookcycle__dot.is-on { background: var(--focus-black); }
.sw-bookcycle__dot:focus-visible { outline: 2px solid var(--focus-black); outline-offset: 3px; }

@media (max-width: 720px) {
  /* Square, not 4:3. Stacked the block is only as wide as the phone, and at 4:3
     the padding and the caption left the books 116px of a 233px block. Square
     gives them 207px for the same width. */
  .sw-bookcycle { aspect-ratio: 1 / 1; }
  .sw-bookcycle__slide { padding: 22px 10% 48px; }
}

/* No movement for anyone who has asked for none. The first set shows and the
   dots still switch by hand — the script does not start the cycle at all. */
@media (prefers-reduced-motion: reduce) {
  .sw-bookcycle,
  .sw-bookcycle__slide { transition: none; }
}

/* ============================================================
   The team as a collage — About Sweet "Who we are"
   ============================================================ */
/* Eleven cut-out headshots on colour tiles, in place of the single group
   photograph. The group shot said "there are some people"; the collage says
   who, and it previews the Meet the Team page it links to rather than
   describing it.

   Deliberately NOT built on .sw-herophoto. That is a photograph bleeding to the
   band's edges, and its white separator down the middle of a green band would
   have read as a seam through nothing. A collage is content, so it takes the
   ordinary two-column .sw-split and sits inside the band's own padding.

   Four columns and a THREE-colour cycle: coprime, so no tile ever matches the
   one above it. Same rule as .sw-team on Meet the Team, and the same reason —
   with a 4-cycle in a 4-wide grid every column would be one flat stripe of
   colour.

   THE CYCLE LEAVES OUT THE BAND'S OWN COLOUR. A tile in it disappears into the
   ground. The band is Curious Blue, so the cycle is violet / lime / green; when
   it was Progress Green the cycle was violet / lime / blue. Change the band
   colour and this has to change with it — that is the whole trap. */
/* FLEX, NOT GRID, as of 13 October 2026, and only so the last row can centre
   itself. It was a four-column grid, which was exact while there were twelve
   tiles — eleven faces and the Sweet mark. Taking Simon off the site left
   eleven, and a grid cannot centre a short final row: it pinned three tiles to
   the left and opened a hole in the bottom-right corner.

   Same construction .sw-team already uses, and for the same reason. The column
   count is in the flex-basis rather than in grid-template-columns; everything
   else — the colour cycle, the square tiles, the radius — is untouched,
   because the cycle keys off :nth-child and does not care how the tiles are
   laid out. */
.sw-collage {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
}

.sw-collage__tile {
  position: relative;
  flex: 0 0 calc((100% - 4 * 12px) / 5);
  aspect-ratio: 1 / 1;
  min-width: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
}

/* These tiles take the same 4% inset .sw-face does — see it up there. Same
   band, same photographs, same problem: a dark jumper at the bottom of a tile
   and a navy band behind it are the same colour. Before it, Natalie and Amy
   were 100% of their bottom edge under 3:1, Kris 87%, Rebecca 74%.

   The slot carries aspect-ratio 3/2 from shape="rect" and height:100% on its
   own :host — the first would beat a square tile, so it is cleared and the
   tile holds the shape. Same fix as .sw-face. */
.sw-collage__tile image-slot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  display: block;
}

/* ── Headshots that stop short of the corner ────────────────────────────────
   Rebecca, 16 October 2026: "you need to sort these gaps at the bottom of the
   images on headshots. its not doing them on all them. maybe the ones that do
   have it just need their images moved down slightly."

   Both halves of that are right, and it took four wrong measurements to see
   why. The headshots are CUT-OUTS WITH A TRANSPARENT BACKGROUND, masked on a
   curve around the shoulders. Where the mask stops short of the square's bottom
   corners the tile colour shows through. Every probe written against the
   RENDERED tile sampled the middle, where there is no gap at all, and came back
   clean while she was looking straight at it.

   The answer was in the alpha channel of the stored images, not on screen.
   Transparent margin at the bottom of each cut-out, as a share of its width:

       michael 12.4% (LEFT ONLY)   rebecca 6.2%   lindsay 5.8%   asher 5.7%
       natalie  3.7%   fran 3.7%   amy 3.0%
       jordan   0.0%   kris 0.0%   martin 0.0%

   — which is exactly "it's not doing them on all them": three of the ten are
   already perfect and are left alone. Each of the rest is scaled by just enough
   to cover its own margin, about a point above centre so the growth goes
   downward into the corners. That is her "moved down slightly", done as a zoom
   so the face stays where it is.

   MICHAEL IS THE ODD ONE and shows why a single blanket value fails: his
   cut-out touches the right edge and leaves 12.4% bare on the left. Scaling him
   about the centre overflowed the right without ever reaching the left — 1.24
   still left a visible wedge. His origin is on the right, so the growth all
   travels leftward into the gap that actually exists.

   Keyed by slot id, so each person is corrected everywhere their photo appears
   — this collage, Meet the Team and the Contact hero — rather than per layout. */
image-slot#team-michael { transform: scale(1.15); transform-origin: 95% 30%; }
image-slot#team-rebecca { transform: scale(1.13); transform-origin: 50% 30%; }
image-slot#team-lindsay { transform: scale(1.12); transform-origin: 50% 30%; }
image-slot#team-asher   { transform: scale(1.12); transform-origin: 50% 30%; }
image-slot#team-natalie { transform: scale(1.05); transform-origin: 50% 30%; }
image-slot#team-fran    { transform: scale(1.05); transform-origin: 50% 30%; }
image-slot#team-amy     { transform: scale(1.04); transform-origin: 50% 30%; }
/* jordan, kris and martin need nothing — their cut-outs already reach the edge. */

.sw-collage__tile:nth-child(3n + 1) { background: var(--accent-violet); }
.sw-collage__tile:nth-child(3n + 2) { background: var(--spark-lime); }
.sw-collage__tile:nth-child(3n + 3) { background: var(--accent-green); }

/* There was a twelfth tile here holding the Sweet symbol, because eleven people
   left a hole in a four-column grid and a hole in a grid of faces reads as a
   photograph that failed to load.

   Removed 13 October 2026. Rebecca said the grid "looks uneven" and the symbol
   tile was the cause, not the cure: at four across, eleven tiles ran 4/4/3 with
   the last row centred, so the bottom row sat indented under two full ones.
   Simon coming off the site left ten people, and ten divides by five exactly —
   so the grid is five across and two deep with nothing standing in for anybody.

   Five columns against the three-colour cycle above are coprime, so no column
   repeats a colour and no tile matches its neighbour. Four never was, which is
   what the mobile override below used to exist for. */

@media (max-width: 720px) {
  /* Still five across on a phone, not three. Three columns would leave ten
     tiles as 3/3/3/1 — one face alone on the last row, which is the ragged
     bottom row this layout was changed to get rid of, reappearing at the width
     where it shows most. Five stays two rows at every width.

     The four-cycle that used to be here has gone with it: it existed only
     because three columns and a three-colour cycle are not coprime and every
     column came out one colour. Five and three are, so the base cycle runs. */
  .sw-collage { gap: 10px; }
  .sw-collage__tile { flex-basis: calc((100% - 4 * 10px) / 5); }
}

/* A photograph held inset inside a .sw-split — the evidence section on About
   Sweet. Not .sw-herophoto: that one bleeds to a band's edges, and this sits on
   the same grid as the copy beside it. Same construction as the .sw-collage and
   .sw-bookcycle panels above and below it, and it takes their 4:3 so the three
   stacked split sections share one rhythm rather than each inventing a height.

   image-slot carries aspect-ratio 3/2 from shape="rect" and height:100% on its
   own :host; both have to go or the photo sizes from its own width and ignores
   the panel. Same fix as .sw-collage__tile and .sw-objectpanel below. */
.sw-splitphoto {
  position: relative;
  aspect-ratio: 4 / 3;
  min-width: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.sw-splitphoto image-slot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  display: block;
}

/* A cut-out held at a fixed ratio in a grid column — the "Start your Sweet
   journey" books on About Sweet.

   Inset, not bled. A photograph can be cropped by a band's edge and often
   should be; a set of books is an object and must not be cut by anything, which
   is the whole reason .sw-herophoto--object exists. Inside a .sw-split it needs
   no slant compensation at all, and it lands on the same grid as every other
   section on the page.

   The ratio is the CUT-OUT'S OWN (16:9, the fan of WorkSkills books), not the
   4:3 of .sw-bookcycle and the collage. Matching those looked tidier in the
   stylesheet and was worse on screen: `contain` letterboxed the books inside a
   4:3 panel, 60px of empty lime above and below, and it made the CTA band
   621px tall for no content. At the image's own ratio there is no dead space
   and the band is back to 501px. Trim the file, then size the box to it — the
   same rule as "encode a photo at the aspect ratio of the box it goes in",
   applied the other way round for an object that must not be cropped. */
.sw-objectpanel {
  position: relative;
  aspect-ratio: 16 / 9;
  min-width: 0;
}

/* image-slot carries aspect-ratio 3/2 and height:100% on its own :host; both
   have to go or the books size from their width and ignore the panel. */
.sw-objectpanel image-slot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  display: block;
}

/* ============================================================
   The Learner Entitlement Indicators Framework (Wave 3)
   ============================================================ */
/* A faithful reproduction of the Welsh Government's own infographic —
   `assets/PDF Downloads/leif-infographic-table-final-web_e.pdf`, WG54094 — in
   its OWN colours and column format, on Rebecca's instruction (16 September
   2026): "stick to the colours and format of the original framework as this is
   what teachers need to recognise".

   It replaces `.sw-bento`, which redrew the four pillars in Sweet's brand
   colours and sized pillar 3 larger because that is where Sweet sits. That was
   a good piece of design for a summary and the wrong thing for a government
   document a curriculum lead is meant to recognise at a glance. Pillar 3 is now
   marked by the lime flag alone, which sits BESIDE the framework rather than
   altering it.

   Colours sampled from the PDF, not guessed:
     title  #273583   pillar 1 #FF6600 on #FFE0CC   pillar 2 #205F9A on #D2DFEA
                      pillar 3 #6F2F9F on #E2D5EB   pillar 4 #009999 on #CCEAEA

   ONE deliberate deviation. The original sets white type on all four headers,
   and white on the orange measures 2.94:1 and on the teal 3.49:1 — both under
   the 4.5:1 AA threshold. Print gets away with what a screen does not. The
   COLOURS are unchanged; the type on those two turns Focus Black (5.76:1 and
   4.85:1). Navy and purple keep white, which already pass at 6.64 and 8.13. */
.sw-leif {
  --leif-title: #273583;
  margin-top: clamp(26px, 3vw, 40px);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--simple-white);
}

.sw-leif__title {
  margin: 0;
  padding: 18px clamp(16px, 2vw, 26px);
  background: var(--leif-title);
  color: var(--simple-white);
  font-size: clamp(18px, 1.9vw, 24px);
  line-height: 1.2;
  font-weight: var(--weight-bold);
}

.sw-leif__cols {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr)) minmax(0, 1.55fr);
  gap: 14px;
  padding: 14px;
}

/* Pillar 4 is wider because it alone splits into two sub-columns — A. Learner
   effectiveness and B. Post-16 planning — exactly as the original does. That is
   the document's own proportion, not an editorial weighting. */
@media (max-width: 1180px) {
  .sw-leif__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sw-leif__col--wide { grid-column: 1 / -1; }
}

@media (max-width: 720px) {
  .sw-leif__cols { grid-template-columns: minmax(0, 1fr); }
  .sw-leif__col--wide { grid-column: auto; }
}

.sw-leif__col { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

.sw-leif__head {
  margin: 0;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  background: var(--leif-c);
  color: var(--leif-ink);
  font-size: 15px;
  line-height: 1.25;
  font-weight: var(--weight-bold);
}

.sw-leif__sub { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
@media (max-width: 560px) { .sw-leif__sub { grid-template-columns: minmax(0, 1fr); } }

.sw-leif__stack { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

/* Card: tinted body, full-colour strip along the bottom naming who publishes
   the figure. The strip is part of the information, not decoration — it is how
   a school knows whether a number arrives from the Welsh Government or has to
   be published by the school itself. */
.sw-leif__card {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--leif-tint);
}

.sw-leif__body {
  flex: 1;
  padding: 12px 14px;
  font-size: 14px;
  line-height: 1.4;
  color: var(--focus-black);
}

.sw-leif__body p { margin: 0 0 6px; }
.sw-leif__body p:last-child { margin-bottom: 0; }

.sw-leif__body ul { margin: 4px 0 6px; padding-left: 16px; }
.sw-leif__body li { margin-bottom: 2px; }

/* The original uses a dash for a plain item and a bullet for a measure. Both
   are meaningful, so both are kept rather than flattened to one list style. */
.sw-leif__body ul.dash { list-style: none; padding-left: 8px; }
.sw-leif__body ul.dash > li::before { content: "\2013"; margin-right: 6px; }

.sw-leif__src {
  padding: 5px 14px;
  background: var(--leif-c);
  color: var(--leif-ink);
  font-size: 12px;
  font-weight: var(--weight-medium);
  line-height: 1.3;
}

.sw-leif__foot {
  margin: 0;
  padding: 10px 14px 16px;
  font-size: 13px;
  color: var(--text-secondary);
}

/* ── The six topic areas ────────────────────────────────────────────────────
   What is actually inside the workbooks — the most concrete thing on the page,
   so it gets tiles rather than a bullet list. Six divides by both three and two,
   so there is no ragged row at any breakpoint. */
.sw-topics {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  margin-top: clamp(22px, 2.6vw, 34px);
}

/* Four items rather than six: two across, one on a phone. A class, not an
   inline grid-template — an inline value beats every media query below, which
   is how the England pillars ended up two-across at 375px. */
.sw-topics--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 620px) { .sw-topics--two { grid-template-columns: minmax(0, 1fr); } }

@media (max-width: 1000px) { .sw-topics { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .sw-topics { grid-template-columns: minmax(0, 1fr); } }

/* Each topic is a card with a photograph above its name — the same anatomy as a
   product card in the shop: a light tile holding the image, inside a coloured
   card. Rebecca asked for photographs on 16 September 2026; until they arrive
   the tile is a MARKED placeholder, never an empty one.

   Do not use an unfilled image-slot for this. The component renders its own grey
   drop-target wash and a "Drop a photo" prompt, which is editor furniture and
   would ship to a live page. Same reasoning as .sw-person__await on the team
   page. */
.sw-topic {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  /* Mid-tones: full Focus Black, per the .sw-on-mid rule. */
  color: var(--focus-black);
}

/* A filled topic tile. Same box as .sw-topic__ph below — identical height,
   inset and radius — so a tile with a photograph and one still waiting line up
   exactly, which matters while only some of the six have photographs
   (Rebecca supplied three on 9 October 2026).

   The slot needs `aspect-ratio: auto` AND `height: 100%`. `shape="rect"` gives
   the component `aspect-ratio: 3 / 2` and `height: 100%` on its own :host, and
   the ratio silently beats a fixed height — the trap already recorded for
   `.sw-herophoto image-slot`, where it sized the photograph against the wrong
   box and looked merely "a bit off" rather than broken.

   Position stays RELATIVE, never static: the component's :host is what
   contains its own absolutely positioned frame. */
.sw-topic__media {
  height: 148px;
  margin: 14px 14px 0;
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.sw-topic__media image-slot {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
}

/* Concentric rule: --radius-sm inside --radius-lg with a 14px inset. */
.sw-topic__ph {
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 8px;
  height: 148px;
  margin: 14px 14px 0;
  border-radius: var(--radius-sm);
  background: var(--simple-white);
  box-shadow: inset 0 0 0 2px rgba(11, 30, 47, 0.12);
}

.sw-topic__ph img { width: 30px; height: auto; opacity: 0.4; }

.sw-topic__ph span {
  font-size: 12px;
  font-weight: var(--weight-medium);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.sw-topic__t {
  padding: clamp(14px, 1.5vw, 18px) clamp(16px, 1.8vw, 22px) clamp(18px, 2vw, 24px);
  font-size: clamp(17px, 1.7vw, 20px);
  line-height: 1.25;
  font-weight: var(--weight-medium);
}

/* Rebecca, 15 October 2026: "why is the paragraph text bold here?"
   .sw-topic__t is the tile's HEADING style — medium weight, 1.25 leading. The
   Alternative Provision tiles put a title AND a paragraph inside that one box,
   so the paragraph inherited the heading's weight and its tight line-height.
   This hands body text its own back. */
.sw-topic__b {
  display: block;
  font-size: 16px;
  font-weight: var(--weight-regular);
  line-height: 1.5;
}

/* A three-cycle across three columns would put one flat colour down every
   column, so this is a four-cycle: coprime with 3, and with 2 at the tablet
   breakpoint. Same rule as .sw-team and .sw-collage. */
.sw-topic:nth-child(4n + 1) { background: var(--accent-blue); }
.sw-topic:nth-child(4n + 2) { background: var(--spark-lime); }
.sw-topic:nth-child(4n + 3) { background: var(--accent-violet); }
.sw-topic:nth-child(4n + 4) { background: var(--accent-green); }

/* ── What a Sweet centre gets ───────────────────────────────────────────────
   Rebecca, 16 September 2026: "the support section looks boring".

   It was seven identical ruled rows, all the same weight, so nothing drew the
   eye. The fix was not decoration — it was noticing that the seven are not
   parallel. SIX are things (resources, guidance, tracking, training,
   standardisation, events). ONE is a person: the named IQA. That one is the
   actual differentiator and it was sitting fourth of seven, indistinguishable
   from a tracking spreadsheet.

   So the IQA is a feature panel with the team's faces on it, and the other six
   sit quietly underneath. The section now makes an argument instead of reading
   a list. As a bonus the remaining six divide by both three and two, so there
   is no ragged row at any breakpoint — which seven never could. */
.sw-iqa {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(24px, 3vw, 52px);
  align-items: center;
  margin-top: clamp(24px, 2.8vw, 36px);
  padding: clamp(26px, 3vw, 40px);
  border-radius: var(--radius-xl);
  /* White on the band, whatever the band is. The faces are cut-outs on their
     own coloured tiles, so they need a light ground to sit on — on the violet
     this band used to be, they would have been violet tiles on violet. */
  background: var(--simple-white);
}

@media (max-width: 1000px) {
  .sw-iqa { grid-template-columns: minmax(0, 1fr); }
}

.sw-iqa__h {
  margin: 0 0 10px;
  font-size: clamp(22px, 2.3vw, 30px);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: var(--weight-regular);
}

/* The six are deliberately quieter than the panel above them: three across, so
   they read as a set rather than as six separate claims competing with the one
   that matters. .sw-diff is two columns by default; this overrides it. */
.sw-access {
  margin-top: clamp(26px, 3vw, 40px);
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 26px 40px;
}

@media (max-width: 1000px) { .sw-access { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .sw-access { grid-template-columns: minmax(0, 1fr); } }

.sw-access .sw-diff__k { font-size: 18px; }

/* ── A Focus Black stage under a hero cut-out (`--stage`) ───────────────────
   Rebecca, 16 September 2026, after seeing both alternatives: the four course
   heroes match the Skills Suite one — the media BLEEDS to the band's top, right
   and bottom edges — and the shape is Focus Black with the books on it.

   Why a stage at all rather than just dropping `--object`. A photograph is meant
   to be cropped by the band's diagonal; a set of books is an object and must not
   be cut by anything, which is the whole reason `--object` insets it. So the
   bleeding shape carries the crop and the books sit inside it, still inset.

   The shape is a pseudo-element on the CONTAINER, which is the skewed element —
   that is deliberate here and the opposite of what an inset panel would need.
   Being skewed is exactly what makes its bottom edge follow the band's own
   diagonal, the same trick the band fill uses. The books live in
   `.sw-herophoto__inner`, which counter-skews, so they stay upright on it.

   No left radius: it bleeds top, right and bottom, so its only visible edge is
   a full-height vertical line with no corners on it — the same reason
   `.sw-band--bleed` and the header square their corners. Not a breach of the
   no-sharp-corners rule.

   It composes with `--object` rather than replacing it: the slot keeps the
   tested insets that clear the slant's deepest point. */
.sw-herophoto--stage::before {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  /* Wider than the slot's 40% + 4% so the books sit ON the shape with clear
     margin rather than touching its edge. */
  width: 46%;
  background: var(--focus-black);
}

@media (max-width: 1000px) {
  /* Stacked, the media is a full-width block and the shape is simply the block.

     It moves to the INNER here, and that matters twice over. At this width
     `.sw-herophoto` is `position: static`, so a pseudo-element on it has no
     positioned ancestor and sizes against the whole section — a black slab
     behind the copy. And the inner needs `position: relative` of its own, or the
     band's absolutely positioned `.sw-band__fill` paints straight over it: the
     books stay visible because the slot is relative, the shape behind them does
     not. */
  .sw-herophoto--stage::before { content: none; }

  /* No radius on the stage either — the band clips it. The 18px padding STAYS:
     it insets the BOOKS within the dark shape, which is a different job from
     insetting the dark shape within the blue. A set of books is an object and
     must not touch an edge; that is the whole reason --object exists. */
  .sw-herophoto--stage .sw-herophoto__inner {
    position: relative;
    background: var(--focus-black);
    border-radius: 0;
    padding: 18px;
  }

  /* `height: 100%` re-asserted: the `--object` rule sets `height: auto` at the
     same specificity and later in the file, so it beats the stacked rule. Left
     alone the slot collapses to 0px and the books vanish — which is exactly what
     had been happening on all four course heroes before today. */
  .sw-herophoto--stage image-slot {
    position: relative;
    inset: auto;
    width: 100%;
    height: 100%;
  }
}

/* ── A varied grid of team faces (`.sw-facegrid`) ───────────────────────────
   Rebecca, 16 September 2026: "can we vary the size of the team tiles". The
   uniform four-in-a-row of `.sw-faces` reads as a row of passport photos; one
   large tile with the rest around it reads as a team.

   SEVEN faces, and the count is not arbitrary — it is the only one that tiles
   at both breakpoints AND leaves the hero at 480px like every other hero on the
   site. The big tile spans 2x2 (4 cells) and six 1x1 fill the rest, so:

       5 columns x 2 rows = 10 cells at desktop   4 + 6 = 10  exactly
       2 columns x 5 rows = 10 cells on a phone   4 + 6 = 10  exactly

   Nine faces in four columns was tried first and made the band 583px, because
   three rows of square tiles across a 636px column is 476px of grid on its own.
   Change the number of faces and you have to redo this arithmetic; a count that
   does not tile leaves a visible hole in the grid. */
.sw-facegrid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 10px;
  min-width: 0;
}

.sw-facegrid > :first-child { grid-column: span 2; grid-row: span 2; }

.sw-facegrid .sw-face {
  width: auto;          /* .sw-face is a fixed clamp width; the grid sizes these */
  aspect-ratio: 1 / 1;
}

@media (max-width: 720px) {
  /* On a phone the mosaic stops being a mosaic and becomes a wall. Two columns
     with the 2x2 tile made the hero 1106px on a 812px screen — the form was
     below a full screen of faces, on the page whose whole job is the form.

     Three equal columns instead, and the seventh face is dropped so six fill two
     rows exactly. Hiding it is safe: these are decorative, the copy names nobody,
     and "Meet the whole team" sits directly above them. Grid height falls from
     782px to about 204px. */
  .sw-facegrid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .sw-facegrid > :first-child { grid-column: auto; grid-row: auto; }
  .sw-facegrid > :nth-child(7) { display: none; }
}
