/* ════════════════════════════════════════════════════════════════════════
   Odisea Design System — COMPONENT LIBRARY
   GENERATED from src/pages/components.astro — DO NOT EDIT BY HAND.
   Regenerate: node extract-components.mjs  (or: node ../sync-design-system.mjs)
   Requires tokens.css (design tokens) + a theme class on an ancestor
   (e.g. .theme-hub-light / .theme-hub-dark) to resolve --t-* colours.
   ════════════════════════════════════════════════════════════════════════ */

[data-theme], [class*="theme-"]{
    /* Accent scale */
    --t-accent:           var(--background-accent-default);
    --t-accent-soft:      var(--background-accent-softer);
    --t-accent-medium:    var(--background-accent-soft);
    --t-accent-strong:    var(--background-accent-strong);
    --t-accent-darker:    var(--background-accent-stronger);
    /* Secondary accent (accent-2 = tag-accent-2 in semantic tokens) */
    --t-accent-2:         var(--background-tag-accent-2-default);
    --t-accent-2-soft:    var(--background-tag-accent-2-soft);
    --t-accent-2-strong:  var(--background-tag-accent-2-strong);
    /* Surfaces
       background-section-light: white in light (#FFFFFF), dark-gray in dark (#2C3030) — used for card rows
       background-tag-neutral-soft: F5F2EB in light, #2C3030 in dark — used for alt surface (showcase shell)
       Note: in dark mode both surface-card and surface-alt resolve to #2C3030, so we override
       surface-alt per mode below to keep visual separation between showcase shell and card rows. */
    --t-surface:          var(--background-page);
    --t-surface-card:     var(--background-section-light);
    --t-surface-alt:      var(--background-tag-neutral-soft);
    --t-surface-inv:      var(--background-section-light-inverse);
    /* Text */
    --t-text:             var(--text-hero);
    --t-text-body:        var(--text-body);
    --t-text-caption:     var(--text-caption);
    --t-text-on-accent:   var(--text-on-accent);
    --t-text-inv:         var(--text-hero-inverse);
    --t-text-inv-body:    var(--text-body-inverse);
    --t-text-inv-caption: var(--text-muted);
    --t-text-accent:      var(--text-accent-default);
    /* Accent text that must hold contrast on a theme-flipping surface. Do NOT use
       --t-accent-strong for this: that aliases a *background* token pinned to
       hub-700 on every theme, so accent text on a dark card drops to ~3.8:1.
       This one flips with the theme (dark accent on light, light accent on dark). */
    --t-text-accent-strong: var(--text-accent-strong);
    /* Eyebrow / text-scale accent — AA criterion (IDENTITY-BRIEF §4.1): text-sized
       accent roles draw from the 500 step on light canvases and the lighter 400
       step on dark ones (see the -dark override below). Never the 500-on-900 pair. */
    --t-eyebrow-text:     var(--text-accent-default);
    /* Accent text on the permanently-dark chrome (nav, footer) — accent-soft
       resolves to a light step in BOTH modes (300 light / 400 dark), so it holds
       contrast on the inverse surface whatever the page mode. */
    --t-text-accent-inv:  var(--text-accent-soft);
    --t-text-heading:     var(--text-heading);
    /* Hero text — theme-flipping (dark on light heroes, light under theme-*-dark) */
    --t-hero-text:        var(--text-hero);
    --t-hero-body-text:   var(--text-body);
    /* Borders */
    --t-border:           var(--border-divider);
    --t-border-card:      var(--border-card-default);
    --t-border-soft:      var(--border-card-soft);
    --t-border-strong:    var(--border-card-strong);
    /* Tag soft = accent tag variant */
    --t-tag-soft-bg:      var(--background-tag-accent-soft);
    --t-tag-soft-text:    var(--text-tag-accent-strong);
    --t-tag-soft-border:  var(--border-tag-accent-soft);
    /* Tag secondary = accent-2 tag variant */
    --t-tag-2-bg:         var(--background-tag-accent-2-soft);
    --t-tag-2-text:       var(--text-tag-accent-2-strong);
    --t-tag-2-border:     var(--border-tag-accent-2-soft);
    /* Neutral outline button. These two were referenced by .btn--neutral but
       existed ONLY in the showcase's JS alias map, never in the distributed
       alias layer — so in production var(--t-neutral-text) resolved to nothing,
       `color` fell back to inherit, and every .btn--neutral rendered as
       near-black text with a near-black border on whatever sat behind it
       ("Book a Call" on the closing banner measured ~1.05:1). Both underlying
       tokens flip with the theme, so this is correct in light and dark. */
    --t-neutral-text:     var(--text-tag-neutral-default);
    --t-neutral-border:   var(--border-tag-neutral-strong);
    /* Section-level card surfaces. Same story as the neutral pair above: used by
       `.sec-testimonial .quote-block`, but present only in the showcase's JS alias
       map, so in production the background was invalid and every quote card
       rendered fully transparent on the section — the tinted card the testimonial
       section re-specs (with standard body text rather than the base component's
       pinned-dark treatment) never actually appeared. Both flip with the theme. */
    --t-card-light:       var(--background-card-light);
    --t-card-dark:        var(--background-card-dark);
    /* Photographic grade — IDENTITY-BRIEF §5.2: "one grade, tied to venetian.
       Dark-anchored, warm-desaturated cinematic grading: desaturated midtones,
       controlled warm highlights, tonal depth preserved. Never the E-Ture
       multiply/duotone crush; never ungraded raw color floating unfused on the
       dark canvas (Malaga's failure)." D3 shipped the scrim and the bloom but not
       this, so photography was arriving at full camera saturation. sepia() supplies
       the warmth, saturate()+brightness() the dark-anchored desaturation, and
       contrast() keeps the tonal depth rather than crushing it. */
    --t-image-grade: saturate(0.72) contrast(1.06) brightness(0.9) sepia(0.12);
    /* Chrome (nav + footer) — permanently DARK in both modes, one value-step
       beyond the canvas (the deep-pool tonal close, IDENTITY-BRIEF §6). Fixed
       Tier-2 grays, deliberately not the flipping -inv roles: a luxury dark
       page must never grow a light footer. */
    --t-chrome-bg:           var(--gray-warm-900);
    --t-chrome-text:         var(--base-white);
    --t-chrome-text-body:    var(--gray-cool-200);
    --t-chrome-text-caption: var(--gray-cool-400);
    /* White-PNG logo filter: none by default (logos sit on dark surfaces). */
    --t-logo-filter:      none;
    /* Transition for smooth theme changes */
    transition: background var(--dur-micro) var(--ease-out);
  }
[data-theme$="-light"], .theme-hub-light, .theme-academy-light, .theme-osm-light, .theme-shop-light, .theme-study-light{
    /* Inv-card: dark surface for cards on inverse (dark) rows */
    --t-surface-inv-card: var(--gray-cool-800);
    /* surface-alt in light mode = warm-100 (slightly darker than white card rows) */
    --t-surface-alt: var(--background-tag-neutral-soft);
    /* Invert white-PNG logos to dark so they're visible on light surfaces. */
    --t-logo-filter: invert(1);
  }
[data-theme$="-dark"], .theme-hub-dark, .theme-academy-dark, .theme-osm-dark, .theme-shop-dark, .theme-study-dark{
    /* Inv-card: light surface for cards on inverse (light) rows in dark mode */
    --t-surface-inv-card: var(--gray-warm-100);
    /* Accent text on dark card surfaces: text-accent-strong resolves to the 200
       step in dark (pale cream at display sizes), but the 500 lands near 3:1 on
       dark cards. The 400 (accent-soft in dark) is the AA-passing middle. */
    --t-text-accent-strong: var(--text-accent-soft);
    /* surface-alt in dark mode = page background (#17191A) so showcase shell is
       visibly darker than the card rows (#2C3030 from background-section-light) */
    --t-surface-alt: var(--background-page);
    /* Eyebrows on dark canvases step up to the 400 (accent-soft in dark) — the
       500-on-900 pair lands near 4:1, below AA for small text. */
    --t-eyebrow-text: var(--text-accent-soft);
    /* Chrome sinks one step below the dark canvas — the page's deepest value */
    --t-chrome-bg: color-mix(in srgb, var(--base-black) var(--opacity-50), var(--gray-warm-900));
  }
  /* ═══════════════════════════════════════════════════════════════════════════
     VELVET PATTERN LAYER (IDENTITY-BRIEF §4.2, §8.8)
     Authored ONCE here; recipes are never forked. Slots (--glow-color,
     --grad-from/--grad-to, --mx/--my) are set per use; knobs are the package's
     --p-dur-* / --p-glow-scale one-line retunes.
     Laws enforced below:
       · two glow moments per page — hero + chapel section recipes ONLY
       · everything else is value-lift: surface value-steps + hairlines,
         zero drop shadows on web components
       · every entrance runs animation-fill-mode: backwards (a held end-state
         transform would silently kill the hover language)
  ═══════════════════════════════════════════════════════════════════════════ */

  /* Core keyframes — compositor-cheap properties only (opacity/transform/clip) */
  @keyframes rise-in    { from { opacity: 0; transform: translateY(var(--rise-md)); }
                          to   { opacity: 1; transform: none; } }
  @keyframes fade-in    { from { opacity: 0; } to { opacity: 1; } }
  @keyframes rise-bloom { from { opacity: 0; transform: translateY(var(--rise-lg)); }
                          to   { opacity: 1; transform: none; } }
  @keyframes bloom-in   { from { opacity: 0; transform: scale(0.92); }
                          to   { opacity: 1; transform: none; } }
  @keyframes draw-line  { from { transform: scaleX(0); } to { transform: scaleX(1); } }
  /* Entrance utilities — hero/chapel get the 900ms bloom; content stays at panel tempo */
  .vel-enter      { animation: rise-in var(--dur-panel) var(--ease-out) backwards; }
  .vel-enter-hero { animation: rise-bloom var(--dur-entrance) var(--ease-out) backwards; }
  /* Scroll reveal — one IntersectionObserver (script below), class-driven.
     Elements hold their layout while invisible (opacity, never display) so
     entrances produce zero CLS. Stagger with --reveal-i: 0..n on siblings. */
  /* ⚠ SCOPED TO `.js-reveal`, WHICH THE OBSERVER'S OWN SCRIPT PUTS ON <html>.
     This rule hides content; the class that shows it again is added by JS. Left
     unscoped, any failure in that script — a bundle that 404s, an exception
     earlier in the file, a browser without IntersectionObserver — leaves the
     element at opacity 0 with no way back, and the failure mode of a marketing
     page is invisible content. Scoped, the hiding half cannot outlive the
     revealing half. (Added 2026-09-11 with the hub home's stats band.) */
  .js-reveal [data-reveal] { opacity: 0; transform: translateY(var(--rise-md)); }
  [data-reveal].in-view {
    opacity: 1; transform: none;
    transition: opacity var(--dur-panel) var(--ease-out),
                transform var(--dur-panel) var(--ease-out);
    transition-delay: calc(var(--reveal-i, 0) * 75ms);
  }
  /* Spotlight border — velvet's signature hover: a hairline that blooms toward
     the cursor. Pseudo-element ring + mask; --mx/--my fed by a 3-line mousemove
     (script below). Apply to any value-lifted card via the vel-spot class. */
  .vel-spot { position: relative; }
  .vel-spot::after {
    content: ''; position: absolute; inset: 0; border-radius: inherit;
    padding: var(--p-edge-w); pointer-events: none;
    background: var(--p-edge-hover);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
    opacity: 0; transition: opacity var(--dur-micro) var(--ease-out);
  }
  .vel-spot:hover::after { opacity: 1; }
  /* Pitch-line signature motif (G1 candidate — prototyped in hero + divider).
     The white line-marking vocabulary of a football pitch drawn as thin
     venetian-tinted strokes; grows from the system's hairline-rule vocabulary. */
  .pitch-lines { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
  .pitch-lines svg { width: 100%; height: 100%; display: block; }
  .pitch-lines [data-pitch-stroke] {
    fill: none;
    stroke: color-mix(in srgb, var(--base-white) var(--opacity-10), transparent);
    stroke-width: var(--stroke-xs);
    vector-effect: non-scaling-stroke;
  }
  .pitch-lines [data-pitch-stroke="accent"] {
    stroke: color-mix(in srgb, var(--t-accent) var(--opacity-25), transparent);
  }
  /* Section-transition divider: a halfway-line fragment + center-circle arc */
  .pitch-divider {
    position: relative; height: var(--size-16); margin: 0 auto; max-width: 1280px;
    display: flex; align-items: center; justify-content: center;
  }
  .pitch-divider__line {
    position: absolute; left: 0; right: 0; top: 50%; height: var(--size-px);
    background: var(--t-border);
    transform-origin: center; animation: draw-line var(--dur-entrance) var(--ease-out) backwards;
  }
  .pitch-divider__circle {
    position: relative; width: var(--size-16); height: var(--size-16);
    border: var(--stroke-xs) solid var(--t-border); border-radius: var(--radius-pill);
    background: var(--t-surface);
    animation: bloom-in var(--dur-panel) var(--ease-out) backwards;
  }
  .pitch-divider__dot {
    position: absolute; inset: 0; margin: auto;
    width: var(--size-1); height: var(--size-1); border-radius: var(--radius-pill);
    background: var(--t-eyebrow-text);
  }
  /* ── Sub-brand typography (George, 2026-09-15; face changed 2026-09-17 and 2026-09-18) ──
     The academy's numerals are set in B612, the Airbus cockpit face: George
     wants numbers with a navigation, journey and map-coordinates character to
     go with the chart grid. Jersey 25 (15 Sep) read as shirt numbers; Overpass
     Mono (17 Sep) looked like the hub's Plex Mono and dots its zero; B612 Mono
     gives commas a full figure's width. The proportional B612 has none of those
     problems and its figures are tabular by default. Every other product keeps
     Plex Mono. Only the NUMBER role changes — eyebrows, labels and captions are
     the eyebrow role and stay mono caps. The stat tiles use the standard sizes. */
  .theme-academy-light, .theme-academy-dark {
    --font-family-number: 'B612', 'IBM Plex Mono', monospace;
  }
  /* ── Sub-brand motifs (George + Deo, 2026-09-14) ──
     Each Odisea product keeps the shared palette and type and gets its OWN
     graphic layer. The academy's is line art — a nautical chart grid, the
     voyage path, a Greek galley, Helios, laurel — inlined by that site's
     Motif component from academy/src/motifs/. Every stroke is currentColor,
     so the HOST sets the ink; the few story elements (the route, the star)
     carry data-tone="accent". Rationed like the glow law: a hero, one band,
     one corner, one flank per page — a splash, never wallpaper. */
  .has-motif { position: relative; overflow: hidden; }
  /* A positioned decoration paints over in-flow siblings, so lift the content. */
  .has-motif > :not(.motif) { position: relative; z-index: 1; }
  .motif {
    position: absolute; z-index: 0; pointer-events: none;
    color: var(--t-accent); opacity: var(--motif-opacity, 0.18);
  }
  .motif svg { display: block; width: 100%; height: 100%; overflow: visible; }
  .motif [data-tone="accent"] { stroke: var(--t-accent-strong); }
  .motif [data-tone="accent"][fill="currentColor"] { fill: var(--t-accent-strong); stroke: none; }
  .motif[data-flip] svg { transform: scaleX(-1); }
  /* Dark canvases: cream lines, sky accent — pinned like the other dark-by-design
     surfaces (see the dark-mode law), never left to the theme flip. */
  .theme-academy-dark .motif, .sec-hero .motif, .sec-cta-banner .motif, .sec-chapel .motif { color: var(--base-white); }
  .theme-academy-dark .motif [data-tone="accent"], .sec-hero .motif [data-tone="accent"],
  .sec-cta-banner .motif [data-tone="accent"], .sec-chapel .motif [data-tone="accent"] { stroke: var(--text-accent-soft); }
  .theme-academy-dark .motif [data-tone="accent"][fill="currentColor"], .sec-hero .motif [data-tone="accent"][fill="currentColor"],
  .sec-cta-banner .motif [data-tone="accent"][fill="currentColor"], .sec-chapel .motif [data-tone="accent"][fill="currentColor"] { fill: var(--text-accent-soft); }
  /* Placements. A hero motif goes AFTER .sec-hero__bg so it paints over the photo —
     and a photograph eats hairlines, so the hero runs far stronger than a flat band. */
  .motif--hero { inset: 0; --motif-opacity: 0.6; }
  .motif--band { inset: 0; --motif-opacity: 0.28; }
  /* A corner emblem the size of the Helios beside "Who We Are". Wide files
     (mountain, waves, the Great Bear) keep their own aspect via --motif-ar. */
  .motif--corner-tr, .motif--corner-tl { top: var(--size-6); width: var(--motif-w, var(--size-32)); aspect-ratio: var(--motif-ar, 1); --motif-opacity: 0.45; }
  .motif--corner-tr { right: var(--size-6); }
  .motif--corner-tl { left: var(--size-6); }
  .motif--wide { --motif-w: calc(var(--size-32) * 1.6); }
  /* The page grid (George, 2026-09-15): one continuous chart layer fixed under
     the whole page. The lightest-gray sections become windows onto it — their
     own background is dropped so the canvas shows through — while white, photo
     and dark sections keep theirs and cover it. `fixed` is what makes it read
     as ONE grid: the sections slide over a still chart instead of each carrying
     a fragment of it.
     The layer sits INSIDE <main>, at z-index -1. A section's own background
     paints above a negative layer, so white, photo and dark sections cover it
     with no rule of their own, and all content paints above it. It goes LAST
     in <main>, not first, because `.sec-split:nth-child(even)` counts main's
     children. */
  .page-grid { position: fixed; inset: 0; z-index: -1; pointer-events: none; }
  /* Quiet: a desaturated petrol at low strength, so it sits behind the copy
     rather than competing with it. */
  .page-grid .motif { inset: 0; --motif-opacity: 0.2; color: color-mix(in srgb, var(--t-accent) 45%, var(--gray-cool-400)); }
  .has-page-grid > main, .has-page-grid > footer { position: relative; z-index: 1; }
  /* The two light grays survive: sections on the page canvas (gray-warm-100)
     go fully clear; the lighter gray-warm-50 sections keep their tone as a
     translucent veil, so they still read a step lighter and the grid shows
     through both. */
  .has-page-grid .sec-split, .has-page-grid .sec-image-grid:not(.sec-image-grid--white) { background: transparent; }
  /* The veil paints UNDER the grid, so the one layer reads at full strength on
     both grays: a positioned ::before at z-index -2 joins main's stacking
     context, one step below the grid. That holds only while the section itself
     creates no stacking context (no z-index, transform, filter, opacity below 1
     or isolation) — a section that does traps its veil above the grid.
     Until 2026-09-17 each veiled section painted a second copy of the grid as a
     `background-attachment: fixed` background instead. iOS ignores `fixed`, and
     `cover` then sizes the drawing to the section rather than the screen, so on
     a phone every tall section showed the chart zoomed in (George, academy
     review round one). With one layer there is no copy to drift.
     The partners strip is not a window: it keeps its own solid surface, so
     the grid stays out from behind the logos (George, 2026-09-17). */
  .has-page-grid .sec-split:nth-child(even),
  .has-page-grid .sec-feature { position: relative; background: transparent; }
  .has-page-grid .sec-split:nth-child(even)::before,
  .has-page-grid .sec-feature::before {
    content: ''; position: absolute; inset: 0; z-index: -2; pointer-events: none;
    background: color-mix(in srgb, var(--t-surface) 70%, transparent);
  }
  /* Flanks around an eyebrow — laurel or olive; the left branch is the same file, flipped. */
  .motif-flank { display: inline-flex; align-items: center; gap: var(--size-3); }
  .motif-flank .motif { position: static; width: var(--size-6); height: var(--size-10); --motif-opacity: 0.85; }
  @media (max-width: 700px) {
    .motif--corner-tr, .motif--corner-tl { top: var(--size-3); width: var(--motif-w, var(--size-20)); }
    .motif--wide { --motif-w: calc(var(--size-20) * 1.6); }
  }
  /* Reduced motion — transforms and reveals off, content immediately visible */
  @media (prefers-reduced-motion: reduce) {
    /* Matches the specificity of the `.js-reveal [data-reveal]` hiding rule —
       a one-class selector would lose to it and leave content waiting on an
       observer that reduced motion has no reason to wait for. */
    .js-reveal [data-reveal], [data-reveal] { opacity: 1; transform: none; transition: none; }
    .vel-enter, .vel-enter-hero,
    .pitch-divider__line, .pitch-divider__circle { animation: none; }
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      transition-duration: 0.01ms !important;
    }
  }
  /* ── BUTTON ──────────────────────────────────────────────────────────────── */
  /* ONE grammar (Gate G1 / IDENTITY-BRIEF §4.3): every control at radius-control
     (12), nav to form submit; the pill variant is pruned — reintroducing it would
     restore the radius coexistence this re-skin removes. The default CTA voice is
     the monochrome outline (E-Ture grammar in the round register); the accent
     fill is the ONE in-content accent CTA a page gets. Hover = one transform
     (2px lift) + one value-step surface change; never a color flash, never a
     drop shadow. */
  /* `justify-content: center`: a button wider than its label — stretched in a
     column, held to a min-width, or a label wrapped onto two lines on a phone —
     keeps the label in the middle, never at the left (George, 2026-09-25). A
     button that hugs its label looks the same either way. */
  .btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--basic-button-gap-md);
    padding: var(--basic-button-padding-y) var(--basic-button-padding-x);
    border-radius: var(--radius-control);
    font-family: var(--font-family-label); font-size: var(--font-size-sm);
    font-weight: var(--font-medium); line-height: var(--lh-none);
    letter-spacing: var(--tracking-wide);
    cursor: pointer; border: var(--basic-button-stroke-sm) solid transparent;
    transition: transform var(--dur-micro) var(--ease-out),
                background var(--dur-micro) var(--ease-out),
                color var(--dur-micro) var(--ease-out),
                border-color var(--dur-micro) var(--ease-out),
                opacity var(--dur-micro) var(--ease-out);
    text-decoration: none; white-space: nowrap;
  }
  .btn:hover:not(:disabled)  { transform: translateY(calc(-1 * var(--size-0p5))); }
  .btn:active:not(:disabled) { transform: none; }
  .btn:focus-visible { outline: var(--stroke-sm) solid var(--border-focus); outline-offset: var(--size-0p5); }
  .btn--sm  { padding: var(--basic-button-padding-y-sm) var(--basic-button-padding-x-sm); font-size: var(--font-size-xs); }
  .btn--lg  { padding: var(--basic-button-padding-y-lg) var(--basic-button-padding-x-lg); font-size: var(--font-size-base); }
  /* Primary — the accent CTA. Budget law (IDENTITY-BRIEF §4.2): at most one
     in-content use per page; sticky conversion chrome counts as chrome. */
  .btn--primary { background: var(--t-accent); color: var(--t-text-on-accent); border-color: var(--t-accent); }
  .btn--primary:hover:not(:disabled) { background: var(--t-accent-strong); border-color: var(--t-accent-strong); }
  .btn--primary:disabled { opacity: 0.5; cursor: not-allowed; }
  /* Ghost — accent outline; text draws from the AA-passing eyebrow step */
  .btn--ghost { background: transparent; color: var(--t-eyebrow-text); border-color: var(--t-eyebrow-text); }
  .btn--ghost:hover:not(:disabled) { background: color-mix(in srgb, var(--t-accent) var(--opacity-10), transparent); }
  .btn--ghost:disabled { opacity: 0.45; cursor: not-allowed; }
  /* Secondary (hub/gold) */
  .btn--secondary { background: var(--t-accent-2); color: var(--t-text-on-accent); border-color: var(--t-accent-2); }
  .btn--secondary:hover:not(:disabled) { background: var(--t-accent-2-strong); border-color: var(--t-accent-2-strong); }
  .btn--secondary-ghost { background: transparent; color: var(--t-accent-2-strong); border-color: var(--t-accent-2-strong); }
  .btn--secondary-ghost:hover { background: color-mix(in srgb, var(--t-accent-2) var(--opacity-10), transparent); }
  /* Outline inverse (for dark rows) */
  .btn--outline-inverse { background: transparent; color: var(--t-text-inv); border-color: color-mix(in srgb, var(--base-white) var(--opacity-50), transparent); }
  .btn--outline-inverse:hover { border-color: var(--t-text-inv); background: color-mix(in srgb, var(--base-white) var(--opacity-10), transparent); }
  /* ── TAG ─────────────────────────────────────────────────────────────────── */
  /* Marker family — radius-marker (10) from the round register; the pill
     variant is pruned with the buttons'. Tags speak the eyebrow language:
     IBM Plex Mono tracked caps (authkit dotDigital pattern), never above
     weight 500 (the mono has no heavier face). */
  .tag {
    display: inline-flex; align-items: center; gap: var(--basic-tag-gap-sm);
    padding: var(--basic-tag-padding-y) var(--basic-tag-padding-x);
    border-radius: var(--radius-marker);
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs);
    font-weight: var(--font-medium); text-transform: var(--case-eyebrow);
    letter-spacing: var(--tracking-wide);
    white-space: nowrap; border: var(--basic-tag-stroke-sm) solid transparent;
    transition: background var(--dur-micro) var(--ease-out),
                color var(--dur-micro) var(--ease-out),
                border-color var(--dur-micro) var(--ease-out);
  }
  .tag--sm   { padding: var(--basic-tag-padding-y-sm) var(--basic-tag-padding-x-sm); }
  .tag--accent    { background: var(--t-accent);         color: var(--t-text-on-accent); border-color: var(--t-accent); }
  .tag--soft      { background: var(--t-tag-soft-bg);   color: var(--t-tag-soft-text);  border-color: var(--t-tag-soft-border); }
  .tag--secondary { background: var(--t-tag-2-bg);      color: var(--t-tag-2-text);     border-color: var(--t-tag-2-border); }
  .tag--neutral   { background: var(--background-tag-neutral-soft); color: var(--t-text-body); border-color: var(--t-border); }
  /* ── LOGO ────────────────────────────────────────────────────────────────── */
  .logo-img {
    width: auto; object-fit: contain; display: block;
    /* Both logo files are RGBA — transparent bg, use natively on any surface */
  }
  .logo-img--lg { height: var(--size-24); }
  .logo-img--md { height: var(--size-16); }
  .logo-img--sm { height: var(--size-10); }
  .logo-img--xs { height: var(--size-6); }
  /* Logo row — the institutions or partners a card names (George, 2026-09-29,
     academy /programs/education-pathway). The files are square and carry their
     own padding, so the gap stays small. Each logo may SHRINK: up to three sit
     at 96px, and on a narrow card (the 281px mobile carousel) all of them shrink
     together rather than one wrapping onto a line of its own. On a pricing card
     the row closes the card, on its foot, so rows line up across a grid. */
  .logo-row { display: flex; align-items: center; gap: var(--size-1); }
  .logo-row > img {
    flex: 0 1 var(--size-24); min-width: 0; width: var(--size-24);
    height: auto; aspect-ratio: 1 / 1; object-fit: contain; display: block;
  }
  .pricing-card > .logo-row { margin-top: auto; padding-top: var(--size-3); }
  /* ── BADGE ───────────────────────────────────────────────────────────────── */
  /* Status dot */
  .badge-dot-group { display: flex; flex-direction: column; align-items: center; gap: var(--size-1); }
  .badge-dot { width: var(--size-2p5); height: var(--size-2p5); border-radius: 50%; display: inline-block; }
  .badge-dot--online  { background: var(--informative-success-500); box-shadow: 0 0 0 var(--size-0p5) color-mix(in srgb, var(--informative-success-500) 20%, transparent); }
  .badge-dot--away    { background: var(--informative-warning-500); box-shadow: 0 0 0 var(--size-0p5) color-mix(in srgb, var(--informative-warning-500) 20%, transparent); }
  .badge-dot--offline { background: var(--gray-cool-500); box-shadow: 0 0 0 var(--size-0p5) color-mix(in srgb, var(--gray-cool-500) 20%, transparent); }
  .badge-dot--accent  { background: var(--t-accent); box-shadow: 0 0 0 var(--size-0p5) color-mix(in srgb, var(--t-accent) 20%, transparent); }
  .badge-sublabel { font-size: var(--font-size-xs); font-weight: var(--font-smibold); text-transform: uppercase; letter-spacing: var(--tracking-wide); color: var(--t-text-caption); }
  /* Count badge — the one marker that stays fully round (a count badge is a
     circle by nature; the G1 note reserves pill for the marker family only).
     Mono numerals at 500 — the mono ships no bold face. */
  .badge {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: var(--size-5); height: var(--size-5); padding: 0 var(--size-1); border-radius: var(--radius-pill);
    font-family: var(--font-family-number); font-size: var(--font-size-xs); font-weight: var(--font-medium); line-height: var(--lh-none);
    white-space: nowrap;
  }
  .badge--xs { min-width: var(--size-4); height: var(--size-4); font-size: var(--font-size-xs); padding: 0 var(--size-1); }
  .badge--accent   { background: var(--t-accent);   color: var(--t-text-on-accent); }
  .badge--neutral  { background: var(--t-surface-alt); color: var(--t-text-body); border: var(--stroke-xs) solid var(--t-border); }
  .badge--neutral-inverse { background: color-mix(in srgb, var(--base-white) var(--opacity-10), transparent); color: var(--t-text-inv); }
  .badge--secondary { background: var(--t-accent-2); color: var(--t-text-on-accent); }
  /* Badge + icon wrap */
  .badge-icon-wrap { position: relative; display: inline-flex; }
  .badge-icon-wrap .badge { position: absolute; top: calc(-1 * var(--size-1)); right: calc(-1 * var(--size-1p5)); }
  .icon-btn {
    width: var(--size-9); height: var(--size-9); border-radius: var(--radius-control);
    background: var(--t-surface-alt); border: var(--stroke-xs) solid var(--t-border);
    display: flex; align-items: center; justify-content: center;
    color: var(--t-text-body); cursor: pointer;
    transition: background var(--dur-micro);
  }
  .icon-btn:hover { background: var(--t-border); }
  .icon-btn--inverse { background: color-mix(in srgb, var(--base-white) var(--opacity-10), transparent); border-color: color-mix(in srgb, var(--base-white) var(--opacity-10), transparent); color: var(--t-text-inv); }
  /* ── COUNTRY BADGE ───────────────────────────────────────────────────────── */
  .country-badge {
    display: inline-flex; align-items: center; gap: var(--size-1);
    padding: var(--size-1) var(--size-2); border-radius: var(--radius-marker);
    background: var(--t-surface-alt); border: var(--stroke-xs) solid var(--t-border);
    font-family: var(--font-family-label); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    color: var(--t-text-body); white-space: nowrap;
    transition: background var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
  }
  .country-badge--flag-only { padding: var(--size-1) var(--size-1p5); gap: 0; }
  .country-badge--inverse { background: color-mix(in srgb, var(--base-white) var(--opacity-10), transparent); border-color: color-mix(in srgb, var(--base-white) var(--opacity-10), transparent); color: var(--t-text-inv); }
  .country-badge__flag { font-size: var(--font-size-sm); line-height: var(--lh-none); }
  .country-badge__name { font-size: var(--font-size-xs); }
  /* ── ICON ────────────────────────────────────────────────────────────────── */
  /* Geometry comes from styles/icons.js; presentation comes from here, and
     from nowhere else. Before this block, every call site carried its own
     fill/stroke/stroke-width attributes, and the estate ran three different
     stroke weights for the same glyph — 1.75, 2 and 2.5, with a stray 3 —
     visible together on all 22 pages because the nav and footer disagreed.

     Stroke weight is ONE value for the whole set, held by --icon-stroke, and
     it holds because of vector-effect: non-scaling-stroke. Without that, a
     glyph on a 24-unit canvas scaled to 12px renders its stroke at half
     weight, so keeping one optical weight would need a per-size ramp — and a
     ramp keyed on the SIZE CLASS silently breaks whenever component CSS
     resizes an icon without changing its class, which is exactly what the nav
     language switcher does: .sec-nav__lang-icon and __lang-caret are marked
     icon--20 and render at 16px and 12px. non-scaling-stroke measures the
     stroke in rendered pixels instead, so the weight is right at every size,
     at any zoom, and no matter who resizes the box. */
  /* The one knob for the whole icon set. 1.75 chosen by George 2026-09-03,
     re-judged against Anton rather than against Fraunces: a hairline that
     read as restrained beside a serif reads as thin and unrelated beside a
     heavy condensed grotesque. It is also the weight most of the estate's
     icons already carried before the set was unified. */
  :root { --icon-stroke: 1.75; }
  .icon {
    flex-shrink: 0;
    fill: none;
    stroke: currentColor;
    stroke-width: var(--icon-stroke);
    stroke-linecap: round;
    stroke-linejoin: round;
  }
  .icon * { vector-effect: non-scaling-stroke; }
  .icon--12 { width: var(--size-3); height: var(--size-3); }
  .icon--16 { width: var(--size-4); height: var(--size-4); }
  .icon--20 { width: var(--size-5); height: var(--size-5); }
  .icon--24 { width: var(--size-6); height: var(--size-6); }
  .icon--32 { width: var(--size-8); height: var(--size-8); }
  /* A brand mark is a filled logotype, not a stroked pictogram: Instagram's
     lens dot and YouTube's play triangle are solid by definition. They opt out
     rather than each call site re-declaring fill on the affected child. */
  .icon--mark [fill="currentColor"] { stroke: none; }
  .icon--accent   { color: var(--t-accent);       }
  .icon--body     { color: var(--t-text-body);    }
  .icon--caption  { color: var(--t-text-caption); }
  .icon--secondary { color: var(--t-accent-2);    }
  .icon-tile {
    display: flex; flex-direction: column; align-items: center; gap: var(--size-1);
    padding: var(--size-2p5) var(--size-2); border-radius: var(--size-1p5); cursor: default;
    color: var(--t-text-body);
    border: var(--stroke-xs) solid transparent;
    transition: background var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
    min-width: var(--size-16);
  }
  .icon-tile:hover { background: var(--t-accent-soft); color: var(--t-accent-strong); border-color: var(--t-accent-medium); }
  .icon-tile__name { font-size: var(--font-size-xs); font-weight: var(--font-medium); color: var(--t-text-caption); text-align: center; line-height: var(--lh-md); }
  .icon-tile--inverse { padding: var(--size-2p5); min-width: auto; color: var(--t-text-inv-body); }
  .icon-tile--inverse:hover { background: color-mix(in srgb, var(--base-white) var(--opacity-10), transparent); border-color: color-mix(in srgb, var(--base-white) var(--opacity-25), transparent); color: var(--t-text-inv); }
  /* ── StatsBlock ─────────────────────────────────────────────────────────── */
  /* Stat-numeral style (IDENTITY-BRIEF §3.3/§5): big tabular mono numerals +
     one tracked-caps label. First-class role — reused by pricing, tables, heroes. */
  /* `align-items: center` centres each child's BOX; it does not centre the lines
     INSIDE a box that wrapped. A two-line value ("Year-Round" breaking at its
     hyphen in a 162px card on a phone) therefore rendered centred as a block but
     ragged-left as text, which reads as a broken alignment. `text-align` is what
     centres the lines. */
  .stat-block {
    display: flex; flex-direction: column; align-items: center; gap: var(--size-1);
    text-align: center;
    padding: var(--size-5) var(--size-7); min-width: var(--size-24);
    background: var(--t-surface-card); border: var(--stroke-xs) solid var(--t-border);
    border-radius: var(--radius-card);
  }
  .stat-block__number {
    font-family: var(--font-family-number); font-size: var(--font-size-5xl); line-height: var(--lh-none);
    font-weight: var(--font-normal); font-variant-numeric: tabular-nums;
    letter-spacing: var(--tracking-tight);
    color: var(--t-text); transition: color var(--dur-micro);
  }
  /* ⚠ THE LABEL WRAPS. It was `white-space: nowrap` until 2026-09-12, on the
     reasoning recorded in the 640px block below: at one-up the block simply grows,
     so nowrap was invisible. THAT IS ONLY TRUE WHERE THE BLOCK CAN GROW. In a
     stat ROW it cannot — the blocks share the width — so a label longer than its
     column runs straight out of the card at DESKTOP, which is where George found
     it on 2026-09-12: the Spanish home's proof row renders "Incorporaciones a
     universidades de EE. UU." at 336px inside a 276px block and spills 60px past
     the edge. English "US College Placements" fits, which is why a year of
     desktop review never saw it.

     There is no width at which nowrap is right for a multi-word caption, so it is
     gone rather than overridden again; the block already centers its text for the
     wrapped case. Short labels are unaffected — wrapping only engages when the
     text does not fit. */
  .stat-block__label {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wide);
    color: var(--t-text-caption); transition: color var(--dur-micro);
  }
  .stat-block--inverse {
    background: color-mix(in srgb, var(--base-white) var(--opacity-5), transparent); border-color: color-mix(in srgb, var(--base-white) var(--opacity-10), transparent);
  }
  .stat-block--inverse .stat-block__number { color: var(--t-text-inv); }
  .stat-block--inverse .stat-block__label  { color: var(--t-text-inv-caption); }
  /* Accented — number in accent text (theme-flipping), label stays neutral */
  .stat-block--accented .stat-block__number { color: var(--t-text-accent-strong); }
  /* ── SectionHeader ──────────────────────────────────────────────────────── */
  /* 920px, not 540 (George, 2026-09-24 — "it only occupies maybe 30% of the
     screen width... set to max of 60% or 70%"). At 1440 this is ~64% of the
     viewport and ~73% of a section's 1265px inner, which is what stops a
     centred heading breaking into two lines for the sake of one short word
     ("What the Application Asks / For" on academy /apply).
     It widens the HEADING only: __sub and __body carry their own 640px cap, so
     the measure of the reading line is unchanged. */
  .section-hdr {
    padding: var(--size-5) 0; max-width: 920px;
    display: flex; flex-direction: column; gap: var(--size-2p5);
  }
  /* Section-opening skeleton (IDENTITY-BRIEF §6): tracked-caps mono eyebrow in
     the AA-passing accent step → weight-disciplined Fraunces display, mixed
     case, never above ~500 → one short muted body line. */
  .section-hdr__eyebrow {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wider); color: var(--t-eyebrow-text);
    transition: color var(--dur-micro);
  }
  .section-hdr__heading {
    font-family: var(--font-family-display); font-size: var(--font-size-6xl); line-height: var(--lh-xs);
    font-weight: var(--font-normal); letter-spacing: var(--tracking-tight);
    text-transform: var(--case-header); color: var(--t-text); margin: 0; transition: color var(--dur-micro);
  }
  /* The optional line BETWEEN the heading and the body (George, 2026-09-16).
     Hub headings were cut to E-Ture's length — at most four words — and the
     sentence each one used to be moves here rather than being lost: same body
     face, one step up from the body and in the full text color, so it reads as
     the second half of the heading rather than as a first paragraph. */
  .section-hdr__sub {
    font-family: var(--font-family-body); font-size: var(--font-size-lg); line-height: var(--lh-lg);
    color: var(--t-text); margin: 0; max-width: 640px; transition: color var(--dur-micro);
  }
  .section-hdr__body {
    font-family: var(--font-family-body); font-size: var(--font-size-base); line-height: var(--lh-xl);
    color: var(--t-text-body); margin: 0; max-width: 640px; transition: color var(--dur-micro);
  }
  .section-hdr--center { align-items: center; text-align: center; align-self: center; }
  /* An action directly under the heading (George, 2026-09-18, academy /staff:
     "What this department does" moved up from under the card grid). */
  .section-hdr__ctas { display: flex; gap: var(--size-3); flex-wrap: wrap; margin-top: var(--size-1); }
  .section-hdr--center .section-hdr__ctas { justify-content: center; }
  .section-hdr--inverse .section-hdr__heading { color: var(--t-text-inv); }
  .section-hdr--inverse .section-hdr__sub     { color: var(--t-text-inv); }
  .section-hdr--inverse .section-hdr__body    { color: var(--t-text-inv-body); }
  .section-hdr--inverse .section-hdr__eyebrow { color: var(--text-accent-soft); }
  /* ── QuoteBlock ─────────────────────────────────────────────────────────── */
  .quote-block {
    padding: var(--size-9) var(--size-8); border-radius: var(--radius-card); flex: 1; min-width: 220px;
    display: flex; flex-direction: column; gap: var(--size-4);
    /* Pinned dark — a statement card, not a contrast-flipping surface */
    background: var(--gray-warm-900);
  }
  .quote-block__mark {
    font-family: var(--font-family-display); font-size: var(--font-size-8xl); line-height: 0.7;
    font-weight: var(--font-normal); color: var(--t-text-accent-inv); display: block;
  }
  /* The quote itself is the serif's moment — Fraunces mixed case at reading size */
  .quote-block__text {
    font-family: var(--font-family-heading); font-size: var(--font-size-lg); line-height: var(--lh-lg);
    font-weight: var(--font-smibold); letter-spacing: var(--tracking-normal);
    color: var(--t-text-inv); margin: 0;
  }
  .quote-block__author { display: flex; flex-direction: column; gap: var(--size-0p5); margin-top: var(--size-1); }
  .quote-block__name {
    font-family: var(--font-family-label); font-size: var(--font-size-xs); font-weight: var(--font-bold);
    color: var(--t-text-inv); letter-spacing: var(--tracking-normal);
  }
  .quote-block__role {
    font-family: var(--font-family-label); font-size: var(--font-size-xs); color: var(--t-text-inv-caption);
  }
  /* --soft: the light card a quote takes inside .sec-testimonial (the academy
     /alumni stories), for a quote that stands alone in a page section. The base
     card is pinned dark as a statement; beside a paragraph and a photograph that
     reads as a heavy block (George, 2026-10-02, /departments/human-performance:
     "the full dark background stands out too much"). The attribution already
     defaults to body text, so only the surface, the quote and the mark move.
     Do not add a theme-*-dark class alongside it. */
  .quote-block--soft { background: var(--t-card-dark); }
  .quote-block--soft .quote-block__text { color: var(--t-text-body); }
  .quote-block--soft .quote-block__mark { color: var(--t-eyebrow-text); }
  .quote-block--accent { background: var(--t-accent-darker); }
  .quote-block--accent .quote-block__mark { color: var(--t-accent-medium); }
  /* Accent variant: author name in soft accent — light and airy on dark bg */
  .quote-block--accent .quote-block__name { color: var(--t-accent-soft); }
  .quote-block--accent .quote-block__role { color: var(--t-accent-medium); }
  /* Portrait variation — author photo + long-form readable message (welcome letter,
     testimonial-with-photo). Pairs with the DocIntroBanner to compose an intro page. */
  .quote-block--portrait { flex-direction: row; gap: var(--size-7); padding: var(--size-7); align-items: stretch; }
  .quote-block__portrait {
    flex: 0 0 32%; min-width: 150px; align-self: stretch; min-height: 220px;
    border-radius: var(--size-2); position: relative; overflow: hidden;
    background: linear-gradient(135deg, var(--t-accent-strong) 0%, var(--t-surface-inv) 100%);
  }
  .quote-block__portrait::after {
    content: attr(data-label); position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    color: color-mix(in srgb, var(--base-white) var(--opacity-50), transparent); font-family: var(--font-family-label);
    font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: var(--tracking-wide);
  }
  .quote-block__content { flex: 1; display: flex; flex-direction: column; gap: var(--size-3); }
  /* Readable long-form message (overrides the default uppercase display quote) */
  .quote-block__text--message {
    font-family: var(--font-family-body); font-size: var(--font-size-sm);
    text-transform: none; letter-spacing: var(--tracking-normal); line-height: var(--lh-xl);
  }
  /* ── ReadingPanel — the information surface (IDENTITY-BRIEF §6) ──────────── */
  /* Dense information for parents comparing fees, dates, curricula — exempt
     from the one-message-per-viewport law. Pinned CREAM whatever the theme
     mode: Newsreader's trust register is licensed on light panels only (§4.4),
     so the panel does not flip dark. Surface radius (28) — it is a surface,
     not a card. */
  .reading-panel {
    background: var(--gray-warm-100);
    border: var(--stroke-xs) solid var(--gray-warm-200);
    border-radius: var(--radius-surface);
    padding: var(--size-8) var(--size-9);
    max-width: 720px; width: 100%;
    display: flex; flex-direction: column; gap: var(--size-4);
  }
  .reading-panel__eyebrow {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wider);
    color: var(--t-accent);
  }
  .reading-panel__title {
    font-family: var(--font-family-heading); font-size: var(--font-size-3xl); line-height: var(--lh-sm);
    font-weight: var(--font-smibold); letter-spacing: var(--tracking-tight);
    color: var(--gray-cool-900); margin: 0;
  }
  .reading-panel__body {
    font-family: var(--font-family-reading); font-size: var(--font-size-lg); line-height: var(--lh-xl);
    color: var(--gray-cool-800); margin: 0;
  }
  .reading-panel__facts { display: flex; flex-direction: column; margin: 0; }
  .reading-panel__fact {
    display: grid; grid-template-columns: var(--size-32) 1fr; gap: var(--size-4);
    padding: var(--size-2p5) 0;
    border-top: var(--stroke-xs) solid var(--gray-warm-200);
  }
  .reading-panel__fact dt {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wide);
    color: var(--gray-cool-500); align-self: baseline; padding-top: var(--size-0p5);
  }
  .reading-panel__fact dd {
    font-family: var(--font-family-reading); font-size: var(--font-size-base); line-height: var(--lh-lg);
    color: var(--gray-cool-800); margin: 0;
  }
  /* ── LegalDoc — long-form document surface (IDENTITY-BRIEF §6) ───────────── */
  /* The information surface at document length: privacy policy, terms, conditions
     of sale. Same register as .reading-panel — cream, pinned light whatever the
     theme, Newsreader as the reading face (§4.4 licenses it on light panels only)
     — but sized for continuous prose rather than a fact list, so it carries its own
     heading rhythm, list, link and table styling. Exempt from the
     one-message-per-viewport law, like every reading panel. */
  .sec-legal {
    background: var(--t-surface);
    padding: var(--size-24) var(--size-20);
  }
  /* ⚠ THE COLUMN *IS* THE MEASURE (George, 2026-09-11: "it seems like there is an
     invisible container limiting the width of the text — it is weirdly aligned to
     the left"). It was 900px holding paragraphs individually capped at 68ch, so the
     prose sat against the left edge of a column a third wider than itself while the
     h2 rules ran the full 900 — an invisible container, exactly as described, and
     the rules were what made it visible. Now one column at reading width: heading,
     meta, rules and prose share both edges and the block centres on the page. The
     per-element caps came off with this; two measures in one document is what
     produced the ragged right in the first place. */
  .sec-legal__inner {
    max-width: 44rem;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: var(--size-8);
  }
  .sec-legal__header {
    display: flex;
    flex-direction: column;
    gap: var(--size-3);
  }
  .sec-legal__eyebrow {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs);
    font-weight: var(--font-medium); text-transform: var(--case-eyebrow);
    letter-spacing: var(--tracking-wider); color: var(--t-accent);
  }
  .sec-legal__title {
    font-family: var(--font-family-display); font-size: var(--font-size-6xl);
    font-weight: var(--font-normal); line-height: var(--lh-sm);
    letter-spacing: var(--tracking-tight); color: var(--t-text); margin: 0;
  }
  .sec-legal__meta {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs);
    letter-spacing: var(--tracking-wide); text-transform: var(--case-eyebrow);
    color: var(--t-text-caption);
  }
  /* The document body itself — NO PANEL (George, 2026-09-11: "it's weird that the
     body content is inside a gray long card… these pages are legal pages, it should
     be plain and highly legible more than anything else"). It was a cream card with
     a border and a radius; a legal document is not a card, and the chrome was
     competing with the only thing these pages exist to do. What carries the reading
     now is the measure (68ch, set on the block elements), the leading, and a step up
     in size — 18px of the reading serif rather than 16 — on the page's own ground.
     The section around it supplies the gutters, so this carries no padding at all. */
  .legal-doc {
    /* Reading measure: design-review §6 wants 45-75ch of prose. */
    font-family: var(--font-family-reading);
    font-size: var(--font-size-lg);
    line-height: var(--lh-xl);
    color: var(--t-text);
  }
  .legal-doc > *:first-child { margin-top: 0; }
  .legal-doc > *:last-child { margin-bottom: 0; }
  /* The markdown carries its own <h1>; the page furniture already shows the title,
     so the generator drops it. h2 is the top level inside the document. */
  .legal-doc h2 {
    font-family: var(--font-family-heading); font-size: var(--font-size-2xl);
    font-weight: var(--font-smibold); line-height: var(--lh-sm);
    letter-spacing: var(--tracking-tight); color: var(--gray-cool-900);
    margin: var(--size-10) 0 var(--size-3);
    padding-top: var(--size-6);
    border-top: var(--stroke-xs) solid var(--gray-warm-200);
  }
  .legal-doc h2:first-of-type { border-top: none; padding-top: 0; margin-top: 0; }
  .legal-doc h3 {
    font-family: var(--font-family-body); font-size: var(--font-size-lg);
    font-weight: var(--font-medium); color: var(--gray-cool-900);
    margin: var(--size-6) 0 var(--size-2);
  }
  .legal-doc p { margin: 0 0 var(--size-4); }
  .legal-doc strong { color: var(--gray-cool-900); font-weight: var(--font-medium); }
  .legal-doc a {
    color: var(--text-accent-strong);
    text-decoration: underline; text-underline-offset: var(--size-0p5);
    transition: color var(--dur-micro) var(--ease-out);
  }
  .legal-doc a:hover { color: var(--t-accent); }
  .legal-doc ul, .legal-doc ol { margin: 0 0 var(--size-4); padding-left: var(--size-5); }
  .legal-doc li { margin: var(--size-1p5) 0; }
  .legal-doc li::marker { color: var(--t-accent); }
  .legal-doc hr { border: 0; border-top: var(--stroke-xs) solid var(--gray-warm-200); margin: var(--size-8) 0; }
  /* Tables scroll inside their own container rather than widening the page —
     the legal-bases table is the one wide element in these documents. */
  .legal-doc__tablewrap { overflow-x: auto; margin: 0 0 var(--size-5); }
  .legal-doc table {
    border-collapse: collapse; width: 100%; min-width: 32rem;
    font-size: var(--font-size-sm);
  }
  .legal-doc th {
    text-align: left; background: var(--gray-warm-200); color: var(--gray-cool-900);
    font-family: var(--font-family-body); font-weight: var(--font-medium);
    padding: var(--size-2p5) var(--size-3);
    border: var(--stroke-xs) solid var(--gray-warm-200);
  }
  .legal-doc td {
    padding: var(--size-2p5) var(--size-3); vertical-align: top;
    border: var(--stroke-xs) solid var(--gray-warm-200);
  }
  /* Any placeholder that survives into a published page must be impossible to miss. */
  .legal-doc__todo {
    background: var(--background-warning-soft);
    color: var(--informative-warning-800);
    border: var(--stroke-xs) solid var(--border-warning-default);
    border-radius: var(--radius-xs); padding: 0 var(--size-1); font-weight: var(--font-medium);
  }
  /* (The phone rule that tightened .legal-doc's padding went with the panel — it has
     no padding of its own now, and .sec-legal already narrows its gutters here.) */

  /* ── PillarCard ─────────────────────────────────────────────────────────── */
  .pillar-card {
    display: flex; flex-direction: column; gap: var(--size-2p5);
    padding: var(--size-6) var(--size-5); border-radius: var(--radius-card); flex: 1; min-width: 180px;
    background: var(--background-card-softer); border: var(--stroke-xs) solid var(--border-card-default);
    transition: background var(--dur-micro), border-color var(--dur-micro);
  }
  .pillar-card__icon {
    width: var(--size-9); height: var(--size-9); border-radius: var(--radius-marker); flex-shrink: 0;
    background: var(--background-card-icon-softer); color: var(--text-card-icon-softer);
    display: flex; align-items: center; justify-content: center;
    transition: background var(--dur-micro), color var(--dur-micro);
  }
  .pillar-card__title {
    font-family: var(--font-family-heading); font-size: var(--font-size-xl); line-height: var(--lh-sm);
    font-weight: var(--font-smibold); letter-spacing: var(--tracking-normal);
    text-transform: var(--case-header); color: var(--t-text); margin: 0; transition: color var(--dur-micro);
  }
  .pillar-card__body {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); line-height: var(--lh-xl);
    color: var(--t-text-body); margin: 0; transition: color var(--dur-micro);
  }
  /* A price inside a pillar card — the number role, like .pricing-card__price,
     so a fee is set in the site's numeral face rather than as bold body text. */
  .pillar-card__price {
    font-family: var(--font-family-number); font-size: var(--font-size-4xl); line-height: var(--lh-none);
    font-weight: var(--font-normal); font-variant-numeric: tabular-nums;
    color: var(--t-text); margin: 0; transition: color var(--dur-micro);
  }
  .pillar-card--dark {
    background: var(--background-card-stronger); border-color: var(--border-card-strong);
  }
  .pillar-card--dark .pillar-card__icon {
    background: var(--background-card-icon-strong); color: var(--text-card-icon-strong);
  }
  .pillar-card--dark .pillar-card__title { color: var(--t-text-inv); }
  .pillar-card--dark .pillar-card__body  { color: var(--t-text-inv-body); }
  /* Photo card (George, 2026-09-17, academy teams): the photograph fills the
     card, and the title and its tag sit on it at the foot. Built like
     .sec-feature--photo: a pinned dark ground under the image, so a slow or
     missing photograph leaves a dark card rather than a white hole, and the
     title is white in every theme. `isolation` keeps the image layer's
     negative z-index inside the card. */
  .pillar-card--photo {
    position: relative; isolation: isolate; overflow: hidden;
    justify-content: flex-end; aspect-ratio: 3 / 4;
    background: var(--gray-warm-900); border-color: transparent;
  }
  .pillar-card__bg {
    position: absolute; inset: 0; z-index: -1;
    background-size: cover; background-position: center;
  }
  .pillar-card__bg::after {
    content: '';
    position: absolute; inset: 0;
    background: linear-gradient(
      180deg,
      transparent 40%,
      color-mix(in srgb, var(--gray-warm-900) 88%, transparent) 100%
    );
  }
  .pillar-card--photo .pillar-card__title { color: var(--base-white); }
  /* A tag is a marker, as a badge is (see below). */
  .pillar-card--photo > .tag { align-self: flex-start; }
  /* A badge is a MARKER, not a band. `.feature-card` and `.pillar-card` are flex
     columns, whose default `align-items: stretch` made a badge child span the
     whole card — a "Step 1" tag rendered as a full-width bar. It sizes to its
     own content instead. */
  .feature-card > .badge,
  .pillar-card > .badge { align-self: flex-start; }
  /* ── VenueCard ──────────────────────────────────────────────────────────── */
  .venue-card {
    border-radius: var(--radius-card); overflow: hidden; flex: 1; min-width: 200px;
    background: var(--t-surface-card); border: var(--stroke-xs) solid var(--t-border);
    display: flex; flex-direction: column;
    transition: background var(--dur-micro), border-color var(--dur-micro);
  }
  .venue-card__media {
    position: relative; height: 140px; flex-shrink: 0; overflow: hidden;
    background: linear-gradient(135deg, var(--t-surface-inv) 0%, var(--t-accent-strong) 100%);
  }
  /* Card imagery: slow scale inside overflow:hidden — the default media behavior */
  .venue-card__media > img { transition: transform var(--dur-sheen) var(--ease-out); }
  .venue-card:hover .venue-card__media > img { transform: scale(1.05); }
  .venue-card__badge { position: absolute; top: var(--size-3); left: var(--size-3); }
  /* flex: 1 so the body fills a card stretched to its row's height, which is
     what lets `.venue-card__actions` sit on the bottom edge.
     NOTE: the media band needs no height modifier for a grid card — the
     aspect-ratio rule further down already resolves a 3-up card to 231px. */
  .venue-card__body  { padding: var(--size-4) var(--size-5); display: flex; flex-direction: column; gap: var(--size-2); flex: 1; }
  .venue-card__title {
    font-family: var(--font-family-heading); font-size: var(--font-size-xl); line-height: var(--lh-sm);
    font-weight: var(--font-smibold); letter-spacing: var(--tracking-normal);
    text-transform: var(--case-header); color: var(--t-text); margin: 0; transition: color var(--dur-micro);
  }
  .venue-card__desc {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); line-height: var(--lh-xl);
    color: var(--t-text-body); margin: 0; transition: color var(--dur-micro);
  }
  .venue-card__tags { display: flex; gap: var(--size-1p5); flex-wrap: wrap; }
  /* Horizontal variation — image + text side by side (documents / brochure venue rows) */
  .venue-card--horizontal { flex-direction: row; align-items: stretch; }
  .venue-card--horizontal .venue-card__media { height: auto; width: 38%; min-width: 140px; flex-shrink: 0; align-self: stretch; }
  /* min-width: 0 — without it a flex item cannot shrink below its min-content
     width, so a long title/description pushed the body out past the card edge
     (measured 31px outside on the --reverse card at 375px). */
  .venue-card--horizontal .venue-card__body { flex: 1; min-width: 0; justify-content: center; }
  .venue-card--horizontal.venue-card--reverse { flex-direction: row-reverse; }
  /* Linked — the whole card is one link to its own page, on a venue card or a
     feature card (the academy's "Other Pathways" cards, George, 2026-09-30:
     "Learn More" alone was not evident at first glance). The real anchor is on
     the TITLE and its hit area is stretched over the card by the `::after`,
     rather than the card being wrapped in an `<a>`: a link may not contain
     links, and these cards carry links (the audience buttons) inside them.
     Anything that must stay separately clickable goes in `.venue-card__actions`,
     which is raised above the stretch.

     The tradeoff is that text in a stretched-link card cannot be selected. That
     is acceptable on a browse card and unacceptable on a content one, which is
     exactly why this is a modifier and not the base. */
  .venue-card--linked, .feature-card--linked { position: relative; cursor: pointer; }
  .venue-card__link, .feature-card__link { color: inherit; text-decoration: none; }
  .venue-card__link::after, .feature-card__link::after { content: ''; position: absolute; inset: 0; }
  /* The ring belongs to the card, because the card is what the link targets —
     but only for the stretched link. `:focus-within` would draw it around the
     whole card when a button inside is focused, which points at the wrong thing. */
  .venue-card--linked:has(.venue-card__link:focus-visible),
  .feature-card--linked:has(.feature-card__link:focus-visible) {
    outline: var(--stroke-sm) solid var(--border-focus); outline-offset: var(--size-0p5);
  }
  /* Actions sit on the bottom edge whatever the text above them measures, so a
     row of cards keeps its buttons on one line. `z-index` lifts them above the
     stretched link; without it they are covered by it and cannot be clicked. */
  .venue-card__actions {
    display: flex; flex-wrap: wrap; gap: var(--size-2);
    margin-top: auto; padding-top: var(--size-3);
    position: relative; z-index: 1;
  }
  /* ── FeatureCard ────────────────────────────────────────────────────────── */
  .feature-card {
    display: flex; flex-direction: column; gap: var(--size-3);
    padding: var(--size-7) var(--size-5); border-radius: var(--radius-card); flex: 1; min-width: 200px;
    background: var(--background-card-softer); border: var(--stroke-xs) solid var(--border-card-default);
    transition: background var(--dur-micro), border-color var(--dur-micro);
  }
  /* Icon replaces badge — same flipping tile style as PillarCard */
  .feature-card__icon {
    width: var(--size-11); height: var(--size-11); border-radius: var(--radius-marker); flex-shrink: 0; align-self: flex-start;
    background: var(--background-card-icon-softer); color: var(--text-card-icon-softer);
    display: flex; align-items: center; justify-content: center;
    transition: background var(--dur-micro), color var(--dur-micro);
  }
  .feature-card__icon--dark    { background: var(--background-card-icon-strong); color: var(--text-card-icon-strong); }
  .feature-card__icon--accent  { background: color-mix(in srgb, var(--base-white) var(--opacity-25), transparent); color: var(--base-white); }
  .feature-card__title {
    font-family: var(--font-family-heading); font-size: var(--font-size-2xl); line-height: var(--lh-sm);
    font-weight: var(--font-smibold); letter-spacing: var(--tracking-normal);
    text-transform: var(--case-header); color: var(--t-text); margin: 0; transition: color var(--dur-micro);
  }
  .feature-card__body {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); line-height: var(--lh-xl);
    color: var(--t-text-body); margin: 0; flex: 1; transition: color var(--dur-micro);
  }
  .feature-card__cta { margin-top: var(--size-1); }
  /* On a linked card the cue is text, not a second link (the title is the link,
     and a second "Learn More" link would be read out twice). It underlines with
     the card's hover, so the pointer shows the whole card is the target. */
  .feature-card--linked .feature-card__cta { text-decoration: underline; text-decoration-color: transparent; text-underline-offset: var(--size-1); transition: text-decoration-color var(--dur-micro); }
  .feature-card--linked:hover .feature-card__cta { text-decoration-color: currentColor; }
  .feature-card--dark {
    background: var(--background-card-stronger); border-color: var(--border-card-strong);
  }
  .feature-card--dark .feature-card__title { color: var(--t-text-inv); }
  .feature-card--dark .feature-card__body  { color: var(--t-text-inv-body); }
  .feature-card--accent {
    background: var(--t-accent); border-color: var(--t-accent-strong);
    /* The accent card always has a teal background regardless of mode.
       Override --t-text-inv so btn--outline-inverse (which uses --t-text-inv for its text)
       always renders as white — the semantic token for text on accent surfaces. */
    --t-text-inv: var(--text-on-accent);
  }
  .feature-card--accent .feature-card__title { color: var(--text-on-accent); }
  .feature-card--accent .feature-card__body  { color: color-mix(in srgb, var(--base-white) var(--opacity-85), transparent); }
  /* ── ItineraryRow ───────────────────────────────────────────────────────── */
  .itinerary-row {
    display: flex; border-radius: var(--radius-card); overflow: hidden;
    background: var(--background-card-softer); border: var(--stroke-xs) solid var(--border-card-default);
    transition: background var(--dur-micro), border-color var(--dur-micro);
  }
  .itinerary-row__day {
    flex-shrink: 0; width: var(--size-20);
    display: flex; align-items: center; justify-content: center;
    background: var(--t-surface-alt); border-right: var(--stroke-xs) solid var(--t-border);
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wide);
    color: var(--t-text-caption); padding: var(--size-4) var(--size-2); text-align: center;
    transition: background var(--dur-micro), color var(--dur-micro), border-color var(--dur-micro);
  }
  /* Day label text overrides per row variant */
  .itinerary-row__day--body     { color: var(--t-text-body); }
  .itinerary-row__day--body-inv { color: var(--t-text-inv-body); }
  /* --letter: the column holds one initial, a step's letter in an acronym, not a
     day name (George, 2026-10-02, the P.E.I.R.C. cycle on the academy
     /departments/human-performance: "so that each letter stands out"). The
     display face at heading size, where a day name takes the 12px caps label.
     Pair it with --accent so the column carries the colour too. */
  .itinerary-row__day--letter {
    font-family: var(--font-family-display); font-size: var(--font-size-3xl);
    font-weight: var(--font-normal); line-height: var(--lh-none); letter-spacing: 0;
  }
  .itinerary-row__content {
    padding: var(--size-3p5) var(--size-5); display: flex; flex-direction: column; gap: var(--size-0p5); flex: 1;
  }
  .itinerary-row__title {
    font-family: var(--font-family-heading); font-size: var(--font-size-lg); line-height: var(--lh-md);
    font-weight: var(--font-smibold); letter-spacing: var(--tracking-normal);
    text-transform: var(--case-header); color: var(--t-text); margin: 0; transition: color var(--dur-micro);
  }
  .itinerary-row__meta {
    font-family: var(--font-family-label); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    color: var(--t-eyebrow-text); display: block; margin-top: var(--size-0p5); transition: color var(--dur-micro);
  }
  .itinerary-row__desc {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); line-height: var(--lh-xl);
    color: var(--t-text-body); margin: var(--size-0p5) 0 0; transition: color var(--dur-micro);
  }
  /* Dark variant — card.strong day column, card.stronger content (both flip per mode) */
  .itinerary-row--dark {
    background: var(--background-card-stronger); border-color: var(--border-card-strong);
  }
  .itinerary-row--dark .itinerary-row__day {
    background: var(--background-card-strong); border-right-color: var(--border-card-strong);
    /* color controlled by .itinerary-row__day--body-inv modifier */
  }
  .itinerary-row--dark .itinerary-row__content { background: var(--background-card-stronger); }
  .itinerary-row--dark .itinerary-row__title { color: var(--t-text-inv); }
  .itinerary-row--dark .itinerary-row__meta  { color: var(--text-accent-soft); }
  .itinerary-row--dark .itinerary-row__desc  { color: var(--t-text-inv-body); }
  /* Accent variant — card.softer bg, accent-strong day column */
  .itinerary-row--accent {
    background: var(--background-card-softer); border-color: var(--border-card-default);
  }
  .itinerary-row--accent .itinerary-row__day {
    background: var(--t-accent-strong); border-right-color: var(--t-accent-strong);
    color: var(--t-text-on-accent);
  }
  .itinerary-row--accent .itinerary-row__title { color: var(--t-text); }
  .itinerary-row--accent .itinerary-row__meta  { color: var(--t-eyebrow-text); }
  .itinerary-row--accent .itinerary-row__desc  { color: var(--t-text-body); }
  /* Accent-2 variant — card.softer bg, accent-2-strong day column */
  .itinerary-row--accent-2 {
    background: var(--background-card-softer); border-color: var(--border-card-default);
  }
  .itinerary-row--accent-2 .itinerary-row__day {
    background: var(--t-accent-2-strong); border-right-color: var(--t-accent-2-strong);
    color: var(--t-text-on-accent);
  }
  .itinerary-row--accent-2 .itinerary-row__title { color: var(--t-text); }
  .itinerary-row--accent-2 .itinerary-row__meta  { color: var(--text-tag-accent-2-strong); }
  .itinerary-row--accent-2 .itinerary-row__desc  { color: var(--t-text-body); }
  /* Neutral variant — card.soft day column, page-bg content (arrival / departure) */
  .itinerary-row--neutral { background: var(--background-page); border-color: var(--border-card-default); }
  .itinerary-row--neutral .itinerary-row__day {
    background: var(--background-card-soft); border-right-color: var(--border-card-default); color: var(--t-text-body);
  }
  .itinerary-row--neutral .itinerary-row__title { color: var(--t-text); }
  .itinerary-row--neutral .itinerary-row__meta  { color: var(--t-text-caption); }
  .itinerary-row--neutral .itinerary-row__desc  { color: var(--t-text-body); }
  /* ── Cell — table cell atom ───────────────────────────────────────────────── */
  .cell {
    padding: var(--size-3) var(--size-5);
    font-family: var(--font-family-body); font-size: var(--font-size-sm);
    color: var(--t-text-body); text-align: left; vertical-align: middle;
  }
  /* Header cell — utility label: mono tracked caps (the eyebrow language) */
  .cell--head {
    font-family: var(--font-family-eyebrow); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wide);
    font-size: var(--font-size-xs); color: var(--t-text-caption);
  }
  /* Emphasised row label — the serif voice, mixed case */
  .cell--label  { font-family: var(--font-family-heading); font-weight: var(--font-smibold); letter-spacing: var(--tracking-normal); text-transform: var(--case-header); color: var(--t-text); font-size: var(--font-size-base); }
  /* Price / numeric value — stat-numeral style: tabular mono */
  .cell--price  { font-family: var(--font-family-number); font-weight: var(--font-normal); font-variant-numeric: tabular-nums; color: var(--t-text-accent-strong); }
  .cell--muted  { color: var(--t-text-caption); }
  .cell--right  { text-align: right; }
  .cell--center { text-align: center; }
  /* ── Data Table ───────────────────────────────────────────────────────────── */
  .data-table {
    border: var(--stroke-xs) solid var(--t-border);
    border-radius: var(--radius-card); overflow: hidden;
  }
  .data-table__table { width: 100%; border-collapse: collapse; font-family: var(--font-family-body); }
  .data-table__row  { background: var(--t-surface-card); transition: background var(--dur-micro); }
  /* Header row — recessed value-step + hairline, never a heavy inverse band */
  .data-table__head { background: var(--t-surface-alt); }
  .data-table__head .cell { color: var(--t-text-caption); }
  /* Divider between body rows */
  .data-table__row + .data-table__row { border-top: var(--stroke-xs) solid var(--t-border); }
  /* Row variations */
  .data-table__row--alt       { background: var(--t-surface-alt); }
  .data-table__row--highlight { background: var(--t-tag-soft-bg); }
  .data-table__row--highlight .cell--label { color: var(--t-tag-soft-text); }
  /* ── Pricing Card ─────────────────────────────────────────────────────────── */
  .pricing-card {
    display: flex; flex-direction: column; gap: var(--size-1);
    padding: var(--size-7) var(--size-5); border-radius: var(--radius-card);
    flex: 1; min-width: 180px;
    background: var(--background-card-softer); border: var(--stroke-xs) solid var(--border-card-default);
  }
  .pricing-card__tier {
    font-family: var(--font-family-heading); font-size: var(--font-size-2xl); line-height: var(--lh-sm);
    font-weight: var(--font-smibold); letter-spacing: var(--tracking-normal);
    text-transform: var(--case-header); color: var(--t-text); margin: 0;
  }
  .pricing-card__term { font-family: var(--font-family-body); font-size: var(--font-size-xs); color: var(--t-text-body); margin: 0; }
  /* Prices speak the stat-numeral style (§5.4) — tabular mono, never display serif */
  .pricing-card__price {
    font-family: var(--font-family-number); font-size: var(--font-size-4xl); line-height: var(--lh-none);
    font-weight: var(--font-normal); font-variant-numeric: tabular-nums;
    color: var(--t-text); margin: var(--size-2) 0 0;
  }
  .pricing-card__caption { font-family: var(--font-family-body); font-size: var(--font-size-xs); color: var(--t-text-caption); margin: 0; }
  /* Featured — accent surface */
  .pricing-card--featured { background: var(--t-accent); border-color: var(--t-accent-strong); }
  .pricing-card--featured .pricing-card__tier,
  .pricing-card--featured .pricing-card__price { color: var(--text-on-accent); }
  .pricing-card--featured .pricing-card__term,
  .pricing-card--featured .pricing-card__caption { color: color-mix(in srgb, var(--base-white) var(--opacity-85), transparent); }
  /* ── Feature List ─────────────────────────────────────────────────────────── */
  .feature-list { display: flex; flex-direction: column; gap: var(--size-2); margin: 0; padding: 0; list-style: none; }
  .feature-list__item {
    display: flex; align-items: flex-start; gap: var(--size-2p5);
    font-family: var(--font-family-body); font-size: var(--font-size-sm); line-height: var(--lh-md);
    color: var(--t-text-body);
  }
  .feature-list__icon { flex-shrink: 0; width: var(--size-5); height: var(--size-5); color: var(--t-accent); }
  .feature-list__icon svg { width: 100%; height: 100%; display: block; }
  /* Excluded — muted */
  .feature-list__item--excluded { color: var(--t-text-caption); }
  .feature-list__item--excluded .feature-list__icon { color: var(--t-text-caption); }
  /* Elevated — each item is a card row rather than a line of text, so a list
     of inclusions reads as a set of things you get rather than as a checklist.
     Depth is the velvet register's: a value step + hairline, never a shadow;
     hover is the card lift the rest of the estate uses. The icon sits in the
     same tinted tile as feature-card__icon so the two components read as kin.
     An item may carry a title + note pair (feature-list__body) or plain text. */
  .feature-list--elevated { gap: var(--size-3); }
  .feature-list--elevated .feature-list__item {
    align-items: center; gap: var(--size-4);
    padding: var(--size-3p5) var(--size-4); border-radius: var(--radius-card);
    background: var(--background-card-softer); border: var(--stroke-xs) solid var(--border-card-default);
    font-size: var(--font-size-base); color: var(--t-text);
    transition: transform var(--dur-micro) var(--ease-out), border-color var(--dur-micro), background var(--dur-micro);
  }
  .feature-list--elevated .feature-list__item:hover {
    transform: translateY(calc(-1 * var(--size-0p5)));
    border-color: var(--t-border-strong, var(--border-card-default));
  }
  .feature-list--elevated .feature-list__icon {
    width: var(--size-10); height: var(--size-10); border-radius: var(--radius-marker);
    display: flex; align-items: center; justify-content: center;
    background: var(--background-card-icon-softer); color: var(--text-card-icon-softer);
  }
  .feature-list--elevated .feature-list__icon svg { width: var(--size-5); height: var(--size-5); }
  .feature-list__body { display: flex; flex-direction: column; gap: var(--size-0p5); min-width: 0; }
  .feature-list__title {
    font-family: var(--font-family-heading); font-size: var(--font-size-base); line-height: var(--lh-md);
    font-weight: var(--font-smibold); color: var(--t-text); margin: 0;
  }
  .feature-list__note {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); line-height: var(--lh-lg);
    color: var(--t-text-body); margin: 0;
  }
  .feature-list--elevated .feature-list__item--excluded { background: transparent; }
  .feature-list--elevated .feature-list__item--excluded .feature-list__icon {
    background: var(--t-surface-alt); color: var(--t-text-caption);
  }
  .feature-list--elevated .feature-list__item--excluded .feature-list__title { color: var(--t-text-body); }
  .feature-list--elevated .feature-list__item--excluded .feature-list__note  { color: var(--t-text-caption); }
  /* Pair — two lists side by side under mono-caps labels ("Included" /
     "Not included"), the shape a what's-included block takes. Columns stack on
     a phone; the labels stay so each list still says what it is. */
  .feature-list-pair {
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--size-8);
    width: 100%; max-width: 1080px; margin: 0 auto;
  }
  .feature-list-pair__col { display: flex; flex-direction: column; gap: var(--size-4); min-width: 0; }
  .feature-list-pair__label {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wider); color: var(--t-eyebrow-text);
    padding-bottom: var(--size-2); border-bottom: var(--stroke-xs) solid var(--border-card-default);
  }
  .feature-list-pair__label--muted { color: var(--t-text-caption); }
  /* ── Segmented control ────────────────────────────────────────────────────
     The shared LOOK of a two- or three-way switch: a recessed track of labels
     with the current one raised onto a card surface. It is a real radio group,
     so assistive tech announces it as one and arrow keys work for free, and
     there is no JS to load, fail or re-initialise. The hidden input uses the
     library's zero-size recipe (`.form-field__radio-input`) so a touch-target
     audit measures the LABEL, which is the thing you actually tap.

     It says nothing about WHEN it appears or WHAT it switches — that belongs to
     whatever uses it. `.feature-list-pair__tabs` shows it on a phone only;
     `.tabset` shows it at every width. */
  .segmented {
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    gap: var(--size-1); padding: var(--size-1);
    border-radius: var(--radius-control);
    background: var(--t-surface-alt);
    border: var(--stroke-xs) solid var(--border-card-default);
  }
  .segmented__input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
  .segmented__tab {
    text-align: center; cursor: pointer;
    padding: var(--size-2p5) var(--size-2);
    border-radius: calc(var(--radius-control) - var(--size-1));
    font-family: var(--font-family-label); font-size: var(--font-size-sm);
    font-weight: var(--font-smibold); color: var(--t-text-body);
    transition: background var(--dur-micro), color var(--dur-micro);
  }
  .segmented__input:checked + .segmented__tab { background: var(--t-surface-card); color: var(--t-text); }
  /* Four tabs on a 375px phone are 80px each, and the 8px side padding leaves
     64px of text, a pixel short of "Transfers" at 14px (the academy trial's
     packages). A four-way control takes 4px: 72px at 375, 68px at 360. */
  .segmented:has(> .segmented__input--4) .segmented__tab { padding-inline: var(--size-1); }
  /* …and at 12px on a phone. "+ Transfers", with the "+" that says each package
     adds to the one before it (George, 2026-09-30), is 78px at 14px against the
     72px there is: 67px at 12px fits at 375 and at 360. */
  @media (max-width: 640px) {
    .segmented:has(> .segmented__input--4) .segmented__tab { font-size: var(--font-size-xs); }
  }
  .segmented__input:focus-visible + .segmented__tab {
    outline: var(--stroke-sm) solid var(--border-focus); outline-offset: var(--size-0p5);
  }
  /* ── Tabset ───────────────────────────────────────────────────────────────
     A segmented control and its panes, at EVERY width — for a genuine choice
     between alternates (the six- and twelve-day sample itineraries), not a
     responsive affordance the way the list pair's tabs are.

     Panes hang off `:has()`; a browser without it shows every pane stacked,
     which is long but legible rather than broken. EACH INSTANCE NEEDS ITS OWN
     `name` or two tabsets on a page switch together. Four tabs is the most a
     375px phone fits (the academy trial's packages, 2026-09-29), with short
     labels. To go past four, add the `--n` input class, the `data-tab` value
     and the hide rule together — they are one unit. */
  .tabset {
    display: flex; flex-direction: column; gap: var(--size-6);
    width: 100%; max-width: 860px; margin: 0 auto;
  }
  /* A pane stacks like `.sec-feature__inner`, and for the same reason: a pane
     can hold a section's worth of blocks, and those blocks expect a flex column
     to sit in. `.section-hdr--center` in particular centers with `align-self`,
     which a `display: block` parent ignores silently — the header simply sits
     left, looking like a broken heading rather than a missing container. */
  .tabset__pane { display: flex; flex-direction: column; gap: var(--size-10); }
  /* ⚠ A flex column DISABLES the `margin: 0 auto` centering idiom: an item with
     auto cross-axis margins sizes to its CONTENT and centers, instead of
     stretching to fill. Every library `__inner` wrapper carries `margin: 0 auto`
     for the block context it was written for, so dropped into a pane it
     collapses — the audience split measured 591px inside a 1280px pane, which
     reads as a styling bug rather than a layout rule. Give them the width back.
     `.section-hdr--center` is untouched: it centers with `align-self`, not with
     auto margins, and keeps its own 540px cap. */
  .tabset__pane > .sec-split__inner,
  .tabset__pane > .sec-image-grid__inner,
  .tabset__pane > .sec-feature__inner { width: 100%; }
  /* THE SAME TRAP, one level up. `.sec-feature__inner` is also a flex column,
     so an `__inner` wrapper nested inside one collapses to its content width
     for the identical reason. Nesting is legitimate — a feature section that
     opens with an image+text block before its card row — so give the width
     back here too rather than making every page work around it. */
  .sec-feature__inner > .sec-image-grid__inner,
  .sec-feature__inner > .sec-split__inner { width: 100%; }
  .tabset:has(.segmented__input--1:checked) .tabset__pane:not([data-tab='1']),
  .tabset:has(.segmented__input--2:checked) .tabset__pane:not([data-tab='2']),
  .tabset:has(.segmented__input--3:checked) .tabset__pane:not([data-tab='3']),
  .tabset:has(.segmented__input--4:checked) .tabset__pane:not([data-tab='4']),
  .tabset:has(.segmented__input--5:checked) .tabset__pane:not([data-tab='5']),
  .tabset:has(.segmented__input--6:checked) .tabset__pane:not([data-tab='6']) { display: none; }
  /* Five or six tabs (the academy /results league tables, one per team, 2026-10-01).
     Labels stay whole ("Women's First Team") rather than abbreviated, so the
     control wraps instead of shrinking them: two columns on a phone, three on a
     tablet, and one row only from 1200px — at 1024 a sixth of the row is 136px
     and "Women's First Team" broke onto two lines. */
  .segmented--wrap { grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  @media (min-width: 641px) {
    .segmented--wrap { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  }
  @media (min-width: 1200px) {
    .segmented--wrap { grid-auto-flow: column; grid-template-columns: none; grid-auto-columns: minmax(0, 1fr); }
  }
  /* Full container width, for a tabset whose panes hold a section's worth of
     content rather than a column of rows. */
  .tabset--wide { max-width: none; }
  /* Two panes that are COLUMNS on a real screen and TABS on a phone — the
     `feature-list-pair--tabbed` idea, expressed on the tabset so there is one
     mechanism rather than two. Above a phone the control is redundant (both
     panes are on screen) so it is hidden, and the panes are re-shown against
     the `:has()` rules above: the selector below is deliberately the same
     SHAPE as theirs so it matches their specificity and wins on source order,
     which is why `[data-tab]` is on the end and cannot be dropped. */
  @media (min-width: 641px) {
    .tabset--pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--size-5); align-items: start; }
    .tabset--pair .segmented { display: none; }
    .tabset--pair:has(.segmented__input:checked) .tabset__pane[data-tab] { display: flex; }
    /* A third pane is a strip under the pair, across both columns: the weekend
       under a day's morning and afternoon (George, 2026-09-29, the academy's
       pathway pages). On a phone it is simply the third tab. */
    .tabset--pair > .tabset__pane[data-tab='3'] { grid-column: 1 / -1; }
    /* Each column's title looks like the tab that names it on a phone: the
       selected segment raised onto the card surface, inside the recessed
       track (George, 2026-09-29: on a phone "each column's title stands out";
       the mono-caps line above each column did not). One element draws both
       layers: the 4px border is the track, the padding box is the raised tab,
       and the ring is the track's hairline. The phone hides the label, since
       its tab already names the pane. */
    .tabset--pair .feature-list-pair__label {
      display: block; text-align: center;
      padding: var(--size-2p5) var(--size-2);
      border: var(--size-1) solid var(--t-surface-alt);
      border-radius: var(--radius-control);
      background: var(--t-surface-card); background-clip: padding-box;
      box-shadow: 0 0 0 var(--stroke-xs) var(--border-card-default);
      font-family: var(--font-family-label); font-size: var(--font-size-sm);
      font-weight: var(--font-smibold); line-height: var(--lh-md);
      text-transform: none; letter-spacing: normal; color: var(--t-text);
    }
  }
  /* Every pane a column on a real screen, tabs on a phone: the pair's idea for
     a set of alternates that reads best side by side, such as the academy
     trial's four packages (George, 2026-09-29: "use tabs on the mobile layout,
     and keep four across on desktop"). One row from 1024px, one column per pane
     however many there are; two columns from 641 to 1023px. Four across needs
     1024: a pricing card will not shrink below 180px, and at 901px, inside the
     80px section gutters, four columns are 170px each, so the cards overran
     their gaps. Full container width, as `.sec-feature__grid` is. The re-show selector is the pair's, for the same
     specificity reason. Panes stretch to the row, so cards in them match
     heights. */
  @media (min-width: 641px) {
    .tabset--grid {
      display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: var(--size-5); max-width: none;
    }
    .tabset--grid .segmented { display: none; }
    .tabset--grid:has(.segmented__input:checked) .tabset__pane[data-tab] { display: flex; }
  }
  @media (min-width: 1024px) {
    .tabset--grid { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
  }
  /* The tab row exists only at phone widths; above that the pair IS the layout. */
  .feature-list-pair__tabs { display: none; }
  @media (max-width: 640px) {
    .feature-list-pair { grid-template-columns: 1fr; gap: var(--size-6); }

    /* ── Included / not-included as a segmented control ───────────────────
       Two lists side by side is the desktop shape. Stacked on a phone, "what
       is NOT included" sits below a full screen of what is, which is where it
       stops being read (George, 2026-09-08). `--tabbed` makes them two panes
       of one control instead.

       CSS-ONLY, deliberately: the control is a real radio group, so assistive
       tech announces it as one and arrow keys work for free, and there is no
       init or resize logic to fail. Pane visibility hangs off `:has()`; a
       browser without it shows BOTH panes — which is the stacked layout this
       replaced, so the fallback is the old behavior rather than a broken page.
       EACH INSTANCE NEEDS ITS OWN `name`: two pairs sharing one would switch
       together. */
    .feature-list-pair--tabbed { gap: var(--size-4); }
    /* The chrome is `.segmented`; this only decides that it is shown here. */
    .feature-list-pair--tabbed .feature-list-pair__tabs { display: grid; }
    /* The column's own label restates the tab that selected it. */
    .feature-list-pair--tabbed .feature-list-pair__label { display: none; }
    .feature-list-pair--tabbed:has(.segmented__input--included:checked) .feature-list-pair__col[data-pane='excluded'],
    .feature-list-pair--tabbed:has(.segmented__input--excluded:checked) .feature-list-pair__col[data-pane='included'] { display: none; }
  }
  /* Itinerary stack — the column of itinerary rows. Measured, not full-bleed:
     a 1280px row whose text hugs the left edge is mostly empty, so the stack
     sits at reading width and centres. A footnote slot closes it ("this is a
     sample"), because an itinerary without one reads as a commitment. */
  .itinerary-stack {
    display: flex; flex-direction: column; gap: var(--size-3);
    width: 100%; max-width: 860px; margin: 0 auto;
  }
  .itinerary-stack__note {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); line-height: var(--lh-xl);
    color: var(--t-text-caption); text-align: center; margin: var(--size-3) auto 0; max-width: 640px;
  }
  /* ── FormQuestion ───────────────────────────────────────────────────────── */
  .form-field {
    display: flex; flex-direction: column; gap: var(--size-1p5); min-width: 200px;
  }
  .form-field__label {
    font-family: var(--font-family-label); font-size: var(--font-size-xs); font-weight: var(--font-smibold);
    color: var(--t-text); letter-spacing: var(--tracking-normal); transition: color var(--dur-micro);
  }
  .form-field__label--disabled { color: var(--t-text-caption); }
  /* Base input */
  .form-field__input {
    font-family: var(--font-family-body); font-size: var(--font-size-sm); color: var(--t-text-body);
    background: var(--t-surface-card); border: var(--basic-button-stroke-sm) solid var(--t-border-strong);
    border-radius: var(--radius-control); padding: var(--size-2p5) var(--size-3p5); width: 100%; box-sizing: border-box;
    outline: none; transition: border-color var(--dur-micro) var(--ease-out), box-shadow var(--dur-micro) var(--ease-out);
    appearance: none; -webkit-appearance: none;
  }
  .form-field__input::placeholder { color: var(--t-text-caption); }
  .form-field__input:focus {
    border-color: var(--t-accent);
    box-shadow: 0 0 0 var(--size-0p5) color-mix(in srgb, var(--t-accent) 15%, transparent);
  }
  .form-field__input:disabled {
    background: var(--t-surface-alt); color: var(--t-text-caption);
    border-color: var(--t-border); cursor: not-allowed;
  }
  /* Input with leading icon */
  .form-field__input-wrap { position: relative; }
  .form-field__input-icon {
    position: absolute; left: var(--size-2p5); top: 50%; transform: translateY(-50%);
    width: var(--size-4); height: var(--size-4); pointer-events: none; color: var(--t-text-caption);
    transition: color var(--dur-micro);
  }
  .form-field__input-icon--disabled { color: var(--t-border); }
  .form-field__input--icon { padding-left: var(--size-9); }
  /* Error state */
  .form-field--error .form-field__input,
  .form-field--error .form-field__select { border-color: var(--informative-error-500); }
  .form-field--error .form-field__input:focus,
  .form-field--error .form-field__select:focus {
    box-shadow: 0 0 0 var(--size-0p5) color-mix(in srgb, var(--informative-error-500) 15%, transparent);
  }
  .form-field__error-msg {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    color: var(--text-error-default); display: flex; align-items: center; gap: var(--size-1);
  }
  .form-field__state-label {
    font-family: var(--font-family-label); font-size: var(--font-size-xs); font-weight: var(--font-smibold);
    letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--t-text-caption);
  }
  .form-field__helper {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); color: var(--t-text-caption);
  }
  /* Date — normalize browser-native calendar icon */
  .form-field__input--date { color-scheme: light; }
  .form-field__input--date::-webkit-calendar-picker-indicator {
    opacity: 0.45; cursor: pointer;
  }
  .form-field__input--date::-webkit-calendar-picker-indicator:hover { opacity: 0.8; }
  /* Select */
  .form-field__select-wrap { position: relative; }
  .form-field__select {
    font-family: var(--font-family-body); font-size: var(--font-size-sm); color: var(--t-text-body);
    background: var(--t-surface-card); border: var(--basic-button-stroke-sm) solid var(--t-border-strong);
    border-radius: var(--radius-control); padding: var(--size-2p5) var(--size-9) var(--size-2p5) var(--size-3p5); width: 100%; box-sizing: border-box;
    outline: none; appearance: none; -webkit-appearance: none; cursor: pointer;
    transition: border-color var(--dur-micro) var(--ease-out), box-shadow var(--dur-micro) var(--ease-out);
  }
  .form-field__select:focus {
    border-color: var(--t-accent);
    box-shadow: 0 0 0 var(--size-0p5) color-mix(in srgb, var(--t-accent) 15%, transparent);
  }
  .form-field__select:disabled {
    background: var(--t-surface-alt); color: var(--t-text-caption);
    border-color: var(--t-border); cursor: not-allowed;
  }
  .form-field__select-icon {
    position: absolute; right: var(--size-2p5); top: 50%; transform: translateY(-50%);
    width: var(--size-4); height: var(--size-4); pointer-events: none; color: var(--t-text-caption);
  }
  /* Textarea */
  .form-field__textarea {
    font-family: var(--font-family-body); font-size: var(--font-size-sm); color: var(--t-text-body);
    background: var(--t-surface-card); border: var(--basic-button-stroke-sm) solid var(--t-border-strong);
    border-radius: var(--radius-control); padding: var(--size-2p5) var(--size-3p5); width: 100%; box-sizing: border-box;
    outline: none; min-height: var(--size-24); resize: vertical;
    transition: border-color var(--dur-micro) var(--ease-out), box-shadow var(--dur-micro) var(--ease-out);
    appearance: none; -webkit-appearance: none;
  }
  .form-field__textarea::placeholder { color: var(--t-text-caption); }
  .form-field__textarea:focus {
    border-color: var(--t-accent);
    box-shadow: 0 0 0 var(--size-0p5) color-mix(in srgb, var(--t-accent) 15%, transparent);
  }
  .form-field--error .form-field__textarea { border-color: var(--informative-error-500); }
  /* Checkbox */
  .form-field__check {
    display: flex; align-items: center; gap: var(--size-2p5); cursor: pointer; padding: var(--size-1) 0;
    user-select: none;
  }
  .form-field__check--disabled { opacity: 0.5; cursor: not-allowed; }
  .form-field__checkbox-input {
    position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
  }
  .form-field__checkbox-box {
    width: var(--size-4); height: var(--size-4); border-radius: var(--size-1); flex-shrink: 0;
    border: var(--basic-button-stroke-sm) solid var(--t-border-strong); background: var(--t-surface-card);
    display: flex; align-items: center; justify-content: center;
    transition: background var(--dur-micro), border-color var(--dur-micro);
  }
  .form-field__checkbox-input:checked ~ .form-field__checkbox-box {
    background: var(--t-accent); border-color: var(--t-accent);
  }
  .form-field__checkbox-input:checked ~ .form-field__checkbox-box::after {
    content: ''; display: block;
    width: 9px; height: 5px;
    border-left: var(--basic-button-stroke-md) solid var(--base-white); border-bottom: var(--basic-button-stroke-md) solid var(--base-white);
    transform: rotate(-45deg) translate(var(--size-px), calc(-1 * var(--size-px)));
  }
  .form-field__check--error .form-field__checkbox-box { border-color: var(--informative-error-500); }
  .form-field__check-text {
    font-family: var(--font-family-body); font-size: var(--font-size-sm); color: var(--t-text-body);
    transition: color var(--dur-micro);
  }
  /* Radio */
  .form-field__radio {
    display: flex; align-items: center; gap: var(--size-2p5); cursor: pointer; padding: var(--size-1) 0;
    user-select: none;
  }
  .form-field__radio--disabled { opacity: 0.5; cursor: not-allowed; }
  .form-field__radio-input {
    position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
  }
  .form-field__radio-dot {
    width: var(--size-4); height: var(--size-4); border-radius: var(--radius-pill); flex-shrink: 0;
    border: var(--basic-button-stroke-sm) solid var(--t-border-strong); background: var(--t-surface-card);
    display: flex; align-items: center; justify-content: center;
    transition: border-color var(--dur-micro);
  }
  .form-field__radio-input:checked ~ .form-field__radio-dot { border-color: var(--t-accent); }
  .form-field__radio-input:checked ~ .form-field__radio-dot::after {
    content: ''; display: block;
    width: var(--size-2); height: var(--size-2); border-radius: var(--radius-pill); background: var(--t-accent);
  }
  .form-field__radio-input:focus-visible ~ .form-field__radio-dot {
    box-shadow: 0 0 0 var(--size-0p5) color-mix(in srgb, var(--t-accent) 25%, transparent);
  }
  .form-field__radio--error .form-field__radio-dot { border-color: var(--informative-error-500); }
  .form-field__radio-text {
    font-family: var(--font-family-body); font-size: var(--font-size-sm); color: var(--t-text-body);
  }
  /* Radio — selectable card / tile variant */
  .form-field__radio-cards { display: flex; flex-direction: column; gap: var(--size-2p5); }
  .form-field__radio-card {
    position: relative; display: flex; align-items: flex-start; gap: var(--size-2p5);
    padding: var(--size-3p5) var(--size-4); cursor: pointer; user-select: none;
    border: var(--basic-button-stroke-sm) solid var(--t-border-strong); border-radius: var(--radius-control);
    background: var(--t-surface-card); transition: border-color var(--dur-micro) var(--ease-out), background var(--dur-micro) var(--ease-out);
  }
  .form-field__radio-card:hover { border-color: var(--t-accent-strong); }
  .form-field__radio-card:has(.form-field__radio-input:checked) {
    border-color: var(--t-accent); background: var(--t-tag-soft-bg);
  }
  .form-field__radio-card:has(.form-field__radio-input:focus-visible) {
    box-shadow: 0 0 0 var(--size-0p5) color-mix(in srgb, var(--t-accent) 20%, transparent);
  }
  .form-field__radio-card .form-field__radio-dot { margin-top: var(--size-0p5); }
  .form-field__radio-card-body { display: flex; flex-direction: column; gap: var(--size-0p5); }
  .form-field__radio-card-title {
    font-family: var(--font-family-label); font-size: var(--font-size-sm); font-weight: var(--font-smibold);
    color: var(--t-text);
  }
  .form-field__radio-card-desc {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); color: var(--t-text-caption);
    line-height: var(--lh-lg);
  }
  .form-field--error .form-field__radio-card { border-color: var(--informative-error-500); }
  /* ── InformativeBanner ──────────────────────────────────────────────────── */
  .info-banner {
    display: flex; align-items: flex-start; gap: var(--size-3);
    padding: var(--size-3p5) var(--size-4); border-radius: var(--radius-card); border: var(--stroke-xs) solid;
    width: 100%; box-sizing: border-box;
  }
  .info-banner__icon {
    flex-shrink: 0; width: var(--size-5); height: var(--size-5); margin-top: var(--size-px);
  }
  .info-banner__body  { flex: 1; display: flex; flex-direction: column; gap: var(--size-0p5); }
  .info-banner__title {
    font-family: var(--font-family-label); font-size: var(--font-size-sm); font-weight: var(--font-smibold);
    line-height: var(--lh-lg); margin: 0;
  }
  .info-banner__desc  {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); line-height: var(--lh-xl); margin: 0;
  }
  .info-banner__dismiss {
    flex-shrink: 0; background: none; border: none; cursor: pointer;
    padding: var(--size-0p5); border-radius: var(--size-1); opacity: 0.55; transition: opacity var(--dur-micro);
    display: flex; align-items: center; line-height: var(--lh-none);
  }
  .info-banner__dismiss:hover { opacity: 1; }
  .info-banner--success {
    background: var(--informative-success-100); border-color: var(--informative-success-500);
  }
  .info-banner--success .info-banner__icon,
  .info-banner--success .info-banner__dismiss { color: var(--informative-success-700); }
  .info-banner--success .info-banner__title   { color: var(--informative-success-800); }
  .info-banner--success .info-banner__desc    { color: var(--informative-success-700); }
  .info-banner--warning {
    background: var(--informative-warning-100); border-color: var(--informative-warning-500);
  }
  .info-banner--warning .info-banner__icon,
  .info-banner--warning .info-banner__dismiss { color: var(--informative-warning-700); }
  .info-banner--warning .info-banner__title   { color: var(--informative-warning-800); }
  .info-banner--warning .info-banner__desc    { color: var(--informative-warning-700); }
  .info-banner--error {
    background: var(--informative-error-100); border-color: var(--informative-error-500);
  }
  .info-banner--error .info-banner__icon,
  .info-banner--error .info-banner__dismiss { color: var(--informative-error-700); }
  .info-banner--error .info-banner__title   { color: var(--informative-error-800); }
  .info-banner--error .info-banner__desc    { color: var(--informative-error-700); }
  .info-banner--info {
    background: var(--informative-info-100); border-color: var(--informative-info-500);
  }
  .info-banner--info .info-banner__icon,
  .info-banner--info .info-banner__dismiss { color: var(--informative-info-700); }
  .info-banner--info .info-banner__title   { color: var(--informative-info-800); }
  .info-banner--info .info-banner__desc    { color: var(--informative-info-700); }
  /* ── Hover language ─────────────────────────────────────────────────────── */
  /* Value-lift only (two-glow law): one transform + one surface change —
     a 4px rise and the hairline brightening. Depth never comes from shadow. */
  .stat-block,
  .quote-block,
  .pillar-card,
  .venue-card,
  .feature-card,
  .pricing-card {
    cursor: default;
    transition: transform var(--dur-panel) var(--ease-out),
                background var(--dur-micro), border-color var(--dur-micro), color var(--dur-micro);
  }
  .stat-block:hover,
  .pillar-card:hover,
  .venue-card:hover,
  .feature-card:hover,
  .pricing-card:hover {
    transform: translateY(calc(-1 * var(--size-1)));
    border-color: var(--t-border-strong);
  }
  .quote-block:hover { transform: translateY(calc(-1 * var(--size-1))); }
  /* Rows shift right */
  .itinerary-row {
    transition: transform var(--dur-panel) var(--ease-out), background var(--dur-micro), border-color var(--dur-micro);
    cursor: default;
  }
  .itinerary-row:hover {
    transform: translateX(var(--size-1));
  }
  /* Full-bleed sections — break out of .cmp-canvas padding */
  #tab-section section,
  #tab-section footer {
    margin-inline: calc(-1 * var(--size-8));
  }
  /* Full-width button utility */
  .btn--full { width: 100%; justify-content: center; }
  /* Neutral — black variation: dark neutral border + text (for light surfaces).
     Hover fills with the neutral text colour at low opacity — one shade darker. */
  .btn--neutral { background: transparent; color: var(--t-neutral-text); border-color: var(--t-neutral-border); }
  .btn--neutral:hover:not(:disabled) { background: color-mix(in srgb, var(--t-neutral-text) var(--opacity-10), transparent); }
  .btn--neutral:disabled { opacity: 0.45; cursor: not-allowed; }
  /* Neutral — white variation: white border + text (for dark surfaces).
     Hover fills with white at low opacity — one shade lighter. */
  .btn--neutral-inverse { background: transparent; color: var(--base-white); border-color: var(--base-white); }
  .btn--neutral-inverse:hover:not(:disabled) { background: color-mix(in srgb, var(--base-white) var(--opacity-10), transparent); }
  .btn--neutral-inverse:disabled { opacity: 0.45; cursor: not-allowed; }
  /* Ghost-inverse button variant (for dark/inverse banner) */
  .btn--ghost-inv {
    background: transparent;
    color: var(--t-text-inv);
    border-color: var(--t-text-inv);
  }
  .btn--ghost-inv:hover {
    background: color-mix(in srgb, var(--base-white) var(--opacity-10), transparent);
  }
  /* Accent outline on always-dark surfaces — the hero's lead CTA. Keeps the
     outline sophistication but carries the product hue: the light accent step
     (accent-soft: 300 light / 400 dark) holds AA over photo scrims in both
     modes where the 500 would sink. */
  .btn--ghost-accent-inv {
    background: transparent;
    color: var(--t-text-accent-inv);
    border-color: var(--t-text-accent-inv);
  }
  .btn--ghost-accent-inv:hover:not(:disabled) {
    background: color-mix(in srgb, var(--t-accent) var(--opacity-25), transparent);
    color: var(--base-white);
  }
  .btn--ghost-accent-inv:disabled { opacity: 0.45; cursor: not-allowed; }
  /* Outlined accent — transparent bg, accent border + text at the AA-passing step. */
  .btn--ghost-accent {
    background: transparent;
    color: var(--t-eyebrow-text);
    border-color: var(--t-eyebrow-text);
  }
  .btn--ghost-accent:hover:not(:disabled) {
    background: color-mix(in srgb, var(--t-accent) var(--opacity-10), transparent);
  }
  .btn--ghost-accent:disabled { opacity: 0.45; cursor: not-allowed; }
  /* ── 0. NAV ───────────────────────────────────────────────────────── */
  /* Always dark by design — bg.section.light-inverse (solid) or transparent
     over a photo/hero backdrop. Uses the same --t-*-inv tokens as Footer, so
     it reads correctly whichever mode the page is in. */
  /* Chrome surface — permanently dark (--t-chrome-*), hairline seam */
  .sec-nav {
    background: var(--t-chrome-bg);
    border-bottom: var(--stroke-xs) solid color-mix(in srgb, var(--base-white) var(--opacity-10), transparent);
  }
  .sec-nav__inner {
    max-width: 1280px; margin: 0 auto;
    display: flex; align-items: center; gap: var(--size-6);
    padding: var(--size-4) var(--size-8);
  }
  .sec-nav__brand { display: flex; align-items: center; gap: var(--size-2p5); flex-shrink: 0; text-decoration: none; }
  .sec-nav__logo { height: var(--size-9); width: auto; display: block; }
  .sec-nav__title {
    font-family: var(--font-family-heading); font-size: var(--font-size-xl); line-height: var(--lh-none);
    font-weight: var(--font-smibold); letter-spacing: var(--tracking-normal);
    text-transform: var(--case-header); color: var(--t-chrome-text);
  }
  /* Centre page navigation */
  .sec-nav__menu {
    flex: 1; display: flex; align-items: center; justify-content: center; gap: var(--size-1);
    list-style: none; margin: 0; padding: 0;
  }
  /* align-items: center (2026-09-16): an inline-flex box stretches its children by
     default, so the 12px caret beside "Events" sat at the top of the 20px line
     instead of on the text's center. */
  .sec-nav__link {
    display: inline-flex; align-items: center; padding: var(--size-2) var(--size-3); border-radius: var(--radius-xs);
    font-family: var(--font-family-body); font-size: var(--font-size-sm); font-weight: var(--font-medium);
    color: var(--t-chrome-text-body); text-decoration: none;
    transition: color var(--dur-micro) var(--ease-out);
  }
  .sec-nav__link:hover,
  .sec-nav__link--active { color: var(--t-text-accent-inv); }
  .sec-nav__link:focus-visible { outline: var(--stroke-sm) solid var(--border-focus); outline-offset: var(--size-0p5); }
  /* Second level (2026-09-14). A parent item reveals its children on :hover and on
     :focus-within — the same two triggers the language panel uses, so a keyboard
     user tabbing onto the parent link gets the children in the tab order and a
     mouse user gets them on hover. Two levels only: the panel is a flat list. */
  .sec-nav__item { position: relative; }
  .sec-nav__link-caret {
    width: var(--size-3); height: var(--size-3); margin-left: var(--size-1);
    transition: transform var(--dur-micro) var(--ease-out);
  }
  .sec-nav__item--parent:hover .sec-nav__link-caret,
  .sec-nav__item--parent:focus-within .sec-nav__link-caret { transform: rotate(180deg); }
  .sec-nav__sub {
    position: absolute; top: 100%; left: 0;
    min-width: var(--size-48);
    display: flex; flex-direction: column; gap: var(--size-0p5);
    /* The top padding doubles as the hover bridge between link and panel — with a
       real gap the pointer leaves the item on the way down and the panel closes. */
    margin: 0; padding: var(--size-2); padding-top: var(--size-2);
    list-style: none;
    background: color-mix(in srgb, var(--base-white) var(--opacity-5), var(--t-chrome-bg));
    border: var(--stroke-xs) solid color-mix(in srgb, var(--base-white) var(--opacity-10), transparent);
    border-radius: var(--radius-control);
    opacity: 0; visibility: hidden; transform: translateY(var(--size-1));
    transition: opacity var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out), visibility var(--dur-micro);
  }
  .sec-nav__item--parent:hover .sec-nav__sub,
  .sec-nav__item--parent:focus-within .sec-nav__sub {
    opacity: 1; visibility: visible; transform: translateY(0);
  }
  .sec-nav__sub-link {
    display: block; padding: var(--size-2) var(--size-2p5); border-radius: var(--radius-xs);
    font-family: var(--font-family-body); font-size: var(--font-size-sm); font-weight: var(--font-medium);
    color: var(--t-chrome-text-body); text-decoration: none; white-space: nowrap;
    transition: color var(--dur-micro) var(--ease-out), background var(--dur-micro) var(--ease-out);
  }
  .sec-nav__sub-link:hover,
  .sec-nav__sub-link--active { color: var(--t-text-accent-inv); background: color-mix(in srgb, var(--base-white) var(--opacity-5), transparent); }
  .sec-nav__sub-link:focus-visible { outline: var(--stroke-sm) solid var(--border-focus); outline-offset: var(--size-0p5); }
  /* Right cluster — language switcher + CTA */
  .sec-nav__actions { display: flex; align-items: center; gap: var(--size-4); flex-shrink: 0; }
  .sec-nav__lang { position: relative; }
  .sec-nav__lang-btn {
    display: inline-flex; align-items: center; gap: var(--size-1p5);
    background: none; border: none; cursor: pointer; padding: var(--size-2) 0;
    font-family: var(--font-family-body); font-size: var(--font-size-sm); font-weight: var(--font-medium);
    color: var(--t-chrome-text-body);
    transition: color var(--dur-micro) var(--ease-out);
  }
  .sec-nav__lang:hover .sec-nav__lang-btn,
  .sec-nav__lang:focus-within .sec-nav__lang-btn { color: var(--t-chrome-text); }
  .sec-nav__lang-icon { width: var(--size-4); height: var(--size-4); }
  .sec-nav__lang-caret { width: var(--size-3); height: var(--size-3); transition: transform var(--dur-micro) var(--ease-out); }
  .sec-nav__lang:hover .sec-nav__lang-caret,
  .sec-nav__lang:focus-within .sec-nav__lang-caret { transform: rotate(180deg); }
  .sec-nav__lang-panel {
    position: absolute; top: calc(100% + var(--size-2)); right: 0;
    min-width: var(--size-32);
    display: flex; flex-direction: column; gap: var(--size-0p5);
    padding: var(--size-2);
    background: color-mix(in srgb, var(--base-white) var(--opacity-5), var(--t-chrome-bg));
    border: var(--stroke-xs) solid color-mix(in srgb, var(--base-white) var(--opacity-10), transparent);
    border-radius: var(--radius-control);
    opacity: 0; visibility: hidden; transform: translateY(var(--size-1));
    transition: opacity var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out), visibility var(--dur-micro);
  }
  .sec-nav__lang:hover .sec-nav__lang-panel,
  .sec-nav__lang:focus-within .sec-nav__lang-panel {
    opacity: 1; visibility: visible; transform: translateY(0);
  }
  .sec-nav__lang-option {
    padding: var(--size-2) var(--size-2p5); border-radius: var(--radius-xs);
    font-family: var(--font-family-body); font-size: var(--font-size-sm);
    color: var(--t-chrome-text-body); text-decoration: none;
    transition: color var(--dur-micro) var(--ease-out), background var(--dur-micro) var(--ease-out);
  }
  .sec-nav__lang-option:hover { color: var(--t-text-accent-inv); background: color-mix(in srgb, var(--base-white) var(--opacity-5), transparent); }
  /* Mobile hamburger toggle */
  .sec-nav__toggle {
    display: none; align-items: center; justify-content: center;
    width: var(--size-9); height: var(--size-9); padding: 0; flex-shrink: 0; margin-left: auto;
    background: none; border: var(--stroke-xs) solid color-mix(in srgb, var(--base-white) var(--opacity-25), transparent);
    border-radius: var(--radius-control); color: var(--t-chrome-text); cursor: pointer;
  }
  /* Transparent variant — overlays a hero/photo backdrop. Velvet's ONE frost
     allowance (nav only, blur 12): venetian-tinted glass over the photograph. */
  .sec-nav--transparent {
    position: absolute; top: 0; left: 0; right: 0; z-index: 3;
    background: color-mix(in srgb, var(--t-chrome-bg) var(--opacity-25), transparent);
    -webkit-backdrop-filter: var(--p-frost);
    backdrop-filter: var(--p-frost);
    border-bottom-color: color-mix(in srgb, var(--base-white) var(--opacity-5), transparent);
  }
  /* Mobile — collapses menu + actions behind the toggle.
     `--mobile` forces this inside the fixed-width docs preview frame;
     the @media rule is the real responsive behaviour in production. */
  .sec-nav--mobile .sec-nav__menu,
  .sec-nav--mobile .sec-nav__actions { display: none; }
  .sec-nav--mobile .sec-nav__toggle { display: inline-flex; }
  @media (max-width: 900px) {
    .sec-nav__menu, .sec-nav__actions { display: none; }
    .sec-nav__toggle { display: inline-flex; }
  }
  /* Mobile drawer — full-screen slide-in menu (the base component is toggle-only;
     this completes it for production). Drawer sits on --t-surface, so it uses the
     page's normal (non-inverse) text colours. */
  .sec-nav__mobile {
    position: fixed; inset: 0; z-index: 100;
    background: var(--t-surface);
    display: flex; flex-direction: column; gap: var(--size-2);
    padding: var(--size-6);
    overflow-y: auto;
    /* Closed drawers were left `visible` off-canvas, so their links stayed in the
       tab order and the a11y tree on every phone page, and the off-screen box
       inflated documentElement.scrollWidth to 2x the viewport (which also made
       full-page screenshots half empty). visibility does both jobs; the 0s delay
       holds it visible for the slide-out, then hides it. */
    visibility: hidden;
    transform: translateX(100%);
    transition: transform var(--dur-panel) var(--ease-out),
                visibility 0s linear var(--dur-panel);
  }
  .sec-nav__mobile--open {
    transform: translateX(0);
    visibility: visible;
    transition: transform var(--dur-panel) var(--ease-out), visibility 0s;
  }
  .sec-nav__mobile-top {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: var(--size-4);
  }
  .sec-nav__mobile-logo { height: var(--size-9); width: auto; display: block; }
  /* LINK TYPE, NOT HEADING TYPE (George, 2026-09-16). The drawer links used to be
     24px uppercase heading type and read as section headers rather than as links.
     They are now body type at 18px, one step above the desktop bar's 14px because
     a finger is the pointer here. Each item is a row with one hairline under it. */
  .sec-nav__mobile-list {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-direction: column;
  }
  .sec-nav__mobile-item { border-bottom: var(--stroke-xs) solid var(--t-border); }
  .sec-nav__mobile-row { display: flex; align-items: center; gap: var(--size-2); }
  .sec-nav__mobile-link {
    flex: 1; display: flex; align-items: center; min-height: var(--size-11);
    font-family: var(--font-family-body); font-size: var(--font-size-lg); font-weight: var(--font-medium);
    line-height: var(--lh-sm); color: var(--t-text); text-decoration: none;
    transition: color var(--dur-micro) var(--ease-out);
  }
  .sec-nav__mobile-link:hover,
  .sec-nav__mobile-link--active { color: var(--t-accent); }
  .sec-nav__mobile .btn { margin-top: var(--size-6); }
  /* EXPANDABLE, LIKE THE DESKTOP BAR (George, 2026-09-16). A parent keeps its label
     as a real link, as on desktop, and gets its own 44px chevron button that shows
     and hides the children, because a touchscreen has no hover. The group starts
     open when the current page is one of its children, so a reader inside Events
     can see where they are. The `hidden` rule has to be stated: the list's own
     `display: flex` outranks the browser's default for [hidden]. */
  .sec-nav__mobile-expand {
    display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
    width: var(--size-11); height: var(--size-11); padding: 0;
    background: none; border: 0; border-radius: var(--radius-xs);
    color: var(--t-text-caption); cursor: pointer;
    transition: color var(--dur-micro) var(--ease-out);
  }
  .sec-nav__mobile-expand:hover { color: var(--t-text); }
  .sec-nav__mobile-expand:focus-visible { outline: var(--stroke-sm) solid var(--border-focus); outline-offset: calc(-1 * var(--stroke-sm)); }
  .sec-nav__mobile-expand .icon { transition: transform var(--dur-micro) var(--ease-out); }
  .sec-nav__mobile-expand[aria-expanded="true"] .icon { transform: rotate(180deg); }
  .sec-nav__mobile-sub {
    list-style: none; margin: 0;
    display: flex; flex-direction: column;
    padding: 0 0 var(--size-2) var(--size-4);
  }
  .sec-nav__mobile-sub[hidden] { display: none; }
  .sec-nav__mobile-sublink {
    display: flex; align-items: center; min-height: var(--size-11);
    font-family: var(--font-family-body); font-size: var(--font-size-base); font-weight: var(--font-normal);
    line-height: var(--lh-sm); color: var(--t-text-body); text-decoration: none;
    transition: color var(--dur-micro) var(--ease-out);
  }
  .sec-nav__mobile-sublink:hover,
  .sec-nav__mobile-sublink--active { color: var(--t-accent); }
  /* Language in the drawer (2026-09-14) — the footer switcher's shape, at the foot
     of the drawer where the thumb already is. Both halves are 44px targets. */
  .sec-nav__mobile-lang {
    display: flex; align-items: center; gap: var(--size-2);
    margin-top: auto; padding-top: var(--size-6);
    color: var(--t-text-caption);
  }
  .sec-nav__mobile-lang > .icon { width: var(--size-5); height: var(--size-5); }
  .sec-nav__mobile-lang-btn {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: var(--size-11); min-height: var(--size-11); padding: 0 var(--size-2);
    background: none; border: 0; border-radius: var(--radius-xs); cursor: pointer;
    font-family: var(--font-family-body); font-size: var(--font-size-sm); font-weight: var(--font-medium);
    letter-spacing: var(--tracking-wide); color: var(--t-text-caption); text-decoration: none;
    transition: color var(--dur-micro) var(--ease-out);
  }
  .sec-nav__mobile-lang-btn:hover { color: var(--t-text); }
  .sec-nav__mobile-lang-btn--active { color: var(--t-text); font-weight: var(--font-smibold); }
  /* The dot takes the footer separator's treatment — caption gray at half strength
     (George, 2026-09-17). It was the border color, 1.21:1 on the drawer and all but
     invisible; half-strength caption is about 1.9:1, visible and still quieter than
     the two letters it sits between. */
  .sec-nav__mobile-lang-sep { color: var(--t-text-caption); opacity: 0.5; user-select: none; }
  .sec-nav__mobile-close {
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--size-9); height: var(--size-9); padding: 0;
    background: none; border: var(--stroke-xs) solid var(--t-border); border-radius: var(--radius-xs);
    color: var(--t-text); cursor: pointer;
  }
  @media (min-width: 901px) { .sec-nav__mobile { display: none; } }
  /* ── 1. HERO ──────────────────────────────────────────────────────── */

  /* ── Always-dark sections: fixed light text roles ─────────────────────
     The hero (photo scrim), chapel, and closing wells are dark by design in
     BOTH modes. The theme-flipping -inv roles resolve to DARK values under
     theme-*-dark (text-hero-inverse → gray-cool-900), which made outline
     CTAs and captions unreadable there. Pin them to fixed light values so
     every child (buttons, stat blocks, captions) inherits correctly. */
  .sec-hero, .sec-chapel, .sec-cta-banner,
  .quote-block, .stat-block--inverse, .section-hdr--inverse,
  .icon-tile--inverse, .badge--neutral-inverse, .country-badge--inverse {
    --t-text-inv: var(--base-white);
    --t-text-inv-body: var(--gray-cool-200);
    --t-text-inv-caption: var(--gray-cool-300);
    /* Same reason the -inv roles are pinned here: these sections are dark in BOTH
       modes, so the theme-flipping neutral roles (dark on a light page) would put
       a near-black outline CTA on a near-black well. */
    --t-neutral-text: var(--gray-cool-200);
    --t-neutral-border: var(--gray-warm-200);
  }
  /* ── Hero: full-bleed graded still + canvas-tinted scrim + bloom ─────
     GLOW MOMENT 1 of 2 (the rationing law) — the only other glow on a page
     is the philosophy chapel. */
  .sec-hero {
    position: relative;
    overflow: hidden;
    min-height: 560px;
    display: flex;
    align-items: center;
    padding: var(--size-28) var(--size-20) var(--size-24);
  }
  /* Simulated photo background — swap for background-image: url(...) in production */
  .sec-hero__bg {
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg, var(--t-surface-inv) 0%, var(--t-accent-darker) 50%, var(--t-accent-strong) 100%);
    /* Production: set background-image: url(...) inline on the element to swap in a
       real photo. The gradient above stays as a graceful fallback. */
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    z-index: 0;
  }
  /* ── Hero background carousel ────────────────────────────────────────────
     A hero that rotates through several stills instead of holding one. The
     slides live INSIDE .sec-hero__bg, so they sit under the scrim (::before)
     and the bloom (::after) exactly as a single still does, and they inherit
     the §5.2 grade from the parent rather than each re-declaring it.

     Motion is IDENTITY-BRIEF §5.10: a slow crossfade with a Ken Burns drift,
     one easing signature, no per-element fades. CSS-only — the slides are
     staggered by animation-delay, so there is no JS to load, fail, or
     re-initialise on resize.

     THE KEYFRAME PERCENTAGES BELOW ASSUME FOUR SLIDES (each visible for a
     quarter of the cycle). Changing the slide count means changing them and
     the nth-child delays together — they are one unit. */
  .sec-hero__bg--carousel {
    /* Total time for one full pass through all four stills. */
    --hero-cycle: 32s;
    /* The base gradient is deliberately left in place as a fallback: with the
       timing below a slide is always on screen, so it never shows — but if an
       image 404s it is a graded backdrop rather than a transparent hole. */
  }
  .sec-hero__slide {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    opacity: 0;
    animation: sec-hero-slide var(--hero-cycle) infinite;
    /* The one easing signature, not a per-slide curve. */
    animation-timing-function: var(--ease-out);
    will-change: opacity, transform;
  }
  /* Slots are 25% apart, but every delay is shifted back by the 3% fade so that
     (a) slide 1 is ALREADY at full opacity at t=0 — otherwise the first second of
     every page load is a hole where the hero should be — and (b) each slide's
     fade-in lands exactly on the previous slide's fade-out instead of after it,
     which is the difference between a crossfade and a blink. */
  .sec-hero__slide:nth-child(1) { animation-delay: calc(var(--hero-cycle) * -0.03); }
  .sec-hero__slide:nth-child(2) { animation-delay: calc(var(--hero-cycle) * 0.22); }
  .sec-hero__slide:nth-child(3) { animation-delay: calc(var(--hero-cycle) * 0.47); }
  .sec-hero__slide:nth-child(4) { animation-delay: calc(var(--hero-cycle) * 0.72); }
  @keyframes sec-hero-slide {
    /* Fade in over 3%, hold to 25%, fade out by 28%, then wait out the other
       three slides. Ken Burns drifts across the visible window and resets while
       the slide is transparent. */
    0%   { opacity: 0; transform: scale(1.005); }
    3%   { opacity: 1; }
    25%  { opacity: 1; }
    28%  { opacity: 0; transform: scale(1.06); }
    100% { opacity: 0; transform: scale(1.005); }
  }
  /* THREE slides. Same 8s per still, so the cycle is 24s rather than 32s, and
     the slots are a third apart instead of a quarter. It needs its own keyframe
     because @keyframes percentages cannot be computed from a custom property —
     the hold has to be written at 33%, not derived. Both variants share the
     fade width (3%) and the drift, so they read as one motion. */
  .sec-hero__bg--carousel-3 { --hero-cycle: 24s; }
  .sec-hero__bg--carousel-3 .sec-hero__slide { animation-name: sec-hero-slide-3; }
  .sec-hero__bg--carousel-3 .sec-hero__slide:nth-child(1) { animation-delay: calc(var(--hero-cycle) * -0.03); }
  .sec-hero__bg--carousel-3 .sec-hero__slide:nth-child(2) { animation-delay: calc(var(--hero-cycle) * 0.30); }
  .sec-hero__bg--carousel-3 .sec-hero__slide:nth-child(3) { animation-delay: calc(var(--hero-cycle) * 0.63); }
  /* TWO slides. Same 8s per still — cycle 16s, slots half apart, hold to 50%.
     Same reason as the three-slide variant for needing its own keyframe: a
     @keyframes percentage cannot be computed from a custom property. */
  .sec-hero__bg--carousel-2 { --hero-cycle: 16s; }
  .sec-hero__bg--carousel-2 .sec-hero__slide { animation-name: sec-hero-slide-2; }
  .sec-hero__bg--carousel-2 .sec-hero__slide:nth-child(1) { animation-delay: calc(var(--hero-cycle) * -0.03); }
  .sec-hero__bg--carousel-2 .sec-hero__slide:nth-child(2) { animation-delay: calc(var(--hero-cycle) * 0.47); }
  @keyframes sec-hero-slide-2 {
    0%   { opacity: 0; transform: scale(1.005); }
    3%   { opacity: 1; }
    50%  { opacity: 1; }
    53%  { opacity: 0; transform: scale(1.06); }
    100% { opacity: 0; transform: scale(1.005); }
  }
  @keyframes sec-hero-slide-3 {
    0%   { opacity: 0; transform: scale(1.005); }
    3%   { opacity: 1; }
    33%  { opacity: 1; }
    36%  { opacity: 0; transform: scale(1.06); }
    100% { opacity: 0; transform: scale(1.005); }
  }
  /* Reduced motion: no crossfade, no drift — the first still simply stays. */
  @media (prefers-reduced-motion: reduce) {
    .sec-hero__slide { animation: none; opacity: 0; transform: none; }
    .sec-hero__slide:nth-child(1) { opacity: 1; }
  }
  /* Canvas-tinted directional scrim (IDENTITY-BRIEF §5.3): the product's deep
     step, ~75% at the text edge falling to ~10% — never a pure-black multiply. */
  .sec-hero::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
      to right,
      color-mix(in srgb, var(--t-accent-darker) var(--opacity-25), color-mix(in srgb, var(--base-black) var(--opacity-75), transparent)) 0%,
      color-mix(in srgb, var(--base-black) var(--opacity-50), transparent) 55%,
      color-mix(in srgb, var(--base-black) var(--opacity-10), transparent) 100%
    );
    z-index: 1;
  }
  /* The bloom — a slow radial breath of the product hue behind the text stack.
     Slots: --glow-color per use; knob: --p-glow-scale. */
  .sec-hero::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background: radial-gradient(
      ellipse 60% 70% at 28% 55%,
      color-mix(in srgb, var(--glow-color, var(--t-accent)) calc(18% * var(--p-glow-scale)), transparent) 0%,
      transparent 70%
    );
    animation: fade-in var(--dur-sheen) var(--ease-out) backwards;
  }
  .sec-hero__inner {
    position: relative;
    z-index: 2;
    max-width: 1280px;
    width: 100%;
    margin: 0 auto;
  }
  .sec-hero__content {
    max-width: 70%;
    display: flex;
    flex-direction: column;
    gap: var(--size-5);
  }
  /* Eyebrow — IBM Plex Mono tracked caps in the light accent step (over a dark
     scrim the AA-passing choice is always the light step, both modes). Staged
     reveal: eyebrow → heading → sub → CTAs, one easing signature, all backwards. */
  .sec-hero__eyebrow {
    font-family: var(--font-family-eyebrow);
    font-size: var(--font-size-sm);
    font-weight: var(--font-medium);
    letter-spacing: var(--tracking-wider);
    text-transform: var(--case-eyebrow);
    color: var(--text-accent-soft);
    animation: fade-in var(--dur-panel) var(--ease-out) backwards;
  }
  /* desktop.hero — Fraunces at weight 400, mixed case, negative tracking:
     scale carries the drama, weight stays disciplined (the E-Ture mechanism
     Javi responds to, transferred to the serif). */
  .sec-hero__heading {
    font-family: var(--font-family-display);
    font-size: var(--font-size-9xl);
    font-weight: var(--font-normal);
    line-height: var(--lh-xs);
    letter-spacing: var(--tracking-tighter);
    text-transform: var(--case-hero);
    /* A line that must wrap breaks into even halves ("Donde crecen / los
       deportistas"), never a word left alone on the tail. It touches only a line
       that already wraps; a line that fits is laid out exactly as before. */
    text-wrap: balance;
    color: var(--t-hero-text);
    margin: 0;
    animation: rise-bloom var(--dur-entrance) var(--ease-out) backwards;
  }
  /* HERO LINES FIT THE COLUMN ON A LAPTOP (George, 2026-10-03: "the third line cuts
     in half"). The hero heading is written as short lines and set at 96px, but the
     text column is 70% of the 1280px inner width: at most 896px at 1440, and
     0.7 x (width - 160) below that. "Entrenamos deportistas." is 961px at 96, so the
     Spanish home broke it into ENTRENAMOS / DEPORTISTAS at 1440; English broke
     "We Educate Students." (814px) the same way at 1280 and 1180. The device scale
     further down only starts at 900, so nothing stepped the heading down on a
     laptop. It now steps down the existing size scale, one rung at the widths
     where the longest line stops fitting. Measured on every hero heading of both
     sites (32 academy + 30 hub pages), the longest line at 96 / 72 / 60 / 48px is
     English 818 / 612 / 509 / 406 and Spanish 1136 / 850 / 707 / 564:
       English  96  from 1360     column 840 holds 818 (the 1440 look is unchanged)
                72  1060-1359     column 630 holds 612
                60  901-1059      column 519 holds 509 (tablet.hero, as below 900)
       Spanish  72  from 1400     column 868 holds 850 (tokens.json desktop.hero)
                60  1180-1399     column 714 holds 707
                48  641-1179      mobile.hero; the longest line (564) fits from 966
     Spanish runs a rung lower because its lines run 20-40% longer than the English
     they translate. The floor is the scale's own: 60px above 900 for English, 48px
     (mobile.hero) for Spanish, whose longest lines do not fit at 60. The phone
     steps below 640 are untouched.
     This block sits HERE, before .sec-hero__heading--mobile, on purpose: that
     modifier is declared after it and wins by source order, so the showcase's phone
     frame keeps its 48px in a laptop-width window. */
  @media (min-width: 901px) and (max-width: 1359px) {
    .sec-hero__heading { font-size: var(--font-size-8xl); }
  }
  @media (min-width: 901px) and (max-width: 1059px) {
    .sec-hero__heading { font-size: var(--font-size-7xl); }
  }
  @media (min-width: 1400px) {
    :lang(es) .sec-hero__heading { font-size: var(--font-size-8xl); }
  }
  @media (min-width: 641px) and (max-width: 1399px) {
    :lang(es) .sec-hero__heading { font-size: var(--font-size-7xl); }
  }
  @media (min-width: 641px) and (max-width: 1179px) {
    :lang(es) .sec-hero__heading { font-size: var(--font-size-6xl); }
  }
  /* body.L — Inter, 18px, xl line-height */
  .sec-hero__sub {
    font-family: var(--font-family-body);
    font-size: var(--font-size-lg);
    line-height: var(--lh-xl);
    color: var(--t-hero-body-text);
    margin: 0;
    max-width: 640px;
    animation: rise-in var(--dur-entrance) var(--ease-out) backwards;
    animation-delay: var(--dur-micro);
  }
  /* body.M — Roboto, var(--size-3p5), xl line-height — text.caption in both modes */
  .sec-hero__caption {
    font-family: var(--font-family-body);
    font-size: var(--font-size-sm);
    line-height: var(--lh-xl);
    color: var(--gray-cool-300);
    margin: 0;
  }
  .sec-hero__ctas {
    display: flex;
    gap: var(--size-3);
    flex-wrap: wrap;
    animation: rise-in var(--dur-entrance) var(--ease-out) backwards;
    animation-delay: var(--dur-panel);
  }
  .sec-hero__ctas--stack {
    flex-direction: column;
    gap: var(--size-2p5);
  }
  .sec-hero__inner--mobile {
    padding: var(--size-8) var(--size-4) var(--size-10);
  }
  /* Mobile content: always full width — no percentage constraint */
  .sec-hero__inner--mobile .sec-hero__content {
    max-width: 100%;
  }
  /* mobile.hero: the display face at 48px, per Global.mobile.hero in tokens.json
     and the production phone step (2026-09-16). This modifier said 60px while the
     token said 48, so the showcase's phone frame drew a larger hero than a phone
     ever showed. */
  .sec-hero__heading--mobile {
    font-size: var(--font-size-6xl);
  }
  /* ── 2. FEATURE GRID ──────────────────────────────────────────────── */

  /* ── EMBLEM ──────────────────────────────────────────────────────────────
     A mark explained by the notes set around it, after the brochure's own badge
     page (George, 2026-09-24: "put the headings and description above, then put
     the logo in the center. Place the description around the logo"). The header
     leads, the mark sits in the middle of the stage, and the notes flank it two
     two to a side.

     Three columns rather than absolute placement, so a note that runs long grows
     its own row instead of colliding with the mark. On a phone the stage becomes
     one column with the mark first, because a ring of notes around a centred
     mark has no meaning at 375px — the notes simply read in order. */
  .sec-emblem {
    background: var(--t-surface);
    padding: var(--size-24) var(--size-20);
  }
  /* Flex column with centred items, exactly as .sec-feature__inner does it: the
     section-hdr is a 540px block with no auto margins, so in a plain block
     container it sits flush left instead of under the mark. */
  .sec-emblem__inner {
    max-width: 1280px; margin: 0 auto;
    display: flex; flex-direction: column; align-items: center;
  }
  .sec-emblem__stage {
    margin-top: var(--size-12);
    width: 100%;
    display: grid;
    grid-template-columns: 1fr minmax(180px, 0.8fr) 1fr;
    gap: var(--size-6) var(--size-10);
    align-items: start;
    justify-items: stretch;
  }
  /* The mark spans the two note rows, so it is centred against them however
     tall they turn out to be. */
  .sec-emblem__mark {
    grid-column: 2; grid-row: 1 / span 2;
    align-self: center;
    width: 100%; aspect-ratio: 1 / 1;
    background-repeat: no-repeat; background-position: center;
    background-size: contain;
  }
  .sec-emblem__note {
    background: var(--t-surface-card);
    border: var(--stroke-xs) solid var(--t-border);
    border-radius: var(--radius-card);
    padding: var(--size-5) var(--size-6);
    display: flex; flex-direction: column; gap: var(--size-2);
  }
  .sec-emblem__label {
    font-family: var(--font-family-heading);
    font-size: var(--font-size-lg); line-height: var(--lh-sm);
    color: var(--t-text); margin: 0;
  }
  .sec-emblem__body {
    font-size: var(--font-size-sm); line-height: var(--lh-lg);
    color: var(--t-text-body); margin: 0;
  }
  /* Narrow: the mark goes on top across the full width and the notes fall into a
     2x2 beneath it (George, 2026-09-24). A single stacked column pushed the last
     note most of a screen below the mark it describes; 2x2 keeps the whole set in
     view with the mark. The gap tightens, because two columns inside a 375px
     phone gutter leave about 150px each. */
  @media (max-width: 900px) {
    /* minmax(0,1fr), not 1fr: a grid item's implicit min-width is auto, so the
       longest unbreakable word in a note widened its column and ran the stage
       2px past a 375px viewport (measured 377). */
    .sec-emblem__stage {
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: var(--size-4);
    }
    .sec-emblem__mark {
      grid-column: 1 / -1; grid-row: 1;
      max-width: 220px; margin: 0 auto var(--size-4);
    }
    .sec-emblem__note { grid-column: auto; grid-row: auto; }
  }
  .sec-feature {
    background: var(--t-surface);
    padding: var(--size-24) var(--size-20);
  }
  .sec-feature__inner {
    max-width: 1280px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: var(--size-10);
  }
  /* Card rows: FLEX, not grid, and CENTERED (George, 2026-08-27 — CONFIRM-LIST
     dimension C). A CSS grid cannot centre an incomplete last row: with 1fr
     tracks the columns fill the container, so a 3-column grid holding 4 cards
     leaves the fourth hanging at the left edge. Flex wrapping can, because
     justify-content applies per line.

     The basis is the exact column width — (100% - the gaps) / n — with
     flex-grow: 0 so a short line does NOT stretch to fill. That gives both
     behaviours at once: a FULL line still spans the container edge to edge
     (n * basis + (n-1) * gap == 100%), and a SHORT line is centred.

     flex-grow must be 0 explicitly: .feature-card, .pillar-card and .venue-card
     each still declare `flex: 1; min-width: …` from an earlier flex layout, and
     those would otherwise stretch a two-card line across the whole row. This
     rule is later in the file at equal specificity, so it wins. min-width: 0
     lets a card shrink below that legacy min rather than overflow. */
  .sec-feature__grid {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--size-5);
  }
  .sec-feature__grid > * {
    flex: 0 1 calc((100% - 2 * var(--size-5)) / 3);
    min-width: 0;
  }
  /* Four across is the cap (George, 2026-08-27). Anything beyond four wraps and
     the short line centres itself. */
  .sec-feature__grid--4 > * {
    flex-basis: calc((100% - 3 * var(--size-5)) / 4);
  }
  /* --pair: a two-up block that carries a photograph or a list — two venue
     cards, two audience cards. Two across from 641px, one per line on a phone.
     Declared HERE, before the 640px block below, so that block's base stack
     wins at equal specificity; the --2 modifier further down is the other
     two-up, the one that stays 2 x 2 on a phone (short fact cards). */
  .sec-feature__grid--pair > * {
    flex-basis: calc((100% - var(--size-5)) / 2);
  }
  .sec-feature__grid--mobile {
    gap: var(--size-3);
  }
  .sec-feature__grid--mobile > * {
    flex-basis: 100% !important;
  }
  .sec-feature--mobile {
    padding: var(--size-8) var(--size-4) !important;
  }
  .sec-feature--mobile .sec-feature__inner {
    gap: var(--size-6);
  }
  /* ── 3. TESTIMONIAL ───────────────────────────────────────────────── */

  .sec-testimonial {
    background: var(--t-surface-card);
    padding: var(--size-24) var(--size-20);
  }
  /* Section-level card bg: use bg.card.dark token (tinted, contrast vs section bg) */
  .sec-testimonial .quote-block {
    background: var(--t-card-dark);
  }
  /* Quote text uses standard body token on card bg (not inverse) */
  .sec-testimonial .quote-block__text {
    color: var(--t-text-body);
  }
  /* ...and so must the mark. The base component's mark is --t-text-accent-inv, an
     accent step chosen to sit on the pinned-dark card; against this section's light
     tinted card it measured 1.41:1 and effectively disappeared. --t-eyebrow-text is
     the text-scale accent role that holds AA on light canvases (§4.1). */
  .sec-testimonial .quote-block__mark {
    color: var(--t-eyebrow-text);
  }
  .sec-testimonial__inner {
    max-width: 1280px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: var(--size-10);
  }
  /* Carousel container clips overflowing cards */
  .sec-testimonial__carousel { overflow: hidden; }
  .sec-testimonial__track {
    display: flex;
    gap: var(--size-5);
    transition: transform var(--dur-panel) var(--ease-out);
    will-change: transform;
  }
  /* Desktop: 3 cards visible — each card takes (100% - 2 gaps) / 3 */
  .sec-testimonial__track .quote-block {
    flex: 0 0 calc((100% - 2 * var(--size-5)) / 3);
    min-width: 0;
  }
  /* Two quotes on the three-up track sit centred instead of leaving the third
     slot empty (academy /alumni, 2026-10-01). Above 700px only: below it the
     track is a one-up carousel and must start at its first card. */
  @media (min-width: 701px) {
    .sec-testimonial__track:not(:has(> .quote-block:nth-child(3))) { justify-content: center; }
  }
  /* Mobile: 1 card fills full width */
  .sec-testimonial--mobile .sec-testimonial__track .quote-block {
    flex: 0 0 100%;
  }
  /* Nav row */
  .sec-testimonial__nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--size-4);
    margin-top: var(--size-6);
  }
  .carousel-btn {
    width: var(--size-10);
    height: var(--size-10);
    border-radius: var(--radius-control);
    border: var(--basic-button-stroke-sm) solid var(--t-border);
    background: var(--t-surface-card);
    color: var(--t-text);
    cursor: pointer;
    font-size: var(--font-size-lg);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background var(--dur-micro);
    flex-shrink: 0;
  }
  .carousel-btn:hover:not(:disabled) { background: var(--t-surface-alt); }
  .carousel-btn:focus-visible,
  .carousel-dot:focus-visible,
  .icon-btn:focus-visible,
  .info-banner__dismiss:focus-visible { outline: var(--stroke-sm) solid var(--border-focus); outline-offset: var(--size-0p5); }
  .carousel-btn:disabled { opacity: 0.35; cursor: not-allowed; }
  /* gap 0: the dots now carry their own 24px hit box (see .carousel-dot), which
     already separates the visible 8px dots — an extra gap would spread them apart. */
  .carousel-dots { display: flex; gap: var(--size-0); align-items: center; }
  /* The dot READS as 8px but must be TAPPABLE at 24px — WCAG 2.5.8 (AA) sets a
     24x24 minimum and the bare dot measured 12x12. The box is the target; the
     ::before is the visible dot, so the design is unchanged. */
  .carousel-dot {
    width: var(--size-6);
    height: var(--size-6);
    border-radius: 50%;
    border: none;
    padding: 0;
    background: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }
  .carousel-dot::before {
    content: '';
    width: var(--size-2);
    height: var(--size-2);
    border-radius: 50%;
    background: var(--t-border);
    transition: background var(--dur-micro), transform var(--dur-micro);
  }
  .carousel-dot--active::before { background: var(--t-accent); transform: scale(1.5); }
  .sec-testimonial__stats {
    display: flex;
    justify-content: center;
    gap: var(--size-5);
    flex-wrap: wrap;
  }
  .sec-testimonial__stats--mobile {
    gap: var(--size-3);
    justify-content: center;
  }
  /* ⚠ DECLARED AFTER `.sec-testimonial__stats`, DELIBERATELY. Both selectors are
     one class, so source order decides — and written up beside `.stat-block`
     where it reads best, the band's `gap: 0` lost to the row's `gap: var(--size-5)`
     further down the file. With a 20px gap surviving, two 50% cells no longer fit
     one line and the phone band silently went one-up. Keep modifiers below the
     thing they modify. */
  /* ── STAT BAND ──────────────────────────────────────────────────────────
     Opt-in on the ROW: takes the card chrome off every block inside it and
     runs them as one band — oversized tabular numerals, hairlines between,
     wide-tracked micro-labels, rules closing the band top and bottom. This is
     the shape for a section where the numbers ARE the content (the hub home's
     proof row); the boxed default stays right for a stat row that sits inside
     another section's flow, so both remain. Scale and rules carry it, not a
     second colour — the number already holds the one accent.
     A band cell does NOT lift on hover: the default 4px rise would pull one
     cell out of a continuous rule line, and these are type, not cards. */
  .sec-testimonial__stats--band {
    width: 100%; gap: 0; align-items: stretch;
    border-top: var(--stroke-xs) solid var(--t-border);
    border-bottom: var(--stroke-xs) solid var(--t-border);
  }
  .sec-testimonial__stats--band > .stat-block {
    flex: 1 1 0; min-width: 0;
    background: none; border: 0; border-radius: 0;
    padding: var(--size-8) var(--size-4); gap: var(--size-2);
  }
  .sec-testimonial__stats--band > .stat-block + .stat-block {
    border-left: var(--stroke-xs) solid var(--t-border);
  }
  .sec-testimonial__stats--band > .stat-block:hover { transform: none; }
  .sec-testimonial__stats--band .stat-block__number { font-size: var(--font-size-7xl); }
  .sec-testimonial__stats--band .stat-block__label  { letter-spacing: var(--tracking-wider); }
  .sec-testimonial--mobile {
    padding: var(--size-8) var(--size-4) !important;
  }
  .sec-testimonial--mobile .sec-testimonial__inner {
    gap: var(--size-6);
  }
  /* Quote attribution (new sub-elements added for sections) */
  .quote-block__attribution {
    display: flex;
    flex-direction: column;
    gap: var(--size-0p5);
    margin-top: var(--size-3);
    padding-top: var(--size-3);
    border-top: var(--basic-button-stroke-sm) solid var(--t-border-soft);
  }
  .quote-block__name {
    font-family: var(--font-family-label);
    font-size: var(--font-size-sm);
    font-weight: var(--font-bold);
    color: var(--t-text-body);
  }
  .quote-block__meta {
    font-family: var(--font-family-caption);
    font-size: var(--font-size-xs);
    color: var(--t-text-body);
  }
  /* ── 4. CTA BANNER ───────────────────────────────────────────────── */

  /* Closing CTA — value-step wells, never a hue band (§6: product hues live in
     content, not section backgrounds). The --accent variant is an accent-TINTED
     deep well; CTAs speak the monochrome outline grammar. Backgrounds are
     PINNED dark (a deep well must not flip to a light band under theme-*-dark). */
  .sec-cta-banner {
    position: relative;
    overflow: hidden;
    padding: var(--size-24) var(--size-20);
    background: var(--gray-warm-900);
  }
  .sec-cta-banner--accent {
    background: color-mix(in srgb, var(--t-accent-darker) 35%, var(--gray-warm-900));
  }
  /* A photographic closing banner. The image goes on its own layer under a scrim
     rather than on the section, so the accent tint above still reads through it
     and the text keeps its contrast whatever photograph is chosen — the banner
     carries two CTAs and they cannot be allowed to fall onto a bright patch. */
  .sec-cta-banner__bg {
    position: absolute; inset: 0;
    background-size: cover; background-position: center;
    z-index: 0;
  }
  .sec-cta-banner__bg::after {
    content: '';
    position: absolute; inset: 0;
    background: linear-gradient(
      90deg,
      color-mix(in srgb, var(--gray-warm-900) 88%, transparent) 0%,
      color-mix(in srgb, var(--gray-warm-900) 72%, transparent) 55%,
      color-mix(in srgb, var(--gray-warm-900) 55%, transparent) 100%
    );
  }
  .sec-cta-banner__inner { position: relative; z-index: 1; }
  .sec-cta-banner--inverse {
    background: var(--gray-warm-900);
  }
  .sec-cta-banner__inner {
    position: relative;
    z-index: 1;
    max-width: 1280px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--size-8);
  }
  .sec-cta-banner__content {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--size-2);
  }
  /* A two-sentence headline breaks BETWEEN its sentences, not inside the second
     (George, 2026-10-03). Beside two long button labels the text column is only
     ~300px at 900-1100, and "One Team. One Dream." broke as "One Team. One /
     Dream." (the same at 320 on a phone), "Plan Your Summer in Castellón" as
     "...in / Castellón". `balance` evens the lines, so the break falls at the
     sentence; `pretty` keeps one word from being left alone on the body's last
     line. Both act only on text that already wraps. */
  .sec-cta-banner__heading {
    font-family: var(--font-family-heading);
    font-size: var(--font-size-5xl);
    font-weight: var(--font-smibold);
    line-height: var(--lh-sm);
    letter-spacing: var(--tracking-tight);
    text-transform: var(--case-header);
    text-wrap: balance;
    color: var(--t-text-inv);
    margin: 0;
  }
  .sec-cta-banner__body {
    font-family: var(--font-family-body);
    font-size: var(--font-size-base);
    line-height: var(--lh-xl);
    text-wrap: pretty;
    color: var(--t-text-inv-body);
    margin: 0;
  }
  .sec-cta-banner__ctas {
    display: flex;
    gap: var(--size-3);
    flex-shrink: 0;
  }
  .sec-cta-banner__ctas--stack {
    flex-direction: column;
    gap: var(--size-2p5);
    width: 100%;
  }
  /* ── 4b. PHILOSOPHY CHAPEL ───────────────────────────────────────── */
  /* GLOW MOMENT 2 of 2 — the page's deepest recessed well. True velvet:
     layered jewel vignette + grain + slow bloom (E-Ture's flat radial done
     right, IDENTITY-BRIEF §3.1 "do better"). Anatomy: eyebrow → one display
     line → hairline rule → short credo → single outline CTA → stat trio. */
  .sec-chapel {
    position: relative;
    overflow: hidden;
    /* Pinned dark: the deepest well must not flip light under theme-*-dark
       (--t-surface-inv resolves to gray-warm-50 there) */
    background: color-mix(in srgb, var(--base-black) var(--opacity-50), var(--gray-warm-900));
    padding: var(--size-32) var(--size-20);
  }
  /* Layered jewel vignette — two radial passes of the product hue, breathing in
     over the sheen tempo. Slots: --glow-color; knob: --p-glow-scale. */
  .sec-chapel::before {
    content: '';
    position: absolute; inset: 0; pointer-events: none;
    background:
      radial-gradient(ellipse 55% 60% at 50% 42%,
        color-mix(in srgb, var(--glow-color, var(--t-accent)) calc(22% * var(--p-glow-scale)), transparent) 0%,
        transparent 70%),
      radial-gradient(ellipse 90% 100% at 50% 110%,
        color-mix(in srgb, var(--glow-color, var(--t-accent)) calc(10% * var(--p-glow-scale)), transparent) 0%,
        transparent 60%);
    animation: fade-in var(--dur-sheen) var(--ease-out) backwards;
  }
  /* Grain — feTurbulence at whisper opacity so the well reads as material */
  .sec-chapel::after {
    content: '';
    position: absolute; inset: 0; pointer-events: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
    opacity: var(--opacity-5);
    mix-blend-mode: overlay;
  }
  .sec-chapel .pitch-lines { opacity: 0.5; }
  .sec-chapel__inner {
    position: relative; z-index: 1;
    max-width: 780px; margin: 0 auto;
    display: flex; flex-direction: column; align-items: center; text-align: center;
    gap: var(--size-5);
  }
  .sec-chapel__eyebrow {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-sm); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wider);
    color: var(--text-accent-soft);
  }
  .sec-chapel__heading {
    font-family: var(--font-family-display); font-size: var(--font-size-8xl); line-height: var(--lh-xs);
    font-weight: var(--font-normal); letter-spacing: var(--tracking-tighter);
    text-transform: var(--case-hero); color: var(--t-text-inv); margin: 0;
  }
  .sec-chapel__rule {
    display: block; width: var(--size-16); height: var(--size-px);
    background: var(--text-accent-soft);
    transform-origin: center;
    animation: draw-line var(--dur-entrance) var(--ease-out) backwards;
  }
  .sec-chapel__credo {
    font-family: var(--font-family-body); font-size: var(--font-size-lg); line-height: var(--lh-xl);
    color: var(--t-text-inv-body); max-width: 560px; margin: 0;
  }
  .sec-chapel__cta { margin-top: var(--size-2); }
  .sec-chapel__stats {
    display: flex; justify-content: center; gap: var(--size-5); flex-wrap: wrap;
    margin-top: var(--size-6);
  }
  .sec-chapel__stats--stack { flex-direction: column; width: 100%; }
  .sec-chapel--mobile { padding: var(--size-20) var(--size-4); }
  .sec-chapel--mobile .sec-chapel__heading { font-size: var(--font-size-6xl); }
  /* ── 5. PARTNERS BANNER ──────────────────────────────────────────── */

  .sec-partners {
    background: var(--t-surface);
    padding: var(--size-10) var(--size-20);
    border-top: var(--basic-button-stroke-sm) solid var(--t-border);
    border-bottom: var(--basic-button-stroke-sm) solid var(--t-border);
  }
  .sec-partners__inner {
    max-width: 1280px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--size-6);
  }
  .sec-partners__label {
    font-family: var(--font-family-eyebrow);
    font-size: var(--font-size-xs);
    font-weight: var(--font-medium);
    letter-spacing: var(--tracking-wider);
    text-transform: uppercase;
    color: var(--t-text-caption);
  }
  /* Partners logo carousel */
  .sec-partners__carousel { overflow: hidden; width: 100%; }
  .sec-partners__track {
    display: flex;
    gap: var(--size-6);
    transition: transform var(--dur-panel) var(--ease-out);
    will-change: transform;
  }
  .sec-partners__track .sec-partners__logo {
    flex: 0 0 calc((100% - 4 * var(--size-6)) / 5);
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--size-3) 0;
  }
  .sec-partners--mobile .sec-partners__track {
    gap: var(--size-5);
  }
  .sec-partners--mobile .sec-partners__track .sec-partners__logo {
    flex: 0 0 calc((100% - 2 * var(--size-5)) / 3);
  }
  .sec-partners__logo img {
    width: 100%;
    max-width: var(--size-16);
    height: var(--size-14);
    object-fit: contain;
    filter: var(--t-logo-filter);
    display: block;
  }
  .sec-partners__nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--size-4);
    margin-top: var(--size-5);
  }
  /* ── 6. TEXT + IMAGE SPLIT ───────────────────────────────────────── */

  .sec-split {
    background: var(--t-surface);
    padding: var(--size-24) var(--size-20);
  }
  .sec-split:nth-child(even) {
    background: var(--t-surface-card);
  }
  .sec-split__inner {
    max-width: 1280px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    gap: var(--size-12);
  }
  .sec-split--reversed .sec-split__inner {
    flex-direction: row-reverse;
  }
  .sec-split__text {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--size-5);
  }
  .sec-split__tags {
    display: flex;
    gap: var(--size-2);
    flex-wrap: wrap;
  }
  .sec-split__ctas {
    display: flex;
    gap: var(--size-3);
    flex-wrap: wrap;
  }
  /* ── Split as a profile (George, 2026-09-18, academy /staff: the founder's
     section follows the orientation deck's bio slide) — a caps label over each
     sub-block, a row of club crests and a two-column fact list. Crest heights
     are per logo (--logo-h, inline): a tall shield and a wide mark at one
     height do not read as the same size. Crests keep their original colors. */
  .sec-split__label {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wider); color: var(--t-eyebrow-text);
    margin: 0 0 var(--size-2);
  }
  .sec-split__logos { display: flex; align-items: center; flex-wrap: wrap; gap: var(--size-7); }
  .sec-split__logos > img { height: var(--logo-h, var(--size-14)); width: auto; display: block; }
  .sec-split__facts { display: grid; grid-template-columns: 1fr 1fr; gap: var(--size-5); }
  .sec-split__fact {
    font-family: var(--font-family-body); font-size: var(--font-size-sm); line-height: var(--lh-xl);
    color: var(--t-text-body); margin: 0;
  }
  @media (max-width: 640px) {
    .sec-split__facts { grid-template-columns: 1fr; }
  }
  .sec-split__media {
    flex: 1;
    min-width: 0;
  }
  .sec-split__img {
    width: 100%;
    aspect-ratio: 4 / 3;
    border-radius: var(--radius-card);
    background: linear-gradient(135deg, var(--t-accent-soft) 0%, var(--t-accent-strong) 100%);
    /* Production: set background-image: url(...) inline to swap in a real photo. */
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    border: var(--basic-button-stroke-sm) solid var(--t-border-soft);
  }
  /* A person in the frame: 4:5 instead of 4:3, anchored high, so a
     head-and-shoulders photo keeps the top of the head in any crop. */
  .sec-split__img--portrait { aspect-ratio: 4 / 5; background-position: center 20%; }
  /* A mark, not a photograph (a club crest): shown whole on the card surface,
     sized by height so it fits the 4:3 frame and the phone's 16:9 alike. */
  .sec-split__img--contain {
    background-color: var(--t-surface-card); background-size: auto 68%;
    background-repeat: no-repeat; background-position: center;
    border: var(--stroke-xs) solid var(--t-border);
  }
  .sec-split__img--mobile {
    width: 100%;
    aspect-ratio: 16 / 9;
    border-radius: var(--radius-card);
  }
  /* ── 7. IMAGE GRID + TEXT ────────────────────────────────────────── */

  .sec-image-grid {
    padding: var(--size-24) var(--size-20);
  }
  /* On white rather than the page canvas. On the academy this is also what
     keeps the page grid out of a band (George, 2026-09-17: the On and Off the
     Field band should not show it) — see `.has-page-grid`. */
  .sec-image-grid--white {
    background: var(--t-surface-card);
  }
  .sec-image-grid__inner {
    max-width: 1280px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    gap: var(--size-12);
  }
  .sec-image-grid--reversed .sec-image-grid__inner {
    flex-direction: row-reverse;
  }
  .sec-image-grid__media {
    flex: 1;
    min-width: 0;
  }
  .sec-image-grid__grid {
    display: grid;
    grid-template-columns: 2fr 1fr;
    grid-template-rows: auto auto;
    gap: var(--size-2p5);
  }
  .sec-image-grid__grid--mobile {
    width: 100%;
    grid-template-columns: 1fr 1fr !important;
    grid-template-rows: auto auto !important;
  }
  .sec-image-grid__img {
    border-radius: var(--radius-card);
    background: linear-gradient(135deg, var(--t-accent-soft) 0%, var(--t-accent-strong) 100%);
    /* Production: set background-image: url(...) inline to swap in a real photo. */
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    border: var(--basic-button-stroke-sm) solid var(--t-border-soft);
    aspect-ratio: 4 / 3;
  }
  .sec-image-grid__img--tall {
    grid-row: 1 / 3;
    aspect-ratio: auto;
    min-height: var(--size-28);
  }
  .sec-image-grid__img--wide {
    grid-column: 1 / 3;
    aspect-ratio: 16 / 7;
  }
  .sec-image-grid__text {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--size-5);
  }
  .sec-image-grid__ctas {
    display: flex;
    gap: var(--size-3);
    flex-wrap: wrap;
  }
  /* ── 8. FORM SECTION ─────────────────────────────────────────────── */

  .sec-form {
    background: var(--t-surface);
    padding: var(--size-24) var(--size-20);
    /* The form is the target of in-page CTAs (#application, #request, #reserve…).
       Without an offset the jump lands the section's top edge at the viewport
       top and the sticky nav (about 69px on a phone) covers the eyebrow. */
    scroll-margin-top: var(--size-20);
  }
  .sec-form__inner {
    max-width: 780px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: var(--size-8);
  }
  .sec-form__header {
    width: 100%;
    text-align: center;
  }
  .sec-form__header .section-hdr {
    max-width: 100%;
    width: 100%;
  }
  .sec-form__form {
    display: flex;
    flex-direction: column;
    gap: var(--size-5);
  }
  .sec-form__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--size-4);
  }
  .sec-form__grid--mobile {
    grid-template-columns: 1fr !important;
  }
  .sec-form__field--full {
    grid-column: 1 / -1;
  }
  .sec-form__trust {
    display: flex;
    justify-content: center;
    gap: var(--size-6);
    flex-wrap: wrap;
  }
  .sec-form__trust-item {
    display: flex;
    align-items: center;
    gap: var(--size-1p5);
    font-family: var(--font-family-caption);
    font-size: var(--font-size-xs);
    color: var(--t-text-caption);
  }
  .sec-form__trust-item svg {
    color: var(--t-eyebrow-text);
    flex-shrink: 0;
  }
  .sec-form__nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--size-3);
    padding-top: var(--size-2);
  }
  .sec-form__nav-right {
    display: flex;
    gap: var(--size-3);
  }
  .sec-form__nav--mobile {
    flex-direction: column;
    gap: var(--size-2);
  }
  .sec-form__nav--mobile .btn {
    width: 100%;
  }
  .sec-form__disclaimer {
    font-family: var(--font-family-caption);
    font-size: var(--font-size-xs);
    color: var(--t-text-caption);
    text-align: center;
    margin: 0;
    line-height: var(--lh-xl);
  }
  .sec-form__link {
    color: var(--t-eyebrow-text);
    text-decoration: underline;
  }
  /* Multi-step wizard: progress indicator */
  .sec-form__progress {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--size-2);
    flex-wrap: wrap;
  }
  .sec-form__progress-step {
    display: flex;
    align-items: center;
    gap: var(--size-2);
    font-family: var(--font-family-label);
    font-size: var(--font-size-xs);
    font-weight: var(--font-smibold);
    color: var(--t-text-caption);
  }
  .sec-form__progress-dot {
    width: var(--size-6);
    height: var(--size-6);
    border-radius: var(--radius-pill);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--t-surface-alt);
    border: var(--basic-button-stroke-sm) solid var(--t-border-strong);
    color: var(--t-text-caption);
    transition: background var(--dur-micro), border-color var(--dur-micro), color var(--dur-micro);
  }
  .sec-form__progress-step--active .sec-form__progress-dot {
    background: var(--t-accent);
    border-color: var(--t-accent);
    color: var(--base-white);
  }
  .sec-form__progress-step--active { color: var(--t-text); }
  .sec-form__progress-step--done .sec-form__progress-dot {
    background: var(--t-accent-soft);
    border-color: var(--t-accent);
    color: var(--t-accent-strong);
  }
  .sec-form__progress-line {
    width: var(--size-8);
    height: var(--basic-button-stroke-sm);
    background: var(--t-border);
    flex-shrink: 0;
  }
  /* Multi-step wizard: step panels */
  .sec-form__step { display: none; flex-direction: column; gap: var(--size-5); }
  .sec-form__step--active { display: flex; }
  .sec-form__step-label {
    font-family: var(--font-family-label);
    font-size: var(--font-size-xs);
    font-weight: var(--font-smibold);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--t-text-accent-strong);
  }
  /* Submission success state */
  .sec-form__success {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--size-4);
    padding: var(--size-6) 0;
  }
  .sec-form__success-icon {
    width: var(--size-16);
    height: var(--size-16);
    border-radius: var(--radius-pill);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--t-accent-soft);
    color: var(--t-accent-strong);
  }
  .sec-form__success-icon svg { width: var(--size-8); height: var(--size-8); }
  .sec-form__success-title {
    font-family: var(--font-family-heading);
    font-size: var(--font-size-2xl);
    text-transform: uppercase;
    font-weight: var(--font-bold);
    color: var(--t-text);
    margin: 0;
  }
  .sec-form__success-body {
    font-family: var(--font-family-body);
    font-size: var(--font-size-base);
    color: var(--t-text-body);
    line-height: var(--lh-xl);
    max-width: 46ch;
    margin: 0;
  }
  .sec-form__success-meta {
    display: flex;
    flex-direction: column;
    gap: var(--size-2);
    width: 100%;
    max-width: 360px;
    margin-top: var(--size-2);
  }
  .sec-form__success-meta-item {
    display: flex;
    align-items: center;
    gap: var(--size-2p5);
    padding: var(--size-2p5) var(--size-3p5);
    background: var(--t-surface-alt);
    border-radius: var(--size-2);
    font-family: var(--font-family-body);
    font-size: var(--font-size-sm);
    color: var(--t-text-body);
    text-align: left;
  }
  .sec-form__success-meta-item svg {
    color: var(--t-accent);
    flex-shrink: 0;
  }
  /* Art. 13 GDPR notice — the block of processing information that must sit
     WITH the form collecting the data, not behind a link to the privacy
     policy. Long by nature: it is legal text, so it is set at body size with
     generous leading rather than shrunk into fine print. */
  .privacy-notice {
    border: var(--stroke-xs) solid var(--t-border-soft);
    border-radius: var(--radius-card);
    background: var(--t-surface-alt);
    padding: var(--size-6);
    display: flex;
    flex-direction: column;
    gap: var(--size-3);
    max-height: 320px;
    overflow-y: auto;
  }
  .privacy-notice__title {
    font-family: var(--font-family-label);
    font-size: var(--font-size-xs);
    font-weight: var(--font-smibold);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--t-text-accent-strong);
    margin: 0;
  }
  .privacy-notice__p {
    font-family: var(--font-family-body);
    font-size: var(--font-size-sm);
    line-height: var(--lh-xl);
    color: var(--t-text-body);
    margin: 0;
  }
  .privacy-notice a { color: var(--t-text-accent); text-decoration: underline; }
  /* A scroll container is only reachable by keyboard if it is focusable, and a
     focusable region needs a visible focus ring. Both are required here
     (WCAG 2.1.1 / 2.4.7) because the notice scrolls. */
  .privacy-notice:focus-visible {
    outline: var(--stroke-sm) solid var(--border-focus);
    outline-offset: var(--size-0p5);
  }
  @media (max-width: 700px) {
    .privacy-notice { padding: var(--size-4); max-height: 260px; }
  }
  /* ── 8b. EMBED SLOT ──────────────────────────────────────────────────
     ONE component, two states, used by every page that hosts a
     Wombo-platform form (/apply, /contact, and the golf/video surfaces
     that follow). The resize contract lives here so the bug that has
     already bitten twice — a fixed min-height iframe cutting off the
     consent tick-box and the submit button — has exactly one place to be
     fixed. See forms/EMBED.md. */
  .embed-slot {
    display: flex;
    flex-direction: column;
    gap: var(--size-5);
  }
  /* MOUNTED: the iframe is sized by postMessage, so it carries no fixed
     height. `min-height` is a first-paint floor only, replaced the moment
     the frame reports its own height. */
  .embed-slot__frame {
    width: 100%;
    border: 0;
    display: block;
    min-height: 640px;
    border-radius: var(--radius-card);
    background: var(--t-surface-card);
  }
  /* GATED: the form does not exist yet. This is a real state with real
     content, never a spinner — the page has to be useful in it. */
  .embed-slot__gate {
    border: var(--stroke-xs) solid var(--t-border-card);
    border-radius: var(--radius-card);
    background: var(--t-surface-card);
    padding: var(--size-10) var(--size-8);
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--size-4);
  }
  .embed-slot__gate-icon {
    width: var(--size-14);
    height: var(--size-14);
    border-radius: var(--radius-pill);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--t-accent-soft);
    color: var(--t-accent-strong);
  }
  .embed-slot__gate-icon svg { width: var(--size-7); height: var(--size-7); }
  .embed-slot__gate-title {
    font-family: var(--font-family-heading);
    font-size: var(--font-size-xl);
    text-transform: uppercase;
    font-weight: var(--font-bold);
    color: var(--t-text);
    margin: 0;
  }
  .embed-slot__gate-body {
    font-family: var(--font-family-body);
    font-size: var(--font-size-base);
    line-height: var(--lh-xl);
    color: var(--t-text-body);
    max-width: 52ch;
    margin: 0;
  }
  .embed-slot__gate-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--size-3);
    justify-content: center;
    margin-top: var(--size-2);
  }
  /* Direct contact details shown inside the gate — /contact's gated state is
     genuinely useful on its own, which is why that page ships indexed. */
  .embed-slot__gate-details {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--size-3);
    width: 100%;
    margin-top: var(--size-2);
    text-align: left;
  }
  .embed-slot__gate-detail {
    display: flex;
    align-items: flex-start;
    gap: var(--size-3);
    padding: var(--size-4);
    border-radius: var(--radius-control);
    background: var(--t-surface-alt);
  }
  .embed-slot__gate-detail svg { color: var(--t-accent); flex-shrink: 0; margin-top: var(--size-0p5); }
  .embed-slot__gate-detail-label {
    display: block;
    font-family: var(--font-family-label);
    font-size: var(--font-size-xs);
    font-weight: var(--font-smibold);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--t-text-caption);
  }
  .embed-slot__gate-detail-value {
    font-family: var(--font-family-body);
    font-size: var(--font-size-sm);
    line-height: var(--lh-lg);
    color: var(--t-text-body);
  }
  .embed-slot__gate-detail-value a { color: var(--t-text-accent); text-decoration: underline; }
  @media (max-width: 700px) {
    .embed-slot__gate { padding: var(--size-8) var(--size-5); }
  }
  /* Skip link — the first focusable thing on every page, visible only when
     focused. Without it keyboard and screen-reader users tab the whole nav
     before reaching a form (WCAG 2.4.1). */
  .skip-link {
    position: absolute;
    left: var(--size-4);
    top: var(--size-4);
    z-index: 200;
    transform: translateY(-200%);
    padding: var(--size-3) var(--size-5);
    border-radius: var(--radius-control);
    background: var(--t-accent);
    color: var(--t-text-on-accent);
    font-family: var(--font-family-label);
    font-size: var(--font-size-sm);
    font-weight: var(--font-smibold);
    text-decoration: none;
    /* Absolutely positioned, so it shrinks to the narrowest word and wraps to
       three lines without this. */
    white-space: nowrap;
  }
  /* `:focus`, not `:focus-visible` — a skip link is only ever reached by
     keyboard or assistive tech, and :focus-visible is not applied to
     programmatic focus, which would leave it invisible in exactly the case
     that matters. */
  .skip-link:focus {
    transform: translateY(0);
    outline: var(--stroke-sm) solid var(--border-focus);
    outline-offset: var(--size-0p5);
  }
  /* ── 9. FOOTER ───────────────────────────────────────────────────── */

  /* The deep-pool close — chrome bg, one value-step past the canvas */
  .sec-footer {
    background: var(--t-chrome-bg);
  }
  .sec-footer__inner {
    max-width: 1280px;
    margin: 0 auto;
    display: flex;
    gap: var(--size-12);
    padding: var(--size-24) var(--size-20) var(--size-10);
    flex-wrap: wrap;
  }
  .sec-footer__brand {
    flex: 2;
    min-width: 180px;
    display: flex;
    flex-direction: column;
    gap: var(--size-3);
  }
  .sec-footer__logo {
    display: flex;
    align-items: center;
  }
  .sec-footer__tagline {
    font-family: var(--font-family-heading);
    font-size: var(--font-size-lg);
    font-weight: var(--font-smibold);
    color: var(--t-chrome-text-body);
    margin: 0;
    line-height: var(--lh-lg);
  }
  .sec-footer__social {
    display: flex;
    gap: var(--size-3);
    margin-top: var(--size-1);
  }
  .sec-footer__social-link {
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--size-8);
    height: var(--size-8);
    border-radius: var(--radius-control);
    color: var(--t-chrome-text-caption);
    border: var(--basic-button-stroke-sm) solid color-mix(in srgb, var(--base-white) var(--opacity-10), transparent);
    transition: color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);
  }
  .sec-footer__social-link:hover {
    color: var(--t-text-accent-inv);
    border-color: var(--t-text-accent-inv);
  }
  .sec-footer__nav-col {
    flex: 1;
    min-width: 120px;
    display: flex;
    flex-direction: column;
    gap: var(--size-2p5);
  }
  .sec-footer__nav-label {
    font-family: var(--font-family-eyebrow);
    font-size: var(--font-size-xs);
    font-weight: var(--font-medium);
    letter-spacing: var(--tracking-wider);
    text-transform: var(--case-eyebrow);
    color: var(--t-chrome-text);
    padding-bottom: var(--size-1);
    border-bottom: var(--stroke-xs) solid color-mix(in srgb, var(--base-white) var(--opacity-10), transparent);
  }
  /* Product-specific nav columns — hide all by default, show per-theme sets */
  .sec-footer__nav-col[data-for] { display: none; }
  /* ── Column visibility per theme ── */
  /* Academy: Academy, Events, Odisea Sports, Company */
  .sec-footer[data-product="academy"] .sec-footer__nav-col[data-for="academy"],
  .sec-footer[data-product="academy"] .sec-footer__nav-col[data-for="events"],
  .sec-footer[data-product="academy"] .sec-footer__nav-col[data-for="odisea"] { display: flex; }
  /* Hub: Company, Academy, Study, Events (no Hub col) */
  .sec-footer[data-product="hub"] .sec-footer__nav-col[data-for="academy"],
  .sec-footer[data-product="hub"] .sec-footer__nav-col[data-for="study"],
  .sec-footer[data-product="hub"] .sec-footer__nav-col[data-for="events"] { display: flex; }
  /* Shop: Shop, Academy, Odisea Sports, Company */
  .sec-footer[data-product="shop"] .sec-footer__nav-col[data-for="shop"],
  .sec-footer[data-product="shop"] .sec-footer__nav-col[data-for="academy"],
  .sec-footer[data-product="shop"] .sec-footer__nav-col[data-for="odisea"] { display: flex; }
  /* OSM: OSM, Academy, Events, Company */
  .sec-footer[data-product="osm"] .sec-footer__nav-col[data-for="osm"],
  .sec-footer[data-product="osm"] .sec-footer__nav-col[data-for="academy"],
  .sec-footer[data-product="osm"] .sec-footer__nav-col[data-for="events"] { display: flex; }
  /* Study: Study, Odisea Sports, Company */
  .sec-footer[data-product="study"] .sec-footer__nav-col[data-for="study"],
  .sec-footer[data-product="study"] .sec-footer__nav-col[data-for="odisea"] { display: flex; }
  /* ── Column order per theme ── */
  /* Brand always first; Company always last by default */
  .sec-footer__brand                           { order: 0; }
  .sec-footer .sec-footer__nav-col:not([data-for]) { order: 99; }
  /* Academy: Academy(1) Events(2) Odisea Sports(3) Company(99) */
  .sec-footer[data-product="academy"] [data-for="academy"] { order: 1; }
  .sec-footer[data-product="academy"] [data-for="events"]  { order: 2; }
  .sec-footer[data-product="academy"] [data-for="odisea"]  { order: 3; }
  /* Hub: Divisions(1) Academy(2) Company(3) Events(4) — master-plan footer order */
  .sec-footer[data-product="hub"] [data-for="academy"] { order: 1; }
  .sec-footer[data-product="hub"] [data-for="study"]   { order: 2; }
  .sec-footer[data-product="hub"] .sec-footer__nav-col:not([data-for]) { order: 3; }
  .sec-footer[data-product="hub"] [data-for="events"]  { order: 4; }
  /* Shop: Shop(1) Academy(2) Odisea Sports(3) Company(99) */
  .sec-footer[data-product="shop"] [data-for="shop"]    { order: 1; }
  .sec-footer[data-product="shop"] [data-for="academy"] { order: 2; }
  .sec-footer[data-product="shop"] [data-for="odisea"]  { order: 3; }
  /* OSM: OSM(1) Academy(2) Events(3) Company(99) */
  .sec-footer[data-product="osm"] [data-for="osm"]     { order: 1; }
  .sec-footer[data-product="osm"] [data-for="academy"] { order: 2; }
  .sec-footer[data-product="osm"] [data-for="events"]  { order: 3; }
  /* Study: Study(1) Odisea Sports(2) Company(99) */
  .sec-footer[data-product="study"] [data-for="study"]  { order: 1; }
  .sec-footer[data-product="study"] [data-for="odisea"] { order: 2; }
  .sec-footer__nav-link {
    font-family: var(--font-family-link);
    font-size: var(--font-size-sm);
    color: var(--t-chrome-text-body);
    text-decoration: none;
    line-height: var(--lh-lg);
    transition: color var(--dur-micro) var(--ease-out);
  }
  .sec-footer__nav-link:hover {
    color: var(--t-text-accent-inv);
  }
  .sec-footer__nav-link:focus-visible { outline: var(--stroke-sm) solid var(--border-focus); outline-offset: var(--size-0p5); }
  .sec-footer__bottom {
    background: color-mix(in srgb, var(--base-black) var(--opacity-25), transparent);
  }
  .sec-footer__bottom-inner {
    max-width: 1280px;
    margin: 0 auto;
    padding: var(--size-4) var(--size-20);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--size-4);
  }
  .sec-footer__copyright {
    font-family: var(--font-family-caption);
    font-size: var(--font-size-xs);
    color: var(--t-chrome-text-caption);
  }
  .sec-footer__lang-switcher {
    display: flex;
    align-items: center;
    gap: var(--size-1p5);
  }
  .sec-footer__lang-sep {
    font-family: var(--font-family-label);
    font-size: var(--font-size-xs);
    color: var(--t-chrome-text-caption);
    opacity: 0.5;
    user-select: none;
  }
  /* inline-flex + center (2026-09-16): as links, EN and ES sat at the top of the
     44px box the touch floor gives them, so on a phone the centered "·" between
     them looked like it had dropped below the line. */
  .sec-footer__lang-btn {
    display: inline-flex; align-items: center; justify-content: center;
    background: none;
    border: none;
    /* EN/ES measured 15x16 — under the WCAG 2.5.8 24x24 minimum. Pad the box out
       to the minimum without changing the type. */
    padding: var(--size-1) var(--size-2p5);
    min-width: var(--size-6);
    min-height: var(--size-6);
    cursor: pointer;
    font-family: var(--font-family-eyebrow);
    font-size: var(--font-size-xs);
    font-weight: var(--font-medium);
    color: var(--t-chrome-text-caption);
    letter-spacing: var(--tracking-wide);
    transition: color var(--dur-micro) var(--ease-out);
  }
  .sec-footer__lang-btn:hover {
    color: var(--t-text-accent-inv);
  }
  .sec-footer__lang-btn--active {
    color: var(--t-chrome-text);
  }
  /* ══════════════════════════════════════════════════════════════════════════
     DOCUMENT COMPONENTS — A4 print / brochure system
     Built from basic components + global --document-* tokens (794×1123px @96dpi).
     FULLY THEME-DRIVEN — no hardcoded brand colour. Accent = --t-accent,
     surfaces = --t-surface / --t-surface-inv / --t-surface-card, text = --t-text*.
     A document inherits the active theme + mode: academy → teal, shop → pink, etc.
     Composite doc blocks (PageHeader, Editorial, Banner, IntroBanner, Partners,
     Statement) live in the Composite tab; section layouts (Cover, ToC, Itinerary)
     render at A4 under the Section tab's Document device.
  ══════════════════════════════════════════════════════════════════════════ */

  /* ── Print-identity carve-out (Phase D2, IDENTITY-BRIEF.md §8.7) ──────────
     The web identity moved to Fraunces/Inter/Newsreader/Plex Mono at Gate G1,
     but the doc-* A4/brochure family deliberately keeps the Anton/Roboto/Inter
     print identity until collateral is re-exported on the new system. Custom
     properties inherit, so re-declaring the font roles on .doc-page re-skins
     everything inside a page — including shared components (tag, icon,
     itinerary-row) used within documents — and nothing outside. The faces are
     self-hosted in base.css alongside the web set. */
  .doc-page, .doc-scope {
    --font-family-display: 'Anton', sans-serif;
    --font-family-body: 'Roboto', sans-serif;
    --font-family-caption: 'Roboto', sans-serif;
    --font-family-link: 'Roboto', sans-serif;
    --font-family-label: 'Roboto', sans-serif;
    --font-family-number: 'Inter Variable', sans-serif;
    --font-family-eyebrow: 'Inter Variable', sans-serif;
    /* The web identity dropped the uppercase display grammar (case tokens now
       resolve to none); print keeps Anton's caps voice for shared components
       rendered inside a document. */
    --case-hero: uppercase;
    --case-header: uppercase;
  }
  /* ── A4 staging + page frame (document section layouts) ──────────────────── */
  .doc-stage {
    background: var(--t-surface); border-radius: var(--size-2);
    padding: var(--size-8); display: flex; flex-wrap: wrap; gap: var(--size-8);
    justify-content: center; align-items: flex-start; overflow-x: auto;
  }
  .doc-page {
    width: var(--document-w); flex: 0 0 var(--document-w); height: var(--document-h);
    background: var(--t-surface); color: var(--t-text); font-family: var(--font-family-body);
    position: relative; box-shadow: 0 var(--size-1) var(--size-7) color-mix(in srgb, var(--base-black) var(--opacity-25), transparent); overflow: hidden;
    display: flex; flex-direction: column;
    padding: var(--document-margin-y) var(--document-margin-x) var(--document-margin-x);  /* top = full margin; sides + bottom = side margin, so the footer sits close to the edge like the side gutters */
  }
  /* ── Three-zone A4 page: header (wrap) · body (fills) · footer (wrap) ─────────
     The page is a vertical flexbox with the A4 margins as padding. The header and
     footer wrap to their content; the body fills the space between; the footer is
     an in-flow flex zone pinned to the bottom (margin-top:auto) — so it is always
     at the bottom and never overlaps content, whatever the content height. */

  /* ── PageHeader — chip + title pill + tag, with tracks beneath ───────────── */
  /* Grid: [chip | pill] over [short track | long track] */
  .doc-header {
    flex: 0 0 auto;
    display: grid; grid-template-columns: var(--size-24) 1fr;
    column-gap: var(--document-header-gap-sm); row-gap: var(--size-2p5);
    align-items: stretch;
  }
  .doc-header__chip {
    grid-column: 1; grid-row: 1; height: var(--size-24);
    background: var(--t-surface-inv); color: var(--t-text-inv);
    border: var(--stroke-sm) solid var(--t-border-card);
    border-radius: var(--document-card-radius-lg);
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-family-display); font-size: var(--font-size-7xl); line-height: var(--lh-none);
  }
  .doc-header__pill {
    grid-column: 2; grid-row: 1; min-height: var(--size-24);
    background: var(--t-surface-card); border-radius: var(--document-card-radius-lg);
    border: var(--stroke-sm) solid var(--t-border-card);
    display: flex; align-items: center; justify-content: space-between;
    padding: 0 var(--size-7); gap: var(--size-4);
  }
  .doc-header__title {
    font-family: var(--font-family-display); font-size: var(--font-size-7xl);
    line-height: var(--lh-none); text-transform: uppercase; color: var(--t-text);
    letter-spacing: var(--tracking-tight);
  }
  .doc-header__tag {
    font-family: var(--font-family-display); font-size: var(--font-size-2xl);
    text-transform: uppercase; color: var(--t-text);
    letter-spacing: var(--tracking-normal); white-space: nowrap;
  }
  .doc-header__track { height: var(--size-3p5); border-radius: var(--basic-tag-radius-pill); }
  .doc-header__track--short { grid-column: 1; grid-row: 2; background: var(--t-surface-card); border: var(--stroke-sm) solid var(--t-border-card); }
  .doc-header__track--long  { grid-column: 2; grid-row: 2; background: var(--t-surface-inv); border: var(--stroke-sm) solid var(--t-border-card); }
  .doc-header__track--outline { background: transparent; border: var(--stroke-sm) solid var(--t-text-caption); border-bottom: 3px solid var(--t-accent); }
  /* The light header pills (number chip + long track) read as the section-light
     white. On dark documents page-inverse resolves to a warm tone, so pin them to
     crisp white there and keep the chip number dark for contrast. */
  [class*="theme-"][class*="-dark"] .doc-header__chip,
  [class*="theme-"][class*="-dark"] .doc-header__track--long { background: var(--base-white); }
  [class*="theme-"][class*="-dark"] .doc-header__chip { color: var(--gray-cool-900); }
  /* ── Page body + footer (section layouts) ────────────────────────────────── */
  .doc-body {
    flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
    gap: var(--document-section-margin-y);
    padding-top: var(--document-section-margin-y); padding-bottom: var(--document-section-margin-y);
  }
  /* Footer is an in-flow zone pinned to the bottom (like the header zone), inside
     the A4 margins. margin-top:auto keeps it at the bottom; its own vertical
     padding keeps the text off the divider above and the page edge below. */
  .doc-footer {
    flex: 0 0 auto; margin-top: auto;
    display: flex; align-items: center; gap: var(--size-3);
    padding-top: var(--size-3p5); padding-bottom: var(--size-2);
    border-top: var(--stroke-xs) solid var(--t-border);
  }
  .doc-footer__shield { height: 26px; width: auto; filter: var(--t-logo-filter); }
  .doc-footer__copy { flex: 1 1 auto; text-align: center; font-size: var(--font-size-sm); color: var(--t-text-caption); }
  .doc-footer__page { font-size: var(--font-size-sm); font-weight: var(--font-bold); color: var(--t-text-caption); min-width: 26px; text-align: right; }
  /* ── Document typography primitives ──────────────────────────────────────── */
  .doc-eyebrow {
    font-family: var(--font-family-display); font-size: var(--font-size-lg);
    text-transform: uppercase; letter-spacing: var(--tracking-wide); color: var(--t-accent); margin: 0;
  }
  .doc-eyebrow--accent { color: var(--t-accent); }
  .doc-heading {
    font-family: var(--font-family-display); font-size: var(--font-size-6xl);
    line-height: var(--lh-xs); text-transform: uppercase; color: var(--t-text-heading);
    letter-spacing: var(--tracking-tight); margin: 0;
  }
  .doc-heading--accent { color: var(--t-accent); }
  .doc-heading--sm { font-size: var(--font-size-3xl); line-height: var(--lh-sm); }
  .doc-text {
    font-family: var(--font-family-body); font-size: var(--font-size-base);
    line-height: var(--lh-xl); color: var(--t-text-body); margin: 0;
  }
  .doc-text + .doc-text { margin-top: var(--size-3); }
  .doc-statement {
    font-family: var(--font-family-display); font-size: var(--font-size-9xl);
    line-height: var(--lh-xs); text-transform: uppercase; color: var(--t-text);
    letter-spacing: var(--tracking-tight); margin: 0;
  }
  .doc-statement--accent { color: var(--t-accent); }
  .doc-statement--inv    { color: var(--t-text-inv); }
  .doc-statement--big    { font-size: var(--font-size-11xl); }
  .doc-statement--sm     { font-size: var(--font-size-8xl); }
  /* ── Image placeholder (showcase stand-in for real photography) ──────────── */
  .doc-img {
    position: relative; overflow: hidden; border-radius: var(--document-card-radius-lg);
    background: linear-gradient(135deg, var(--t-surface-inv) 0%, var(--t-accent-strong) 100%);
  }
  .doc-img::after {
    content: attr(data-label); position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    color: color-mix(in srgb, var(--base-white) var(--opacity-50), transparent); font-family: var(--font-family-label);
    font-size: var(--font-size-xs); font-weight: var(--font-smibold);
    letter-spacing: var(--tracking-wide); text-transform: uppercase;
  }
  /* Drop a real photo into any doc-img / venue-card media placeholder: the <img>
     fills the container (object-fit: cover) and hides the gradient stand-in. */
  .doc-img > img,
  .venue-card__media > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
  .venue-card__media { overflow: hidden; }
  /* Portrait media (staff headshots). The default 140px-tall media band with a
     centred cover crop takes the MIDDLE of a portrait frame — which on a
     head-and-shoulders photo is the chest, not the face. `--portrait` gives the
     band room and anchors the crop to the top of the frame so the face survives.
     Measured before/after on academy/staff: 26 headshots, all cropped at the
     neck under the default. */
  .venue-card--portrait .venue-card__media { height: var(--size-56); }
  .venue-card--portrait .venue-card__media > img { object-position: center top; }
  /* --crest: a club's or a university's mark in place of a photograph, for a
     partner the library has no picture of (made 2026-10-02 for the academy's CSU
     Bakersfield card, before its photographs arrived). The mark sits whole on a light
     surface instead of being cropped like a photo. Put .img-ungraded on the media
     element, not the <img>: the grade's `.venue-card__media > img` outranks a bare
     `.img-ungraded`, while `.img-ungraded img` comes later at equal weight. */
  .venue-card__media--crest { background: var(--t-surface-alt); }
  .venue-card__media--crest > img { object-fit: contain; padding: var(--size-6); }
  /* ── FLIP CARD — a tall portrait card that turns over to show a bio ───────
     George, 2026-09-18 (academy /staff): staff cards taller than the portrait
     venue card, with the person's bio on the back, turned with a flip. The
     turn happens on hover where there is a real pointer, on keyboard focus
     (the card carries tabindex="0"), and on tap where there is no hover — a
     touch screen has nothing to hover, so the page's script toggles
     `.is-flipped` there. Both faces stay in the DOM, so a screen reader reads
     the bio without turning anything. Reduced motion makes the turn instant:
     the global reduced-motion rule zeroes every transition.
     Markup: .flip-card[tabindex=0] > .flip-card__inner > .flip-card__face
     (front: __media + __body) + .flip-card__face.flip-card__face--back. */
  /* No flex or min-width here, unlike the older cards: this rule sits after
     `.sec-feature__grid > *`, so a `flex` shorthand would override the grid's
     sizing at equal specificity and pack five or six cards on a line. */
  .flip-card {
    position: relative;
    aspect-ratio: 3 / 4; perspective: 1600px;
    border-radius: var(--radius-card); cursor: pointer;
  }
  .flip-card:focus-visible { outline: var(--stroke-sm) solid var(--border-focus); outline-offset: var(--size-0p5); }
  /* --story: a card whose back tells a longer story (the academy /alumni cards,
     George, 2026-10-01: "a little longer", with the level each player reached).
     On a phone the card grows taller (3:4 → 9:16) so an 80–90 word story fits on the back
     at 360px without scrolling inside the card; from 701px the 3:4 card already has the room. */
  @media (max-width: 700px) {
    .flip-card--story { aspect-ratio: 9 / 16; }
  }
  .flip-card__inner {
    position: absolute; inset: 0; transform-style: preserve-3d;
    transition: transform calc(var(--dur-panel) * 2) var(--ease-in-out);
  }
  @media (hover: hover) and (pointer: fine) {
    .flip-card:hover .flip-card__inner { transform: rotateY(180deg); }
  }
  .flip-card:focus-visible .flip-card__inner,
  .flip-card.is-flipped .flip-card__inner { transform: rotateY(180deg); }
  .flip-card__face {
    position: absolute; inset: 0; display: flex; flex-direction: column;
    border-radius: var(--radius-card); overflow: hidden;
    background: var(--t-surface-card); border: var(--stroke-xs) solid var(--t-border);
    backface-visibility: hidden; -webkit-backface-visibility: hidden;
  }
  .flip-card__media {
    position: relative; flex: 1; min-height: 0; overflow: hidden;
    background: linear-gradient(135deg, var(--t-surface-inv) 0%, var(--t-accent-strong) 100%);
  }
  .flip-card__media > img {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; object-position: center top; display: block;
  }
  /* No photo is coming (the orientation deck's --crest state): the club shield
     on the plain surface. A finished card, not a gap to chase. */
  .flip-card__media--crest {
    background: var(--t-surface-card);
    display: flex; align-items: center; justify-content: center;
  }
  .flip-card__media--crest > img { position: static; width: 44%; height: auto; object-fit: contain; }
  /* The turn cue: a small disc on the photo, so the card reads as one that has
     a back before anyone hovers it. */
  .flip-card__cue {
    position: absolute; top: var(--size-3); right: var(--size-3);
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--size-8); height: var(--size-8); border-radius: var(--radius-pill);
    background: var(--t-surface-card); color: var(--t-text-accent-strong);
    border: var(--stroke-xs) solid var(--t-border);
  }
  .flip-card__body { padding: var(--size-4) var(--size-5); display: flex; flex-direction: column; gap: var(--size-1); }
  .flip-card__name {
    font-family: var(--font-family-heading); font-size: var(--font-size-xl); line-height: var(--lh-sm);
    font-weight: var(--font-smibold); letter-spacing: var(--tracking-normal);
    text-transform: var(--case-header); color: var(--t-text); margin: 0;
  }
  .flip-card__role {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); line-height: var(--lh-xl);
    color: var(--t-text-caption); margin: 0;
  }
  /* The back takes the accent's strongest step — the academy petrol — rather
     than the neutral inverse surface, which reads as the hub's near-black. */
  .flip-card__face--back {
    transform: rotateY(180deg);
    background: var(--t-accent-strong); border-color: transparent;
    padding: var(--size-6) var(--size-5); gap: var(--size-3);
  }
  .flip-card__face--back .flip-card__name { color: var(--t-text-inv); }
  .flip-card__face--back .flip-card__role { color: var(--t-text-accent-inv); }
  .flip-card__label {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wider); color: var(--t-text-accent-inv);
    margin: var(--size-2) 0 0;
  }
  .flip-card__bio {
    font-family: var(--font-family-body); font-size: var(--font-size-sm); line-height: var(--lh-xl);
    color: var(--t-text-inv); margin: 0; overflow-y: auto;
  }
  /* ── MATCH CARD — one team's latest result, or its next fixture ───────────
     The academy /results page (2026-10-01): one card per FC Odisea team, built
     from the federation's own data. Our line is the stronger one (weight and the
     accent score) and the outcome is spelled out in a pill, so the card reads
     without colour. Scores sit in the number role (B612 on the academy).
     Markup: article.match-card > .match-card__head (h3.match-card__team + .tag)
     + p.match-card__meta + .match-card__lines (> .match-card__line[--us] >
     .match-card__club + .match-card__score) + .match-card__foot. A fixture that
     is not played yet leaves the scores out and shows .match-card__kickoff. */
  .match-card {
    display: flex; flex-direction: column; gap: var(--size-4);
    padding: var(--size-6) var(--size-5); border-radius: var(--radius-card);
    background: var(--t-surface-card); border: var(--stroke-xs) solid var(--t-border);
  }
  .match-card__head { display: flex; flex-direction: column; align-items: flex-start; gap: var(--size-2); }
  .match-card__team {
    font-family: var(--font-family-heading); font-size: var(--font-size-xl); line-height: var(--lh-sm);
    font-weight: var(--font-smibold); letter-spacing: var(--tracking-normal);
    text-transform: var(--case-header); color: var(--t-text); margin: 0;
  }
  .match-card__meta {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); line-height: var(--lh-xl);
    color: var(--t-text-caption); margin: 0;
  }
  .match-card__lines {
    display: flex; flex-direction: column; gap: var(--size-3);
    padding-block: var(--size-4); border-block: var(--stroke-xs) solid var(--t-border);
  }
  .match-card__line { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--size-4); }
  .match-card__club {
    font-family: var(--font-family-body); font-size: var(--font-size-base); line-height: var(--lh-md);
    color: var(--t-text-body); overflow-wrap: anywhere;
  }
  .match-card__line--us .match-card__club { font-weight: var(--font-smibold); color: var(--t-text); }
  .match-card__score {
    font-family: var(--font-family-number); font-size: var(--font-size-3xl); line-height: var(--lh-none);
    font-variant-numeric: tabular-nums; color: var(--t-text-caption); min-width: 1ch; text-align: right;
  }
  .match-card__line--us .match-card__score { color: var(--t-text-accent-strong); }
  .match-card__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--size-3); flex-wrap: wrap; }
  .match-card__kickoff {
    font-family: var(--font-family-number); font-size: var(--font-size-lg); line-height: var(--lh-sm);
    font-variant-numeric: tabular-nums; color: var(--t-text-accent-strong); margin: 0;
  }
  .match-card__where { font-family: var(--font-family-body); font-size: var(--font-size-xs); color: var(--t-text-caption); margin: 0; }
  /* The outcome, in words. Win takes the success hue, loss the error hue, and a
     draw the neutral white surface (George, 2026-10-01: "red for loss, tie being
     white is ok"). */
  .match-card__outcome {
    display: inline-flex; align-items: center; gap: var(--size-1p5);
    padding: var(--size-1) var(--size-3); border-radius: var(--radius-pill);
    font-family: var(--font-family-label); font-size: var(--font-size-xs); font-weight: var(--font-smibold);
    text-transform: uppercase; letter-spacing: var(--tracking-wide);
    background: var(--t-surface-alt); color: var(--t-text-body); border: var(--stroke-xs) solid var(--t-border);
  }
  .match-card__outcome--win {
    background: var(--informative-success-100); color: var(--informative-success-800);
    border-color: var(--informative-success-200);
  }
  .match-card__outcome--loss {
    background: var(--informative-error-100); color: var(--informative-error-800);
    border-color: var(--informative-error-200);
  }
  /* ── FIXTURE LIST — the coming matches, by date ──────────────────────────
     A dated column in the number role, then who plays whom, when and where. One
     list for every team, in kick-off order, so a parent finds this weekend at a
     glance. Markup: ol.fixture-list > li.fixture-list__item > .fixture-list__date
     (__dow + __day + __month) + .fixture-list__body (p.fixture-list__team +
     p.fixture-list__match + p.fixture-list__meta). */
  .fixture-list {
    list-style: none; margin: 0 auto; padding: 0; width: 100%; max-width: 860px;
    border-radius: var(--radius-card); overflow: hidden;
    background: var(--t-surface-card); border: var(--stroke-xs) solid var(--t-border);
  }
  .fixture-list__item {
    display: grid; grid-template-columns: var(--size-16) minmax(0, 1fr) auto;
    gap: var(--size-5); align-items: start; padding: var(--size-5) var(--size-6);
  }
  /* The ground on a map (George, 2026-10-01) — a plain link to Google Maps, so
     nothing loads from Google until someone taps it. */
  .fixture-list__action { align-self: center; }
  .fixture-list__action .icon { flex: none; }
  .fixture-list__item + .fixture-list__item { border-top: var(--stroke-xs) solid var(--t-border); }
  .fixture-list__date {
    display: flex; flex-direction: column; align-items: center; gap: var(--size-0p5);
    padding-block: var(--size-2); border-radius: var(--radius-control);
    background: var(--t-surface-alt);
  }
  .fixture-list__dow,
  .fixture-list__month {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wide); color: var(--t-text-caption);
  }
  .fixture-list__day {
    font-family: var(--font-family-number); font-size: var(--font-size-2xl); line-height: var(--lh-none);
    font-variant-numeric: tabular-nums; color: var(--t-text-accent-strong);
  }
  .fixture-list__body { display: flex; flex-direction: column; gap: var(--size-1); min-width: 0; }
  .fixture-list__team {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs); font-weight: var(--font-medium);
    text-transform: var(--case-eyebrow); letter-spacing: var(--tracking-wide); color: var(--t-eyebrow-text); margin: 0;
  }
  .fixture-list__match {
    font-family: var(--font-family-heading); font-size: var(--font-size-lg); line-height: var(--lh-sm);
    font-weight: var(--font-smibold); text-transform: var(--case-header); color: var(--t-text); margin: 0;
    overflow-wrap: anywhere;
  }
  .fixture-list__v { font-weight: var(--font-normal); color: var(--t-text-caption); padding-inline: var(--size-1); }
  .fixture-list__meta { font-family: var(--font-family-body); font-size: var(--font-size-sm); line-height: var(--lh-lg); color: var(--t-text-body); margin: 0; }
  @media (max-width: 640px) {
    .fixture-list__item { grid-template-columns: var(--size-14) minmax(0, 1fr); gap: var(--size-4); padding: var(--size-4); }
    .fixture-list__match { font-size: var(--font-size-base); }
    .fixture-list__action { grid-column: 2; align-self: start; justify-self: start; }
  }
  /* A pane's own heading inside a tabset (the /results league tables): the
     section heading two steps down, so it reads as a sub-head of the section
     rather than a second section. */
  /* Two classes on purpose: the heading's own breakpoint steps further down this
     file (5xl at 900px, 4xl at 640px) would otherwise win on source order. */
  .section-hdr__heading.section-hdr__heading--sm { font-size: var(--font-size-4xl); }
  @media (max-width: 640px) {
    .section-hdr__heading.section-hdr__heading--sm { font-size: var(--font-size-3xl); }
  }
  /* The quiet line under a section — a source, a date, a caveat. */
  .sec-feature__note {
    font-family: var(--font-family-body); font-size: var(--font-size-xs); line-height: var(--lh-xl);
    color: var(--t-text-caption); text-align: center; margin: 0 auto; max-width: 640px;
  }
  /* Read by a screen reader, not shown: a table caption, or the date a visual
     date block already shows. */
  .visually-hidden {
    position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  /* ── STANDINGS — a league table on the data table ────────────────────────
     .data-table--standings tightens the cells, sets every figure in the number
     role, and drops the detail columns on a phone: position, club, played, goal
     difference and points are what a table is read for. Our row takes
     .data-table__row--highlight. The form run is the last five, oldest first. */
  .data-table--standings .cell { padding: var(--size-2p5) var(--size-3); }
  .data-table--standings .cell:first-child { padding-left: var(--size-5); }
  .data-table--standings .cell:last-child { padding-right: var(--size-5); }
  .data-table--standings .cell--num {
    font-family: var(--font-family-number); font-variant-numeric: tabular-nums;
    text-align: right; white-space: nowrap;
  }
  /* Figures and dates never wrap ("Sat / 26 / Sep" stacked three lines high in the
     results list); the club column takes what is left and wraps instead. */
  .data-table--standings .cell { white-space: nowrap; }
  .data-table--standings .cell--club { width: 100%; white-space: normal; overflow-wrap: anywhere; }
  .data-table--standings abbr[title] { text-decoration: none; cursor: help; }
  .data-table--standings .data-table__row--highlight .cell { color: var(--t-tag-soft-text); font-weight: var(--font-smibold); }
  .data-table--standings .cell--pts { color: var(--t-text); font-weight: var(--font-smibold); }
  .form-run { display: inline-flex; gap: var(--size-1); }
  .form-run__item {
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--size-5); height: var(--size-5); border-radius: var(--radius-sm);
    font-family: var(--font-family-label); font-size: var(--font-size-xs); font-weight: var(--font-smibold);
    background: var(--t-surface-alt); color: var(--t-text-caption); border: var(--stroke-xs) solid var(--t-border);
  }
  .form-run__item--w { background: var(--informative-success-100); color: var(--informative-success-800); border-color: var(--informative-success-200); }
  .form-run__item--l { background: var(--informative-error-100); color: var(--informative-error-800); border-color: var(--informative-error-200); }
  /* A result in a list of results: green for a win, red for a loss, a draw in the
     body colour (George, 2026-10-01). */
  .data-table--standings .cell--win  { color: var(--informative-success-700); }
  .data-table--standings .cell--loss { color: var(--informative-error-700); }
  /* League zones: a bar down the left edge of the row, the way a league table marks
     the promotion and relegation places. The zones and where they start come from
     the federation's own legend for each table. */
  .data-table--standings .data-table__row[data-zone] > .cell:first-child { box-shadow: inset var(--size-1) 0 0 var(--zone-color); }
  [data-zone='promotion']          { --zone-color: var(--informative-success-500); }
  [data-zone='playoff']            { --zone-color: var(--informative-warning-400); }
  [data-zone='relegation-playoff'] { --zone-color: var(--informative-error-300); }
  [data-zone='relegation']         { --zone-color: var(--informative-error-500); }
  .table-legend {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-wrap: wrap; gap: var(--size-2) var(--size-5);
  }
  .table-legend__item {
    display: inline-flex; align-items: center; gap: var(--size-2);
    font-family: var(--font-family-body); font-size: var(--font-size-xs); color: var(--t-text-body);
  }
  .table-legend__item::before {
    content: ''; width: var(--size-1); height: var(--size-4); border-radius: var(--radius-pill);
    background: var(--zone-color);
  }
  @media (max-width: 640px) {
    .data-table--standings .cell--wide { display: none; }
    .data-table--standings .cell { padding-inline: var(--size-2); }
    .data-table--standings .cell:first-child { padding-left: var(--size-3); }
    .data-table--standings .cell:last-child { padding-right: var(--size-3); }
  }
  /* ── TIMELINE — a club's story as a route ────────────────────────────────
     George, 2026-09-18 (academy /about): the years sit in the number role (B612
     on the academy) and every stop hangs on one line, like a route drawn on a
     chart. Markup: ol.timeline > li.timeline__item > .timeline__year +
     .timeline__marker + .timeline__body (h3.timeline__title + p.timeline__text).
     On a phone the year moves above its title and the route runs down the left. */
  .timeline {
    list-style: none; margin: 0 auto; padding: 0; max-width: 800px;
    display: flex; flex-direction: column;
  }
  .timeline__item {
    display: grid; grid-template-columns: var(--size-32) var(--size-6) 1fr;
    column-gap: var(--size-6); padding-bottom: var(--size-10);
  }
  .timeline__item:last-child { padding-bottom: 0; }
  .timeline__year {
    font-family: var(--font-family-number); font-size: var(--font-size-5xl); line-height: var(--lh-none);
    font-variant-numeric: tabular-nums; color: var(--t-text-accent-strong); text-align: right;
  }
  .timeline__marker { position: relative; }
  .timeline__marker::before {
    content: ''; position: absolute; top: var(--size-2); left: 50%; transform: translateX(-50%);
    width: var(--size-4); height: var(--size-4); border-radius: var(--radius-pill);
    background: var(--t-surface-card); border: var(--stroke-sm) solid var(--t-accent);
  }
  /* The route: from under each stop to the next one; the last stop ends it. */
  .timeline__item:not(:last-child) .timeline__marker::after {
    content: ''; position: absolute; top: var(--size-7); bottom: calc(-1 * var(--size-10) + var(--size-1));
    left: 50%; transform: translateX(-50%); width: var(--stroke-sm); background: var(--t-accent-soft);
  }
  .timeline__body { display: flex; flex-direction: column; gap: var(--size-2); padding-top: var(--size-1); }
  .timeline__title {
    font-family: var(--font-family-heading); font-size: var(--font-size-xl); line-height: var(--lh-sm);
    font-weight: var(--font-smibold); color: var(--t-text); margin: 0;
  }
  .timeline__text {
    font-family: var(--font-family-body); font-size: var(--font-size-base); line-height: var(--lh-xl);
    color: var(--t-text-body); margin: 0;
  }
  @media (max-width: 640px) {
    .timeline__item { grid-template-columns: var(--size-6) 1fr; column-gap: var(--size-4); }
    .timeline__year { grid-column: 2; grid-row: 1; text-align: left; font-size: var(--font-size-4xl); }
    .timeline__marker { grid-column: 1; grid-row: 1 / span 2; }
    .timeline__body { grid-column: 2; grid-row: 2; }
  }
  /* Compact: a day's schedule rather than a club's story (George, 2026-09-29,
     academy /programs/athletic-pathway: "a little more spaced tightly
     vertically … more like a timeline"). It keeps the same route and markers.
     The time sits at base size in the number role, in a 96px column where a
     range such as "09:00–13:00" breaks after its dash. Stops are 20px apart and
     the text drops to the small sizes; the connector's reach follows the
     tighter step. It is made for half-width columns (a `.tabset--pair` pane),
     so it drops the 800px cap and fills its container. */
  .timeline--compact { max-width: none; margin: 0; }
  .timeline--compact .timeline__item {
    grid-template-columns: var(--size-24) var(--size-4) 1fr;
    column-gap: var(--size-3); padding-bottom: var(--size-5);
  }
  .timeline--compact .timeline__year { font-size: var(--font-size-base); line-height: var(--lh-sm); padding-top: var(--size-0p5); }
  .timeline--compact .timeline__marker::before { top: var(--size-1); width: var(--size-3); height: var(--size-3); }
  .timeline--compact .timeline__item:not(:last-child) .timeline__marker::after {
    top: var(--size-5); bottom: calc(-1 * var(--size-5) + var(--size-1));
  }
  .timeline--compact .timeline__body { gap: var(--size-1); padding-top: 0; }
  .timeline--compact .timeline__title { font-size: var(--font-size-base); }
  .timeline--compact .timeline__text { font-size: var(--font-size-sm); line-height: var(--lh-lg); }
  @media (max-width: 640px) {
    .timeline--compact .timeline__item { grid-template-columns: var(--size-4) 1fr; column-gap: var(--size-3); }
    .timeline--compact .timeline__year { font-size: var(--font-size-sm); }
    /* In a paired tabset the tab already names the pane, so a column label
       inside it would only repeat it (as `.feature-list-pair--tabbed` hides its own). */
    .tabset--pair .feature-list-pair__label { display: none; }
  }
  /* ── Grids + stack helpers for document layouts ──────────────────────────── */
  .doc-grid { display: grid; gap: var(--size-4); }
  .doc-grid--2 { grid-template-columns: 1fr 1fr; }
  .doc-grid--3 { grid-template-columns: repeat(3, 1fr); }
  .doc-stack { display: flex; flex-direction: column; gap: var(--size-3); }
  /* ── Editorial — image + text block (image left / right) ─────────────────── */
  .doc-editorial { display: grid; grid-template-columns: 38% 1fr; gap: var(--size-6); align-items: start; }
  .doc-editorial__img { aspect-ratio: 3 / 4; }
  .doc-editorial__text { display: flex; flex-direction: column; gap: var(--size-3); }
  .doc-editorial--reverse { grid-template-columns: 1fr 38%; }
  .doc-editorial--reverse .doc-editorial__img { order: 2; }
  /* ── Banner — image with overlaid headline (white text on photo) ─────────── */
  .doc-banner {
    position: relative; overflow: hidden; border-radius: var(--document-card-radius-lg);
    height: 250px; display: flex; align-items: flex-end;
  }
  .doc-banner__scrim { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 0%, color-mix(in srgb, var(--base-black) var(--opacity-25), transparent) 45%, color-mix(in srgb, var(--base-black) var(--opacity-90), transparent) 100%); }
  .doc-banner__scrim--soft { background: linear-gradient(180deg, color-mix(in srgb, var(--base-black) var(--opacity-25), transparent) 0%, color-mix(in srgb, var(--base-black) var(--opacity-75), transparent) 100%); }
  .doc-banner .doc-img { position: absolute; inset: 0; border-radius: 0; }
  .doc-banner__title {
    position: relative; z-index: 1;
    font-family: var(--font-family-display); font-size: var(--font-size-7xl);
    line-height: var(--lh-xs); text-transform: uppercase; color: var(--base-white);
    letter-spacing: var(--tracking-tight); padding: var(--size-7);
  }
  .doc-banner--facts { align-items: stretch; flex-direction: column; justify-content: flex-end; height: 300px; }
  /* ── FactStrip — icon + label quick facts (over a banner) ────────────────── */
  .doc-fact-strip {
    position: relative; z-index: 1; display: flex; flex-wrap: wrap;
    align-items: center; justify-content: center;
    gap: var(--size-3) var(--size-6); padding: var(--size-7);
  }
  .doc-fact {
    display: flex; align-items: center; gap: var(--size-2p5);
    font-family: var(--font-family-display); font-size: var(--font-size-xl);
    text-transform: uppercase; color: var(--base-white); letter-spacing: var(--tracking-normal);
  }
  .doc-fact svg { width: var(--size-6); height: var(--size-6); color: var(--t-accent); flex: 0 0 auto; }
  /* ── Partners — partner / college logo strip ─────────────────────────────── */
  .doc-partners { display: flex; align-items: center; gap: var(--size-8); flex-wrap: wrap; }
  /* White PNG logos: --t-logo-filter inverts them to dark on light surfaces and
     leaves them white on dark — automatically per theme mode. --on-dark forces
     "no invert" for a dark surface shown inside a light-mode canvas. */
  .doc-partners__img { height: var(--size-11); width: auto; opacity: 0.8; filter: var(--t-logo-filter); }
  .doc-partners--on-dark .doc-partners__img { filter: none; }
  /* ── Cover — front / back (full-bleed photo, white text) ─────────────────── */
  .doc-page--cover { padding: 0; background: var(--t-surface-inv); }
  .doc-img--cover { position: absolute; inset: 0; border-radius: 0;
    background: linear-gradient(150deg, var(--t-surface-inv) 0%, var(--t-accent-strong) 60%, var(--t-accent) 100%); }
  .doc-cover__scrim { position: absolute; inset: 0; background: linear-gradient(180deg, color-mix(in srgb, var(--base-black) var(--opacity-10), transparent) 0%, color-mix(in srgb, var(--base-black) var(--opacity-25), transparent) 45%, color-mix(in srgb, var(--base-black) var(--opacity-85), transparent) 100%); }
  .doc-cover__scrim--center { background: radial-gradient(ellipse at center, color-mix(in srgb, var(--base-black) var(--opacity-25), transparent), color-mix(in srgb, var(--base-black) var(--opacity-75), transparent)); }
  .doc-cover__body {
    position: absolute; left: var(--document-margin-x); right: var(--document-margin-x);
    bottom: 150px; z-index: 1; display: flex; flex-direction: column; gap: var(--size-4);
  }
  .doc-cover__body--center { inset: 0; bottom: auto; top: 50%; transform: translateY(-50%); align-items: center; text-align: center; gap: var(--size-6); }
  .doc-cover__overline {
    font-family: var(--font-family-display); font-size: var(--font-size-2xl);
    text-transform: uppercase; letter-spacing: var(--tracking-wide); color: var(--t-accent);
  }
  .doc-cover__title {
    font-family: var(--font-family-display); font-size: var(--font-size-9xl);
    line-height: var(--lh-md); text-transform: uppercase; color: var(--base-white);
    letter-spacing: var(--tracking-tight); margin: 0;
  }
  .doc-cover__title--back { font-size: var(--font-size-8xl); }
  .doc-cover__rule { display: block; width: 100%; height: 3px; background: var(--t-accent); margin: var(--size-2) 0; }
  .doc-cover__logos { display: flex; align-items: center; gap: var(--size-5); margin-top: var(--size-4); }
  .doc-cover__logo { height: 72px; width: auto; }
  .doc-cover__logo--partner { background: var(--base-white); border-radius: var(--size-2); padding: var(--size-2); height: var(--size-16); }
  .doc-cover__logo-div { width: var(--size-px); height: var(--size-14); background: color-mix(in srgb, var(--base-white) var(--opacity-25), transparent); }
  .doc-cover__shield { height: 150px; width: auto; }
  .doc-cover__footer { position: absolute; left: var(--document-margin-x); right: var(--document-margin-x); bottom: var(--size-8); z-index: 1; text-align: right; font-size: var(--font-size-sm); color: color-mix(in srgb, var(--base-white) var(--opacity-50), transparent); }
  .doc-cover__footer--center { text-align: center; }
  /* ── Table of Contents ───────────────────────────────────────────────────── */
  .doc-toc__masthead { display: grid; grid-template-columns: 200px 1fr; grid-auto-rows: 84px; gap: var(--size-2p5); margin-bottom: var(--size-12); }
  .doc-toc__mh {
    display: flex; align-items: center; padding: 0 var(--size-5);
    border-radius: var(--document-card-radius-lg);
    font-family: var(--font-family-display); text-transform: uppercase;
    letter-spacing: var(--tracking-tight); line-height: var(--lh-none);
  }
  .doc-toc__mh--dark  { background: var(--t-surface-inv); color: var(--t-text-inv); font-size: var(--font-size-3xl); }
  .doc-toc__mh--light { background: var(--t-surface-card); border: var(--stroke-xs) solid var(--t-border); color: var(--t-text); font-size: var(--font-size-3xl); }
  .doc-toc__mh--blank { background: var(--t-surface-card); border: var(--stroke-xs) solid var(--t-border); }
  .doc-toc__mh--wide  { font-size: var(--font-size-7xl); justify-content: flex-start; }
  .doc-toc__cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--size-10) var(--size-8); }
  .doc-toc__cols--single { grid-template-columns: 1fr; }
  .doc-toc__group { display: flex; gap: var(--size-4); align-items: stretch; }
  .doc-toc__num {
    flex: 0 0 72px; align-self: flex-start; min-height: var(--size-24);
    background: var(--t-surface-inv); color: var(--t-text-inv);
    border-radius: var(--document-card-radius-md);
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-family-display); font-size: var(--font-size-8xl); line-height: var(--lh-none);
  }
  .doc-toc__list { flex: 1 1 auto; display: flex; flex-direction: column; gap: var(--size-1p5); }
  .doc-toc__heading { font-family: var(--font-family-display); font-size: var(--font-size-3xl); text-transform: uppercase; color: var(--t-text); margin: 0 0 var(--size-1); }
  .doc-toc__entry { display: flex; align-items: baseline; gap: var(--size-2); }
  .doc-toc__label { font-size: var(--font-size-base); color: var(--t-text-body); }
  .doc-toc__leader { flex: 1 1 auto; border-bottom: var(--size-0p5) dotted var(--t-border-strong); transform: translateY(-3px); }
  .doc-toc__page { font-size: var(--font-size-base); color: var(--t-text-body); font-weight: var(--font-medium); }
  /* ── SITE NAV — persistent top navigation (sticky header + dropdowns + mobile) ─ */
  .site-nav {
    position: sticky; top: 0; z-index: 50;
    background: var(--t-surface);
    border-bottom: var(--stroke-xs) solid var(--t-border);
  }
  .site-nav__inner {
    max-width: 1280px; margin: 0 auto;
    display: flex; align-items: center; gap: var(--size-4);
    padding: var(--size-3) var(--size-6);
  }
  .site-nav__brand { display: flex; align-items: center; flex-shrink: 0; }
  .site-nav__menu {
    flex: 1; display: flex; align-items: center; justify-content: center; gap: var(--size-1);
    list-style: none;
  }
  .site-nav__item { position: relative; }
  .site-nav__link {
    display: inline-flex; align-items: center; gap: var(--size-1);
    padding: var(--size-2) var(--size-3); border-radius: var(--radius-xs);
    font-family: var(--font-family-body); font-size: var(--font-size-sm); font-weight: var(--font-medium);
    color: var(--t-text-body); cursor: pointer; background: none; border: none;
    transition: color var(--dur-micro) var(--ease-out), background var(--dur-micro) var(--ease-out);
  }
  .site-nav__link:hover,
  .site-nav__link--active { color: var(--t-eyebrow-text); }
  .site-nav__link:focus-visible { outline: var(--stroke-sm) solid var(--border-focus); outline-offset: var(--size-0p5); }
  .site-nav__caret { width: var(--size-3); height: var(--size-3); transition: transform var(--dur-micro) var(--ease-out); }
  .site-nav__item:hover .site-nav__caret,
  .site-nav__item:focus-within .site-nav__caret { transform: rotate(180deg); }
  .site-nav__panel {
    position: absolute; top: calc(100% + var(--size-1)); left: 0;
    min-width: var(--size-56);
    display: flex; flex-direction: column; gap: var(--size-0p5);
    padding: var(--size-2);
    background: var(--t-surface-card);
    border: var(--stroke-xs) solid var(--t-border); border-radius: var(--radius-control);
    opacity: 0; visibility: hidden; transform: translateY(var(--size-1));
    transition: opacity var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out), visibility var(--dur-micro);
  }
  .site-nav__item:hover .site-nav__panel,
  .site-nav__item:focus-within .site-nav__panel {
    opacity: 1; visibility: visible; transform: translateY(0);
  }
  .site-nav__panel-link {
    padding: var(--size-2) var(--size-2p5); border-radius: var(--radius-xs);
    font-family: var(--font-family-body); font-size: var(--font-size-sm);
    color: var(--t-text-body); white-space: nowrap;
    transition: color var(--dur-micro) var(--ease-out), background var(--dur-micro) var(--ease-out);
  }
  .site-nav__panel-link:hover {
    color: var(--t-eyebrow-text);
    background: color-mix(in srgb, var(--t-accent) var(--opacity-10), transparent);
  }
  .site-nav__actions { display: flex; align-items: center; gap: var(--size-2); flex-shrink: 0; margin-left: auto; }
  /* Language switcher, left of Apply (George, 2026-10-01) — the hub's .sec-nav__lang
     on the academy's light bar: a globe, the current language and a caret, opening
     a panel of languages on hover or keyboard focus. Below 901px the panel gives way
     to an inline EN · ES pair (.site-nav__lang-inline): a hover panel is unreliable
     on a touch screen (iOS does not focus a tapped button), and two short links fit
     beside Apply and the menu button at 360px. */
  .site-nav__lang { position: relative; }
  .site-nav__lang-btn {
    display: inline-flex; align-items: center; gap: var(--size-1p5);
    padding: var(--size-2); border-radius: var(--radius-xs);
    background: none; border: none; cursor: pointer;
    font-family: var(--font-family-body); font-size: var(--font-size-sm); font-weight: var(--font-medium);
    color: var(--t-text-body);
    transition: color var(--dur-micro) var(--ease-out);
  }
  .site-nav__lang:hover .site-nav__lang-btn,
  .site-nav__lang:focus-within .site-nav__lang-btn { color: var(--t-eyebrow-text); }
  .site-nav__lang-btn:focus-visible { outline: var(--stroke-sm) solid var(--border-focus); outline-offset: var(--size-0p5); }
  .site-nav__lang-icon { width: var(--size-4); height: var(--size-4); }
  .site-nav__lang-caret { width: var(--size-3); height: var(--size-3); transition: transform var(--dur-micro) var(--ease-out); }
  .site-nav__lang:hover .site-nav__lang-caret,
  .site-nav__lang:focus-within .site-nav__lang-caret { transform: rotate(180deg); }
  .site-nav__lang-panel {
    position: absolute; top: calc(100% + var(--size-1)); right: 0;
    min-width: var(--size-32);
    display: flex; flex-direction: column; gap: var(--size-0p5);
    padding: var(--size-2);
    background: var(--t-surface-card);
    border: var(--stroke-xs) solid var(--t-border); border-radius: var(--radius-control);
    opacity: 0; visibility: hidden; transform: translateY(var(--size-1));
    transition: opacity var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out), visibility var(--dur-micro);
  }
  .site-nav__lang:hover .site-nav__lang-panel,
  .site-nav__lang:focus-within .site-nav__lang-panel { opacity: 1; visibility: visible; transform: translateY(0); }
  .site-nav__lang-option {
    padding: var(--size-2) var(--size-2p5); border-radius: var(--radius-xs);
    font-family: var(--font-family-body); font-size: var(--font-size-sm);
    color: var(--t-text-body); white-space: nowrap;
    transition: color var(--dur-micro) var(--ease-out), background var(--dur-micro) var(--ease-out);
  }
  .site-nav__lang-option:hover { color: var(--t-eyebrow-text); background: color-mix(in srgb, var(--t-accent) var(--opacity-10), transparent); }
  .site-nav__lang-option[aria-current] { color: var(--t-text); font-weight: var(--font-smibold); }
  .site-nav__lang-inline { display: none; align-items: center; gap: var(--size-0p5); }
  .site-nav__lang-inline-link {
    display: inline-flex; align-items: center; justify-content: center; padding: var(--size-1) var(--size-1p5);
    font-family: var(--font-family-body); font-size: var(--font-size-sm); font-weight: var(--font-medium);
    color: var(--t-text-caption);
  }
  .site-nav__lang-inline-link[aria-current] { color: var(--t-text); font-weight: var(--font-smibold); }
  .site-nav__lang-inline-sep { color: var(--t-text-caption); opacity: 0.5; user-select: none; }
  @media (max-width: 900px) {
    .site-nav__lang { display: none; }
    .site-nav__lang-inline { display: inline-flex; }
  }
  .site-nav__toggle {
    display: none; align-items: center; justify-content: center;
    width: var(--size-9); height: var(--size-9); padding: 0;
    background: none; border: var(--stroke-xs) solid var(--t-border); border-radius: var(--radius-xs);
    color: var(--t-text); cursor: pointer;
  }
  /* Mobile full-screen menu */
  .site-nav__mobile {
    position: fixed; inset: 0; z-index: 100;
    background: var(--t-surface);
    display: flex; flex-direction: column; gap: var(--size-2);
    padding: var(--size-6);
    overflow-y: auto;
    /* Closed drawers were left `visible` off-canvas, so their links stayed in the
       tab order and the a11y tree on every page — measured 2026-08-27, the closed
       academy drawer held 10 focusable links. `visibility` fixes that; the 0s
       delay holds it visible for the slide-out, then hides it. Same treatment
       .sec-nav__mobile already carries. */
    visibility: hidden;
    transform: translateX(100%);
    transition: transform var(--dur-panel) var(--ease-out),
                visibility 0s linear var(--dur-panel);
  }
  .site-nav__mobile--open {
    transform: translateX(0);
    visibility: visible;
    transition: transform var(--dur-panel) var(--ease-out), visibility 0s;
  }
  .site-nav__mobile-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--size-2); }
  .site-nav__mobile-group { display: flex; flex-direction: column; gap: var(--size-0p5); padding: var(--size-2) 0; border-bottom: var(--stroke-xs) solid var(--t-border); }
  .site-nav__mobile-label {
    font-family: var(--font-family-eyebrow); font-size: var(--font-size-xs); font-weight: var(--font-bold);
    letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--t-text-caption);
    margin-bottom: var(--size-1);
  }
  .site-nav__mobile-link {
    padding: var(--size-2) 0; font-family: var(--font-family-body); font-size: var(--font-size-base);
    color: var(--t-text); font-weight: var(--font-medium);
  }
  .site-nav__mobile-link--sub { font-size: var(--font-size-sm); font-weight: var(--font-normal); color: var(--t-text-body); padding-left: var(--size-2); }
  /* The page you are on, in the drawer: the header marks it with --active, and the drawer
     had only aria-current for a screen reader, so a phone reader saw no cue at all. */
  .site-nav__mobile-link[aria-current="page"] { color: var(--t-eyebrow-text); font-weight: var(--font-bold); }
  .site-nav__mobile-close {
    display: inline-flex; align-items: center; justify-content: center;
    width: var(--size-9); height: var(--size-9); padding: 0;
    background: none; border: var(--stroke-xs) solid var(--t-border); border-radius: var(--radius-xs);
    color: var(--t-text); cursor: pointer;
  }
  @media (max-width: 900px) {
    .site-nav__menu { display: none; }
    .site-nav__toggle { display: inline-flex; }
  }
  @media (min-width: 901px) {
    .site-nav__mobile { display: none; }
  }
  /* DEVICE TYPE SCALE (George, 2026-09-16). tokens.json has always defined a
     tablet and a mobile typography scale (Global.tablet and Global.mobile: hero,
     h1–h5), and no production rule used it. The only step-downs were two ad-hoc
     hero breakpoints (700 and 420), so a section heading kept its desktop 48px on
     a 375px phone. That is how Spanish headings ran to four lines and needed
     hyphenation to fit at all. George's ruling: fit more words by setting phone
     headings smaller, not by hyphenating. Each display class steps to its token
     role at the gutter ladder's breakpoints (900 is where the nav collapses, 640
     is the phone step):
       hero heading (hero)       96 → 60 (tablet.hero) → 48 (mobile.hero)
       section heading (h2)      48 → 36 (tablet.h2)   → 32 (mobile.h2)
       closing banner (h3)       36 → 32 (tablet.h3)   → 28 (mobile.h3)
       feature-card title (h5)   24 → 24               → 20 (mobile.h5)
     The desktop hero stays at 96 (9xl), above the token's 72: that was the
     identity pass's call, and this rule does not reach it.
     Measured over the 30 hub pages that carry these headings, at 375: hero 3.1 →
     2.2 lines on average, section headings 2.1 → 1.5, and no word split across
     lines. */
  @media (max-width: 900px) {
    .sec-hero__heading { font-size: var(--font-size-7xl); }
    .section-hdr__heading { font-size: var(--font-size-5xl); }
    .sec-cta-banner__heading { font-size: var(--font-size-4xl); }
  }
  @media (max-width: 640px) {
    .sec-hero__heading { font-size: var(--font-size-6xl); }
    .section-hdr__heading { font-size: var(--font-size-4xl); }
    .sec-cta-banner__heading { font-size: var(--font-size-3xl); }
    .feature-card__title { font-size: var(--font-size-xl); }
  }
  /* HERO TEXT RUNS FULL WIDTH ON PHONES AND TABLETS (George, 2026-09-16). The base
     .sec-hero__content caps the text at 70% so it clears the photograph's subject on
     a desktop. On a phone the text sits over the photograph anyway, and the library
     said so ("Mobile content: always full width") only on the showcase-only
     .sec-hero__inner--mobile modifier, which production pages never carry. A 375px
     hero therefore gave its heading 240px of a 343px column, and "universitarios"
     did not fit on a line at any size that still read as a hero. 768 is where
     that word stopped fitting the 70% column. */
  @media (max-width: 768px) {
    .sec-hero__content { max-width: none; }
  }
  /* Card + form grids are fixed multi-column; production pages don't apply the
     --mobile modifiers, so collapse them to a single column on small screens
     (matches the .sec-feature__grid--mobile / .sec-form__grid--mobile intent). */
  /* The hub's proof band at four-up between 700 and 900: "1,000+" at 7xl measured
     215px in a 171px block (2026-09-12 sweep, both languages). The 640 rule below
     already steps it to 5xl at two-up; this fills the band in between. */
  @media (max-width: 900px) {
    .sec-testimonial__stats--band .stat-block__number { font-size: var(--font-size-6xl); }
  }
  @media (max-width: 640px) {
    .sec-feature__grid > * { flex-basis: 100%; }
    /* STAT ROWS GO TWO-UP, not one (George, 2026-09-09). A stat block is a
       number and a short label; one per line wastes the width and makes a
       four-stat row four screens tall. `:has()` keeps it automatic — any grid
       holding stat blocks, on any page — and outranks the 100% above on
       specificity, so source order here does not matter. */
    .sec-feature__grid:has(.stat-block) > *,
    /* ⚠ `:has()` covers any GRID holding stat blocks — and stat rows are not all
       grids. Five pages put their stats in `.sec-testimonial__stats`, a flex row
       of its own (hub `/` and `/for-universities`, academy `/`, `/results` and
       `/departments/talent-management`), so the rule above never reached them and
       the hub home's four-stat proof row was still four screens tall on a phone.
       Named explicitly rather than widened, so it stays obvious which containers
       are covered. Found 2026-09-11 adding a third stat to /for-universities. */
    .sec-testimonial__stats > * {
      flex-basis: calc((100% - var(--size-5)) / 2);
      flex-grow: 0;
      min-width: 0;
    }
    /* One step down at two-up: 36px in a ~122px content box wrapped "12-25"
       across two lines. 32px keeps every numeric value on one line. A two-WORD
       value ("Year-Round") wraps at any size that still reads as a stat — it is
       the space/hyphen, not the scale — which is why the block centers its text
       rather than pretending the wrap will not happen. */
    .stat-block__number { font-size: var(--font-size-4xl); }
    /* The label used to need `white-space: normal` restated here, because the base
       rule was `nowrap`: at two-up "Months in a Typical NCAA Season" measured 236px
       inside a 162px block and pushed the document to 396px on a 375px phone
       (found 2026-09-11). The base rule wraps as of 2026-09-12 — the same defect
       turned out to reach DESKTOP in Spanish, where a stat row's blocks cannot grow
       — so this override is redundant and is gone. Kept as a note because the
       phone case is the one that will be re-tested first. */
    /* The band goes two-up like any stat row, but its own rules have to be
       re-drawn for a wrapped grid: the vertical hairline belongs between the
       columns only, and a horizontal one opens the second row. Its flex-basis
       is restated here because `--band > .stat-block` is a two-class selector
       and outranks the one-class two-up rule above wherever that sits. */
    .sec-testimonial__stats--band > .stat-block {
      flex-basis: 50%; padding: var(--size-6) var(--size-3);
    }
    .sec-testimonial__stats--band > .stat-block:nth-child(odd) { border-left: 0; }
    .sec-testimonial__stats--band > .stat-block:nth-child(n+3) {
      border-top: var(--stroke-xs) solid var(--t-border);
    }
    .sec-testimonial__stats--band .stat-block__number { font-size: var(--font-size-5xl); }
    /* Two per line on a phone, opted into per block with --mobile-2 (George,
       2026-09-07: /contact's Visit Us row and the showcase's Event Details,
       two across in the vertical phone layout). Only for SHORT cards — a
       venue card's title spilled 39px at this width. The gap tightens because
       two cards at 375px leave ~160px each and the desktop gutter would eat a
       fifth of it. */
    .sec-feature__grid--mobile-2 { gap: var(--size-3); }
    .sec-feature__grid--mobile-2 > * { flex-basis: calc((100% - var(--size-3)) / 2); }
    .sec-form__grid    { grid-template-columns: 1fr; }
    /* Same reasoning for the wizard's Back / Continue row: production pages
       don't apply .sec-form__nav--mobile either, and two side-by-side pill
       buttons measured 385px against a 375px viewport — i.e. the Submit button
       hangs off a phone screen. Mirrors that modifier's own declarations.
       Same treatment as the closing banner above (George, 2026-09-08): stacked
       and centered, at their own width. The overflow this rule exists to fix
       came from two pill buttons SIDE BY SIDE (385px against a 375px viewport);
       stacking is what cures it, and stretching them was never the fix. */
    .sec-form__nav       { flex-direction: column-reverse; align-items: center; gap: var(--size-2); }
    .sec-form__nav-right { flex-direction: column; align-items: center; gap: var(--size-2); }
    .sec-form__nav .btn  { width: auto; }
  }
  /* Tablet: both the three-up and the four-up drop to two per line, because
     three 180-200px cards plus gaps do not read at that width. A five-card set
     therefore lands as 2 + 2 + 1 with the last line centred, which is the point
     of the flex switch above. Below 641px the rule above takes everything to
     one per line. */
  @media (max-width: 1024px) and (min-width: 641px) {
    .sec-feature__grid > * { flex-basis: calc((100% - var(--size-5)) / 2); }
  }
  /* ── A photographic feature band ───────────────────────────────────────────
     Same construction as the closing CTA banner — the photograph on its own
     layer beneath a scrim, over a background PINNED dark so the band cannot
     flip to a light surface under `theme-*-dark` — but for a whole feature
     section rather than a two-line banner.

     ⚠ ITS CONTENTS MUST SPEAK THE INVERSE VOCABULARY. The band is dark by
     design in BOTH modes, so anything inside it that resolves a theme-flipping
     role will go dark-on-dark in dark mode: use `section-hdr--inverse` and the
     `--dark` card variants, never the plain ones. This is the dark-mode law
     from D3, and this section is a new place to trip over it.

     The scrim runs vertically, unlike the banner's horizontal one: a feature
     section's content is centered, so the darkest part belongs top and bottom
     rather than on one side. */
  .sec-feature--photo {
    position: relative;
    overflow: hidden;
    background: var(--gray-warm-900);
  }
  .sec-feature__bg {
    position: absolute; inset: 0;
    background-size: cover; background-position: center;
    z-index: 0;
  }
  .sec-feature__bg::after {
    content: '';
    position: absolute; inset: 0;
    background: linear-gradient(
      180deg,
      color-mix(in srgb, var(--gray-warm-900) 84%, transparent) 0%,
      color-mix(in srgb, var(--gray-warm-900) 66%, transparent) 50%,
      color-mix(in srgb, var(--gray-warm-900) 88%, transparent) 100%
    );
  }
  .sec-feature--photo .sec-feature__inner { position: relative; z-index: 1; }
  /* ── Photographic grade (IDENTITY-BRIEF §5.2) ───────────────────────────────
     One grade, applied to every photographic role so images read as one set
     rather than as whatever the camera produced. Applied to the image itself, so
     the hero's scrim (::before) and bloom (::after) layer on top ungraded.

     COMMERCE EXCEPTION (§5.4): merchandise photography is exempt — "grading merch
     lies about what parents are buying" — so shop product tiles must carry
     .img-ungraded. It is defined here rather than in the shop so the rule travels
     with the design system instead of being rediscovered per site. */
  .sec-hero__bg,
  .sec-feature__bg,
  .sec-cta-banner__bg,
  .sec-split__img,
  .venue-card__media > img,
  .flip-card__media > img,
  .sec-image-grid__img {
    filter: var(--t-image-grade);
  }
  .img-ungraded,
  .img-ungraded img { filter: none; }
  /* Section gutter ladder. Every sec-* block sets its horizontal padding to
     --size-20 (80px) — the desktop gutter — and production pages don't apply the
     --mobile modifiers, so a 375px phone was left with 215px of content and 160px
     of padding (57% of the screen). Step the gutter down by breakpoint; the phone
     step is --size-4, the value the library's own --mobile modifiers already use
     (.sec-inner--mobile, .sec-chapel--mobile). VERTICAL padding steps down only
     at the phone step: 96-120px is the cathedral rhythm IDENTITY-BRIEF §6 asks
     for on a desktop, and the library's own --mobile modifiers have always set
     32px (--size-8) above and below. George, 2026-09-16: on a phone, "go with the
     design system's 32 px" — the brief's larger spacing had overwritten it.
     The gutter is also published as --section-gutter (2026-09-16), because a
     component that bleeds to the screen edge has to cancel exactly this padding.
     The feature carousel hard-coded 20px against a 16px phone gutter, so its
     strip ran 4px past both edges of every phone. */
  @media (max-width: 900px) {
    .sec-hero, .sec-feature, .sec-testimonial, .sec-cta-banner, .sec-emblem,
    .sec-chapel, .sec-partners, .sec-split, .sec-image-grid, .sec-form, .sec-legal {
      --section-gutter: var(--size-10);
      padding-inline: var(--section-gutter);
    }
  }
  @media (max-width: 640px) {
    .sec-hero, .sec-feature, .sec-testimonial, .sec-cta-banner, .sec-emblem,
    .sec-chapel, .sec-partners, .sec-split, .sec-image-grid, .sec-form, .sec-legal {
      --section-gutter: var(--size-4);
    }
    /* The phone step of the vertical rhythm (see the ladder note above). The hero
       keeps its own height; the footer has its own phone rule below. */
    .sec-feature, .sec-testimonial, .sec-cta-banner, .sec-emblem,
    .sec-chapel, .sec-partners, .sec-split, .sec-image-grid, .sec-form, .sec-legal {
      padding-block: var(--size-8);
    }
    .sec-footer__inner { padding-inline: var(--size-4); }
    /* PHONE FOOTER (George, 2026-09-16: "very spaced out vertically", wanted
       "closer to how it is on desktop"). The library's phone footer (32px above,
       24px below, 32px between groups, a 16px gutter on the bottom bar) existed
       only on the showcase-only .sec-inner--mobile modifier. Phones therefore got
       the desktop 96px top, 48px gaps and an 80px bottom-bar gutter, and the
       copyright wrapped to five lines. The link pitch is set in the touch-target
       block at the end of this sheet. */
    .sec-footer__inner { padding-block: var(--size-8) var(--size-6); gap: var(--size-8) var(--size-5); }
    /* The brand block takes its own row and the link columns pair up beneath it.
       With the tighter gap, the brand and the first column fitted on one line,
       and that column shrank to 120px and wrapped its links in two. */
    .sec-footer__brand { flex-basis: 100%; }
    .sec-footer__nav-col { flex: 1 1 calc((100% - var(--size-5)) / 2); gap: 0; }
    .sec-footer__nav-label { margin-bottom: var(--size-1); }
    /* The language switcher goes ABOVE the copyright (George, 2026-09-16), so the
       copyright line gets the bar's whole width. column-reverse keeps the source
       order (copyright, then switcher), which desktop still reads left to right.
       The switcher's first button is padded for its hit area, so it is pulled
       back by that padding to line its text up with the copyright. */
    .sec-footer__bottom-inner {
      padding-inline: var(--size-4);
      flex-direction: column-reverse; align-items: flex-start; gap: var(--size-1);
    }
    .sec-footer__lang-switcher { margin-left: calc(-1 * var(--size-2p5)); }
    /* The English line measured 344px against the 343px a 375px phone leaves it.
       The tight tracking step (-0.16px a character) takes it to 335 and onto one
       line. The Spanish line is 419px and still takes two. */
    .sec-footer__copyright { letter-spacing: var(--tracking-tight); }
  }
  /* Closing CTA — the library ships the stacked mobile look as showcase-only
     classes (.sec-inner--mobile + .sec-cta-banner__ctas--stack) and never wrote
     the production @media half, so on a phone the heading kept a row layout and
     the CTA row ran ~270px past the viewport, where the section's own
     `overflow: hidden` clipped it: "Book a Call" was fully off-screen and
     "Explore Our Programs" was cut mid-word. Mirror the --mobile intent.
     768, NOT 700 (2026-09-14): the Summer Experience closing banner clipped its CTA
     row by 33px at exactly 768 in English, one notch above the old breakpoint —
     the labels stop fitting beside the heading before 700. */
  @media (max-width: 768px) {
    .sec-cta-banner__inner {
      flex-direction: column;
      align-items: center;
      text-align: center;
    }
    .sec-cta-banner__content { align-items: center; text-align: center; }
    /* Stacked and CENTERED, not stretched. The buttons used to take
       `width: 100%` here, which on a 375px phone drew two 343px slabs and made
       a closing banner look like a settings screen (George, 2026-09-08). The
       column still needs its full width for the centering to have something to
       center in; only the buttons go back to their own size. */
    .sec-cta-banner__ctas {
      flex-direction: column;
      align-items: center;
      gap: var(--size-2p5);
      width: 100%;
    }
    .sec-cta-banner__ctas > .btn { width: auto; }
  }
  /* BUTTON LABELS WRAP ON PHONES (George, 2026-09-14). `.btn` is nowrap so a label
     never breaks inside a row of buttons; at ≤480 a long Spanish CTA ("Descubra
     nuestros programas") is wider than the row it sits in, and nowrap then pushes
     the button out of its box instead of wrapping the words. Phones only — desktop
     and tablet button rows keep their single line. */
  @media (max-width: 480px) {
    .btn { white-space: normal; text-align: center; line-height: var(--lh-sm); }
  }
  /* NO AUTOMATIC HYPHENATION IN HEADINGS (George, 2026-09-16). This reverses the
     2026-09-14 `hyphens: auto` rule for Spanish display headings. That rule split
     every heading word that half-fitted a line, not only the words that could not
     fit at all: 130 Spanish heading words broke across lines on phones and tablets
     ("DONDE CRE- / CEN LOS DE- / PORTISTAS"). The device type scale and the
     full-width hero above now make the words fit instead.
     What stays is the last-resort guard. overflow-wrap breaks only a word that is
     wider than its whole line, so it changes nothing that fits (at 375 it never
     fires). At 320 it keeps "Escríbanos" inside its two-up card instead of past
     the edge. It stays scoped to Spanish, like the rule it replaces: English at
     320 was left as it is (George, 2026-09-16). */
  @media (max-width: 768px) {
    :lang(es) .sec-hero__heading,
    :lang(es) .section-hdr__heading,
    :lang(es) .sec-cta-banner__heading,
    :lang(es) .feature-card__title {
      overflow-wrap: anywhere;
    }
  }
  /* A feature card's body can hold one unbreakable token — /contact's card carries
     "info@odiseasports.com", 134px in a 124px two-up card at 375, both languages.
     An address has no dictionary break, so this is overflow-wrap alone: the token
     splits at the box edge rather than leaving it. */
  @media (max-width: 480px) {
    .feature-card__body { overflow-wrap: anywhere; }
  }
  /* Horizontal venue cards stack on phones. A 140px min-width media strip beside a
     text body leaves ~190px of measure at 375px, which is below the readable
     minimum; the vertical card is the same component without that squeeze. */
  @media (max-width: 640px) {
    .venue-card--horizontal,
    .venue-card--horizontal.venue-card--reverse { flex-direction: column; }
    /* --size-36 (144px) restores the vertical card's own media height, which the
       --horizontal variant had replaced with height:auto + a 38% width. */
    .venue-card--horizontal .venue-card__media { width: 100%; height: var(--size-36); }
  }
  /* Carousels — same --mobile-without-@media gap. The card WIDTH is CSS-driven while
     the step/dot count is JS-driven, so both must flip at the SAME breakpoint or they
     disagree: the hub script gates on (max-width: 700px), so this block does too.
     Without it a 375px phone showed 3 quote cards and 5 logos side by side. */
  @media (max-width: 700px) {
    .sec-testimonial__track .quote-block { flex: 0 0 100%; }
    .sec-partners__track { gap: var(--size-5); }
    .sec-partners__track .sec-partners__logo {
      flex: 0 0 calc((100% - 2 * var(--size-5)) / 3);
    }
  }
  /* Split panels stack on phones — production pages don't apply --reversed/-mobile
     modifiers, so the two flex children kept sharing one row and the media side
     collapsed to a couple of px wide (measured 2px on /for-clubs at 375). */
  @media (max-width: 900px) {
    .sec-split__inner,
    .sec-split--reversed .sec-split__inner { flex-direction: column; }
    .sec-split__text, .sec-split__media { flex: none; width: 100%; }
    /* 16/9 on phones is the .sec-split__img--mobile ratio the library declares. */
    .sec-split__img { aspect-ratio: 16 / 9; }
    /* --phone-top anchors the crop to the top of the frame, for a photograph whose
       subjects stand in the upper half (George, 2026-09-25, academy /apply "Rather
       Talk First?"). Phone-only on purpose: the desktop frame is 4:3 and centres
       fine; it is the 16:9 squeeze that cuts heads off. */
    .sec-split__img--phone-top { background-position: center top; }
    /* THE PICTURE LEADS ON A PHONE (George, 2026-09-09). Source order is
       text-then-media because that is the reading order side by side; stacked,
       it buries the photograph under a heading and a paragraph. `order` lifts
       the media visually WITHOUT touching source order, so a screen reader
       still meets the heading before its body text. */
    .sec-split__media { order: -1; }
    /* --text-first is the exception to that rule, not a reversal of it (George,
       2026-09-24). A split sitting directly under a hero has a photograph
       immediately above it already, so leading with a second one stacks two
       images with nothing between them. Where that happens — the academy /about
       intro — the text leads and the photograph follows. Everywhere else the
       picture still leads. */
    .sec-split--text-first .sec-split__media { order: 0; }
  }
  /* Image grid + text — the same treatment, and it had NO stacking rule at all:
     the grid and the text kept sharing one row on a phone, each taking half of
     375px. The media is already first in source order, so stacking alone puts
     the pictures on top. The 2fr/1fr grid also evens out — the small frames are
     ~110px wide otherwise — which is what the library's own `--mobile` modifier
     does, written here as the production half that pages actually get. */
  @media (max-width: 900px) {
    .sec-image-grid__inner { flex-direction: column; align-items: stretch; gap: var(--size-8); }
    /* ⚠ `--reversed` declares flex-direction on a TWO-class selector, so it
       outranks the single-class rule above wherever that sits — specificity, not
       source order. Left unmatched, a reversed grid kept `row-reverse` on a phone
       and row-reverse laid its text out at x:-359: entirely off-screen to the
       LEFT, invisible and unreachable, with `scrollWidth` still equal to the
       viewport — so the harness's overflow check cannot see this defect at all.
       Found on /for-clubs 2026-09-10, the first page to use the modifier. */
    .sec-image-grid--reversed .sec-image-grid__inner { flex-direction: column; }
    .sec-image-grid__media,
    .sec-image-grid__text { flex: none; width: 100%; }
    .sec-image-grid__grid { grid-template-columns: 1fr 1fr; }
  }
  /* ── CARD MEDIA BAND ─────────────────────────────────────────────────────── */
  /* ⚠ These blocks modify components declared earlier and MUST stay below them.
     Every selector here is single-class, as are the rules they override, so
     source order decides. Placed near the top of this stylesheet the carousel
     lost flex-wrap:nowrap to the base grid's wrap and never engaged at all. */
  /* `venue-card` has carried a photo band since the beginning; `pillar-card` and
     `feature-card` only ever had an icon tile. Added 2026-09-04 for the /about
     review, where George's verdict on seven cards was "take off the icon, use an
     image on the top half of the card" — a card either leads with a glyph or with
     a photograph, and these two could only do the first.

     The band is the SAME 140px as venue-card's, deliberately: three card types
     showing photography at three different heights is the drift the icon set was
     just pulled out of. It breaks the card's own horizontal padding with a
     negative margin rather than the card zeroing its padding, so a card can carry
     a band without every other rule about it changing. */
  .pillar-card--media,
  .feature-card--media,
  .pricing-card--media { padding-top: 0; overflow: hidden; }
  .pillar-card__media,
  .feature-card__media,
  .pricing-card__media {
    position: relative;
    flex-shrink: 0;
    overflow: hidden;
    margin: 0 calc(-1 * var(--size-5)) var(--size-1);
    /* Explicit width, not left to the block's auto width: a box with an
       aspect-ratio and a min-height computes its width from those in
       WebKit/Safari, and the negative margins no longer widen it, so the photo
       stopped short of the card edge (Safari, 1280px and up). */
    width: calc(100% + 2 * var(--size-5));
    max-width: none;
    background: linear-gradient(135deg, var(--t-surface-inv) 0%, var(--t-accent-strong) 100%);
  }
  .pillar-card__media > img,
  .feature-card__media > img,
  .pricing-card__media > img {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover; display: block;
    transition: transform var(--dur-sheen) var(--ease-out);
  }
  .pillar-card--media:hover .pillar-card__media > img,
  .feature-card--media:hover .feature-card__media > img,
  .pricing-card--media:hover .pricing-card__media > img { transform: scale(1.05); }
  /* A pricing card sets its rows 4px apart (a tier, a term and a price read as
     one block), so under its band that leaves the title 8px from the photograph.
     It takes more room than a pillar or feature card's band needs. */
  .pricing-card__media { margin-bottom: var(--size-4); }
  /* ── CARD MEDIA BAND: HEIGHT ─────────────────────────────────────────────── */
  /* One rule for every card that shows a photograph. It was a flat 140px, which
     is a different fraction of the card at every width the estate uses — a
     shallow letterbox on a 628px values card, and nearly half a 303px results
     card. George, 2026-09-04: "when the card has images, I want their minimum
     height to be larger so that photos show more."

     So the band takes a RATIO and is bounded at both ends: it grows with the
     card, never falls below 180px on the narrowest card in the estate, and
     never takes more than 280px on the widest, where 16:9 would otherwise leave
     a 354px photograph over 130px of text.

       281px card (mobile carousel)  → 180  (floor)
       303px card (academy results)  → 180  (floor)
       411px card (3-up)             → 231
       486px card (events)           → 273
       628px card (values, 2-up)     → 280  (cap)

     Both existing shape variants opt out, or the ratio would fight the height
     they set: --horizontal is a side-by-side panel stretched to the card, and
     --portrait is a fixed 224px headshot frame. */
  .venue-card__media,
  .pillar-card__media,
  .feature-card__media,
  .pricing-card__media {
    height: auto;
    aspect-ratio: 16 / 9;
    min-height: 180px;
    max-height: 280px;
  }
  /* The same WebKit rule as the negative-margin bands above: with an
     aspect-ratio and a min-height the band took its width from the ratio
     (320px in a 305px card at 375). The horizontal variant's 38% below wins on
     specificity. */
  .venue-card__media { width: 100%; max-width: none; }
  .venue-card--horizontal .venue-card__media,
  .venue-card--portrait .venue-card__media {
    aspect-ratio: auto;
    min-height: 0;
    max-height: none;
  }
  .venue-card--portrait .venue-card__media { height: var(--size-56); }
  /* ── FEATURE GRID: TWO UP ────────────────────────────────────────────────── */
  /* The base grid is three per line and `--4` caps at four. Two is the third
     legitimate count — four cards read better as 2 x 2 than as 3 + 1 when each
     one carries a photograph and wants the width. */
  /* Two up at EVERY width, phone included (George, 2026-09-04). It was briefly
     scoped above 640px, when the cards it governs still carried a photograph
     and two of those side by side on a 375px screen were 165px of picture each.
     With the cards down to a title and one line there is nothing to crowd, and
     "--2" meaning two columns everywhere is what the name says.

     A future two-up block that DOES carry photography should collapse on a
     phone — that wants its own modifier rather than a width guard bolted back
     onto this one, or it re-breaks the case this now serves. */
  .sec-feature__grid--2 > * {
    flex: 0 1 calc((100% - var(--size-5)) / 2);
  }
  /* Two 162px columns on a 375px phone leave 122px of inner width, and a 24px
     card title does not fit a long single word in it — "Perseverance" and
     "Development" both rendered past the card's right edge. The word cannot
     wrap and the card will not grow, so the type comes down instead. Scoped to
     this grid at this width: nothing else changes size.
     One step further since 2026-09-16 (18 → 16px): every phone card title came
     down to 20px with the device type scale, and the two-up titles stay a step
     below the one-up ones, which keeps "perseverancia" inside its card at 375. */
  @media (max-width: 640px) {
    .sec-feature__grid--2 .feature-card__title,
    .sec-feature__grid--2 .pillar-card__title {
      font-size: var(--font-size-base);
      line-height: var(--lh-sm);
    }
  }
  /* --phone-2: the grid keeps its desktop count (put it beside `--4`) and goes
     2 x 2 on a phone instead of one card per line (George, 2026-10-02, the four
     pillars on /departments/sports-performance). `--2` is two up at every width;
     this is two up on a phone only. For four short text cards: a card with a
     photograph should not sit two to a 375px line. The card gives up a little
     padding and the title steps down to the two-up size, so a long word still
     fits a 160px column. Declared after the 640px base stack, which it must
     outrank at equal specificity. */
  @media (max-width: 640px) {
    .sec-feature__grid--phone-2 { gap: var(--size-3); }
    .sec-feature__grid--phone-2 > * {
      flex: 0 1 calc((100% - var(--size-3)) / 2);
    }
    .sec-feature__grid--phone-2 .feature-card,
    .sec-feature__grid--phone-2 .pillar-card { padding: var(--size-4); }
    .sec-feature__grid--phone-2 .feature-card__title,
    .sec-feature__grid--phone-2 .pillar-card__title {
      font-size: var(--font-size-base);
      line-height: var(--lh-sm);
    }
  }
  /* Nav for the swipe strip — same chrome as the partners carousel (arrows
     either side of a dot row), shown only where the strip is actually a strip.
     Above the breakpoint the cards are a plain grid and there is nothing to
     page through, so the nav is not merely hidden but absent from the tab
     order: `display: none` removes it from the accessibility tree too. */
  .sec-feature__nav { display: none; }
  @media (max-width: 700px) {
    .sec-feature__nav {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: var(--size-4);
      margin-top: var(--size-5);
    }
  }
  /* ── FEATURE GRID: SWIPE ON A PHONE ──────────────────────────────────────── */
  /* Five photo cards stacked one per screen is an endless scroll on a phone. This
     turns the row into a horizontal scroll-snap strip below the mobile breakpoint
     and leaves every wider viewport exactly as it was.

     Deliberately CSS, not the partners-carousel JS. That carousel builds an
     infinite loop by appending a clone set and auto-advances on a timer, which is
     right for a logo strip and wrong for cards a reader is part-way through — it
     would move under them mid-sentence, and it doubles the DOM (and so the image
     payload) of a section that already carries five photographs. Scroll-snap is
     swipeable natively, keyboard- and screen-reader-navigable, and adds nothing
     to the page weight.

     700px matches the breakpoint the hub's carousels already gate on in JS. */
  @media (max-width: 700px) {
    .sec-feature__grid--carousel {
      display: flex;
      flex-wrap: nowrap;
      justify-content: flex-start;
      overflow-x: auto;
      scroll-snap-type: x mandatory;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;
      /* Bleed to the viewport edge so the strip reads as scrollable, then pad
         the ends back so the first and last card still align with the gutter.
         The bleed is the section's own gutter (--section-gutter, set by the
         gutter ladder), not a constant: a hard-coded 20px against the 16px phone
         gutter ran the strip 4px past both screen edges, so a phone could drag
         the whole page sideways (2026-09-16). scroll-padding carries the same
         inset into snapping. Without it, `scroll-snap-align: start` parked every
         card flush against the scrollport edge, and the first card's border sat
         off-screen. */
      margin-inline: calc(-1 * var(--section-gutter, var(--size-5)));
      padding-inline: var(--section-gutter, var(--size-5));
      scroll-padding-inline: var(--section-gutter, var(--size-5));
      padding-bottom: var(--size-2);
    }
    .sec-feature__grid--carousel::-webkit-scrollbar { display: none; }
    .sec-feature__grid--carousel > * {
      flex: 0 0 82%;
      scroll-snap-align: start;
    }
    /* A flip-card is the same size whether or not its group was long enough to
       become a strip (George, 2026-09-24). Only groups of three or more get
       --carousel; a group of one or two fell through to the full-width stack and
       rendered its card 343x457 against the strip's 281x375 — on the academy
       /staff page, Education and Talent Management sat visibly larger than the
       departments either side of them. Matching the strip's basis, rather than
       making short groups scrollable, keeps a one-person group from presenting a
       swipe affordance it has nothing to swipe to. */
    .sec-feature__grid:not(.sec-feature__grid--carousel) > .flip-card {
      flex: 0 0 82%;
    }
    .sec-feature__grid:not(.sec-feature__grid--carousel):has(.flip-card) {
      justify-content: flex-start;
    }
    /* …except a lone card, which centres (George, 2026-10-02, the academy
       Education department's one person: "it is weird to see the card aligned to
       the left"). Left-aligned, it lined up with nothing and left a gap on the
       right. Outranks the rule above on specificity, so order does not matter. */
    .sec-feature__grid:not(.sec-feature__grid--carousel):has(> .flip-card:only-child) {
      justify-content: center;
    }
  }
  /* ── TOUCH TARGETS ───────────────────────────────────────────────────────── */
  /* A floor of 44px on anything a finger has to hit. Scoped to `pointer: coarse`
     rather than to a width breakpoint on purpose: 44px is a property of fingers,
     not of viewports, and a width query would inflate the same chrome on a small
     laptop window while missing a tablet held in landscape.

     Measured before this existed, every page on both sites carried the same six
     to eight undersized controls — all of them in the shared nav and footer
     rather than in page content, so a per-page review would have re-found them
     twenty-two times. The worst was the academy header's Apply button at 69x30:
     the conversion CTA, on the phone, at two thirds of the minimum.

     It sets a FLOOR, not a size — a control already at or above 44 is untouched,
     and nothing here changes how anything looks on a mouse-driven screen.
     ⚠ It must stay at the BOTTOM of this stylesheet. Every selector here is
     single-class, and so are the rules it raises — .sec-footer__lang-btn sets
     its own min-height: 24px (a deliberate WCAG 2.5.8 AA choice, kept; 44 is
     simply the stricter floor). With equal specificity source order decides,
     and this block declared earlier in the file lost to all of them silently.

     Re-measure with: node scripts/shoot-pages.mjs --view=mobile */
  @media (pointer: coarse) {
    .btn,
    .sec-nav__brand, .site-nav__brand,
    .sec-nav__toggle, .site-nav__toggle,
    .sec-nav__mobile-close, .site-nav__mobile-close,
    .sec-nav__mobile-link, .site-nav__mobile-link,
    .sec-footer__social-link, .sec-footer__lang-btn,
    .site-nav__lang-inline-link,
    .form-field__input, .form-field__select,
    .feature-card__cta,
    /* The mail and phone fallbacks on the gated /apply page are standalone
       contact rows, not links inside prose — on a phone they are the whole
       point of that page while the embed is not mounted. */
    .embed-slot__gate-detail-value a,
    .carousel-btn {
      min-height: var(--size-11);
    }
    .sec-nav__brand, .site-nav__brand,
    .sec-nav__toggle, .site-nav__toggle,
    .sec-nav__mobile-close, .site-nav__mobile-close,
    .sec-footer__social-link, .sec-footer__lang-btn, .carousel-btn,
    .site-nav__lang-inline-link {
      min-width: var(--size-11);
    }
    /* The footer's link lists are the one deliberate exception, at 32px rather
       than 44 (George, 2026-09-16: the phone footer was "very spaced out
       vertically"). At 44px plus the column gap, a phone spaced the links 54px
       apart against 30px on a desktop, and the footer ran 1,131px tall. 32px is
       still above the WCAG 2.5.8 AA minimum of 24px, and a list of plain links
       at a steady pitch is the case that minimum was written for. */
    .sec-footer__nav-link {
      min-height: var(--size-8);
      display: flex; align-items: center;
    }

    /* The nav's small-button variant is a deliberate visual size; it gets the
       floor without being re-styled, so it still reads as the compact button. */
    .btn--sm { padding-block: var(--size-2p5); }

    /* min-height does nothing to an inline box, so the contact links need one
       that honours it. inline-flex keeps them on the text line. */
    .embed-slot__gate-detail-value a { display: inline-flex; align-items: center; }

    /* A carousel dot must stay a dot. Growing the box would turn the row of
       dots into a row of circles, so the HIT AREA grows instead, via a
       pseudo-element centred on the dot — invisible, and 44x44. */
    .carousel-dot { position: relative; }
    .carousel-dot::after {
      content: '';
      position: absolute;
      top: 50%; left: 50%;
      width: var(--size-11); height: var(--size-11);
      transform: translate(-50%, -50%);
    }
  }
