/* =====================================================================
   SITES AT SCALE, DANCE STUDIO WEBSITE TEMPLATE
   REFERENCE BUILD, style.css
   ---------------------------------------------------------------------
   TEMPLATE DEMO. The studio rendered by this stylesheet ("Riverbend
   Dance Co.") is fictional. See 02-homepage-template.md section 4.1.
   ---------------------------------------------------------------------
   WHAT THIS FILE IS
   The design system for the dance template. It implements
   01-eye-path-design-law.md verbatim: its spacing scale (F1, F2, F3),
   type ramp (F4), opacity ladder (G2), motion spec (E1, E3, E4) and
   named zones (A3). It also carries the shared component vocabulary in
   04-inner-page-library.md section 7, so the same file serves the
   plug-and-play inner pages, not only the homepage.

   HOW TO USE IT ON A CLIENT BUILD
   1. Edit ONLY the LAYER 2 block below. Nothing else in this file is a
      per client decision.
   2. Run the contrast remediation in 07-brand-application-and-conflicts
      section 1.5 BEFORE you edit it, and paste the measured ratios into
      the comments next to each hex. Why: L1-03 requires measured floors,
      not eyeballed ones, and a reviewer has to be able to check work
      they did not do.
   3. Do not add a gradient. L1-01: the only gradient allowed anywhere is
      the one inside a logo mark.
   4. Do not add a second saturated colour to a section. L1-02.

   THE TWO LAYER MODEL (07 sections 1.2 and 1.3)
   LAYER 1 is ours and is not negotiable: spacing, type scale, contrast
   floors, motion discipline, focus rings, tap targets. It answers "how
   good is it."
   LAYER 2 is the client's: palette, display face, radius, accent ramp.
   It answers "who is it."
   ===================================================================== */


/* =====================================================================
   LAYER 2, THE CLIENT LAYER. THIS IS THE SWAP POINT.
   Everything a builder changes per studio lives between this banner and
   the "END LAYER 2" banner. If you are editing anything below the end
   banner, stop: you are changing the template, not the client.
   ===================================================================== */
:root {

  /* ---- Palette -----------------------------------------------------
     Demo studio: Riverbend Dance Co. (fictional).
     Every hex needs a named source in BRAND.md on a real build
     ("sampled from the 2019 recital backdrop" is a source, "looked
     nice" is not, 07 section 1.6 step 1).

     Contrast values below are computed from the WCAG relative luminance
     formula. RE-MEASURE THEM IN DEVTOOLS after any palette change and
     write the measured numbers back here. 07 section 1.5 step 5.
     -------------------------------------------------------------- */

  /* Page surfaces. Elevate Dance & Fitness Studio, Camilla GA.
     Brand reads cool-jewel (purple PMS 2612C, teal PMS 7730C, mint
     PMS 7478C, Brand Guide 2025), so the paper is a lilac-warm off
     white, never pure #FFFFFF. All ratios measured 2026-07-29, see
     BRAND.md. */
  --client-paper:        #FAF8FB;  /* primary tinted to ~98pct lightness. Ink on it 18.57:1 */
  --client-paper-tint:   #F0F7F3;  /* mint #9fd6bc at ~96pct. Ink 70pct on it 7.23:1 */
  --client-card:         #FFFFFF;  /* card fill. Ink 70pct on it 7.48:1 */

  /* Ink. Plum-cast near-black derived from the primary. 70pct on paper
     7.35:1, clears the 7:1 body floor. */
  --client-ink:          #0E0A12;

  /* The brand hue AS THE STUDIO OWNS IT (Brand Guide 2025, PMS 2612C).
     Rare case: white label on it = 8.37:1 PASS, so it may carry large
     surfaces, but the CTA still uses the ink variant below. */
  --client-primary:      #792b87;

  /* Derived ink variant, G3 procedure: hue held, lightness minus 4pct.
     White label on it = 9.47:1 PASS. On paper = 8.97:1.
     THIS is the CTA fill and the section accent. */
  --client-primary-ink:  #6D2779;

  /* Large tint fields only. No text under 24px sits on these. */
  --client-primary-soft: #F3E7F5;  /* primary at ~95pct. Ink on it 15.07:1 */
  --client-terminal:     #D8EEE2;  /* mint tint TERMINAL band. CTA fill on it 7.78:1 */
  --client-band-dark:    #241329;  /* deep plum footer band. White on it 17.50:1 */

  /* The accent ramp: one hue per program family. Marks, chips and
     hairlines only, never a button fill, never text under 24px.
     Teal remediation registered in BRAND.md: brand #1e817f fails 4.5:1
     as text (4.42:1); derived --client-ramp-2 #14615F is 6.85:1. */
  --client-ramp-1:       #6D2779;  /* classes by age (primary ink) */
  --client-ramp-2:       #14615F;  /* classes by style (teal ink) */
  --client-ramp-3:       #47795D;  /* tumbling (mint deepened) */
  --client-ramp-4:       #533D6B;  /* adult fitness (plum violet) */

  /* Deep teal band (vibrancy pass, Connor 2026-08-08): brand secondary
     PMS 7730C #1e817f, hue and saturation held, lightness stepped down
     (G3 procedure) until every white mix clears its floor on the Proof
     band. Measured: white 11.19:1, white 84pct 8.43:1 (7:1 body floor),
     white 70pct 6.35:1 and 66pct 5.85:1 (caption roles), mint #9fd6bc
     6.83:1 (display-size stat numerals), terminal #D8EEE2 9.19:1. */
  --client-teal-band:    #0F4241;

  /* ---- Type --------------------------------------------------------
     Brand guide faces: Olive Village Regular Italics (script, logo
     only; script faces banned for headings, 07 1.6 step 2) + Gotham.
     Web faces per the studio's Brand Guide (Jetmore Banot, 06/17/2026):
     heading typeface Lora 600/700 (italic 600 for accent words), body
     typeface Montserrat 400/600. 2 families, 4 weights. --------- */
  --client-display: "Lora", "Georgia", serif;
  --client-body:    "Montserrat", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* ---- Shape -------------------------------------------------------
     One radius site wide, echoing the rounded bowl geometry of the
     Elevate logotype (assets/img/raw/logos/).
     -------------------------------------------------------------- */
  --client-radius: 14px;
}
/* ===================== END LAYER 2 ================================== */


/* =====================================================================
   LAYER 1, THE QUALITY LAYER. Do not edit per client.
   ===================================================================== */
:root {

  /* --- Semantic aliases. Components read these, never the client vars,
         so a palette swap is a four line edit. ---------------------- */
  --paper:      var(--client-paper);
  --paper-tint: var(--client-paper-tint);
  --card:       var(--client-card);
  --ink:        var(--client-ink);
  --accent:     var(--client-primary-ink);  /* ONE accent per section, L1-02.
                                               Sections may re-declare this. */
  --accent-soft: var(--client-primary);      /* decorative only, never text */
  --display:    var(--client-display);
  --body:       var(--client-body);
  --radius:     var(--client-radius);

  /* --- The opacity ladder, design law G2. Do not invent intermediate
         values. On light background. ------------------------------- */
  --ink-heading:  1;      /* headings, ink 100 pct */
  --ink-body:     0.70;   /* body copy, ink 70 pct, measured 7.4:1 here */
  --ink-caption:  0.60;   /* captions, meta, labels, measured 5.1:1 here */
  --ink-muted:    0.40;   /* de-emphasised */
  --ink-hairline: 0.10;   /* borders and dividers */
  --glyph-op:     0.08;   /* depth glyph on light, E1 says 0.06 to 0.10 */

  /* On a dark band, same ladder, on-dark values. */
  --ond-body:     0.82;
  --ond-caption:  0.65;
  --ond-muted:    0.45;
  --ond-hairline: 0.22;

  /* --- Containers and gutters, design law F1 --------------------- */
  --container:      min(1200px, calc(100% - 48px));
  --container-read: min(720px,  calc(100% - 48px));

  /* --- Section padding, design law F2.
         64 at 390, 80 at 768, 96 at 1024, 112 at 1440 and up.

         PX-OFFSET RAMPS, NOT PURE vw. A pure-vw clamp cannot pass through
         four fixed points: clamp(64px, 7.8vw, 112px) gave 64px at 768
         (table says 80) and 80px at 1024 (table says 96), so the built
         page was 16px short of its own spec at both middle breakpoints.
         Each ramp below is fitted to the table's endpoints and lands
         within 3px at every intermediate row. Verify with
         `getComputedStyle` at 390 / 768 / 1024 / 1440 before changing. -- */
  --pad-section:      clamp(64px, 46.1px + 4.571vw, 112px);
  --pad-band:         clamp(72px, 51.2px + 5.333vw, 128px);
  --pad-section-same: clamp(48px, 39.1px + 2.286vw,  72px); /* same tone */

  /* --- Grid gutters, design law F3 ------------------------------- */
  --gap-card:  clamp(16px, 1.7vw, 24px);
  --gap-split: clamp(32px, 5.0vw, 72px);
  --pad-card:  clamp(20px, 2.2vw, 28px);

  /* --- The type ramp, design law F4. Numbers are verbatim. ------- */
  --t-h1:      clamp(2.75rem, 5.6vw, 4.5rem);      /* 44 to 72 */
  --t-h2:      clamp(2rem, 3.8vw, 3rem);           /* 32 to 48 */
  --t-h3:      clamp(1.25rem, 1.8vw, 1.625rem);    /* 20 to 26 */
  --t-lede:    clamp(1.0625rem, 1.3vw, 1.25rem);   /* 17 to 20 */
  --t-body:    1.0625rem;                          /* 17 */
  --t-small:   0.9375rem;                          /* 15 */
  --t-caption: 0.84rem;                            /* 13.5 */
  --t-eyebrow: 0.78rem;                            /* 12.5 */
  --t-stat:    clamp(2.5rem, 4.4vw, 3.75rem);      /* 40 to 60 */

  /* --- Motion, design law E1. transform and opacity only. -------- */
  --ease:       cubic-bezier(.19, 1, .22, 1);
  --dur-reveal: .6s;
  --dur-media:  .9s;
  --dur-ui:     .24s;

  /* --- Chrome ---------------------------------------------------- */
  --nav-h:      80px;
  --nav-h-sm:   68px;
  --tap:        44px;   /* Policy 3.6 minimum, everywhere */
  --tap-mobile: 52px;   /* design law A3 mobile CTA minimum */
  --hairline:   1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  --focus-ring: 3px;
}


/* =====================================================================
   RESET AND BASE
   ===================================================================== */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Sticky nav plus the demo banner. Anchor targets land under the bar
     without this. */
  scroll-padding-top: calc(var(--nav-h) + 16px);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--t-body);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg, video { display: block; max-width: 100%; }
img, video { height: auto; }

/* Every media container is aspect locked so CLS is 0 (Policy 3.6). */
figure { margin: 0; }

ul, ol { margin: 0; padding: 0; list-style: none; }
p { margin: 0 0 1rem; }
p:last-child { margin-bottom: 0; }

a { color: inherit; }


/* =====================================================================
   FOCUS, TAP TARGETS, SKIP LINK
   Accessibility is part of the demo, not an afterthought.
   ===================================================================== */
:focus-visible {
  outline: var(--focus-ring) solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}
.on-dark :focus-visible,
.cta-final :focus-visible { outline-color: var(--ink); }

.skip-link {
  position: absolute;
  left: 12px; top: -100px;
  z-index: 200;
  padding: 12px 18px;
  min-height: var(--tap);
  display: inline-flex; align-items: center;
  background: var(--ink);
  color: var(--paper);
  font-weight: 600;
  text-decoration: none;
  border-radius: var(--radius);
  transition: top var(--dur-ui) var(--ease);
}
.skip-link:focus-visible { top: 12px; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap; border: 0;
}


/* =====================================================================
   TYPE ROLES, design law F4.
   Max line lengths are enforced in CSS, not in a copy brief, because a
   copy brief cannot be checked at 150 builds.
   ===================================================================== */
h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 700;
  color: var(--ink);
  margin: 0 0 .6em;
  text-wrap: balance;
}

h1 {
  font-size: var(--t-h1);
  line-height: 1.04;
  letter-spacing: -0.02em;
  max-width: 12ch;   /* keeps Policy 3.2's "under 10 words" from wrapping
                        into four lines and swallowing the HEADLINE BLOCK */
}
h2 {
  font-size: var(--t-h2);
  line-height: 1.08;
  letter-spacing: -0.018em;
  max-width: 18ch;
}
h3 {
  font-size: var(--t-h3);
  line-height: 1.2;
  letter-spacing: -0.01em;
  max-width: 26ch;
}
/* h4 is the Product card title only. Defined by 02 section 8.4 / CL-3,
   because the type ramp in design law F4 has no h4 token. */
h4 {
  font: 600 1.0625rem/1.35 var(--display);
  letter-spacing: -0.01em;
  margin: 0 0 .4em;
}

.eyebrow {
  font-size: var(--t-eyebrow);
  line-height: 1;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 600;
  max-width: 32ch;
  color: var(--accent);
  margin: 0 0 1rem;
}

.lede, .answer {
  font-size: var(--t-lede);
  line-height: 1.55;
  max-width: 58ch;
  color: color-mix(in srgb, var(--ink) 70%, transparent);
  text-wrap: pretty;
}
/* .answer is the 40 to 60 word direct answer that opens a question
   heading (Policy 3.4). Same type role as .lede, different name so QA
   can find it. 04 section 2.2. */

p, li { max-width: 68ch; }              /* body copy over 68ch is a reject */
.small { font-size: var(--t-small); line-height: 1.6; max-width: 60ch; }

.caption,
.caption-ill {
  font-size: var(--t-caption);
  line-height: 1.5;
  max-width: 48ch;
  color: color-mix(in srgb, var(--ink) 60%, transparent);
}
/* .caption-ill is the visible italic label that every piece of
   illustrative content carries (04 section 2.7, 07 D-13). A reviewer
   must be able to see at a glance what is real and what is placeholder. */
.caption-ill { font-style: italic; }

.stat {
  font-family: var(--display);
  font-size: var(--t-stat);
  line-height: 1;
  letter-spacing: -0.02em;
  font-weight: 700;
  color: var(--accent);
}

/* The "Illustrative" marker chip. Loud on purpose. */
.ill-flag {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  font-family: var(--body);
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  padding: .35em .7em;
  border-radius: 999px;
  color: var(--ink);
  background: repeating-linear-gradient(
    45deg,
    color-mix(in srgb, var(--ink) 8%, transparent) 0 6px,
    transparent 6px 12px
  );
  border: 1px dashed color-mix(in srgb, var(--ink) 45%, transparent);
}
/* Note on the stripes: this is a hatch pattern on a NON brand surface
   used to mark placeholder content in an internal reference build. It is
   not a gradient on a panel, a button or a badge in the L1-01 sense, and
   it does not ship on a client build. */


/* =====================================================================
   LAYOUT, design law F1 and F2
   ===================================================================== */
.container      { width: var(--container);      margin-inline: auto; }
.container-read { width: var(--container-read); margin-inline: auto; }

.section {
  position: relative;
  padding-block: var(--pad-section);
  /* overflow-x only, so the depth glyph cannot cause a horizontal
     scrollbar while a LEAD handoff can still cross the boundary
     vertically (design law C3). Never overflow: hidden here: that would
     clip the handoff, which is the Encore defect. */
  overflow-x: clip;
  overflow-y: visible;
}
/* Two sections on the same background tone never both run full padding.
   Two 112px paddings back to back is 224px of nothing, which blows the
   160px handoff limit in design law C3 on its own. */
.section.same-tone { padding-block: var(--pad-section-same); }

.band       { padding-block: var(--pad-band); }
.band-cream { background: var(--paper-tint); }
.band-dark  { background: var(--client-band-dark); color: #fff; }

.on-dark              { color: #fff; }
.on-dark .lede,
.on-dark .answer,
.on-dark p            { color: color-mix(in srgb, #fff 82%, transparent); }
.on-dark .caption,
.on-dark .caption-ill { color: color-mix(in srgb, #fff 65%, transparent); }
.on-dark h1, .on-dark h2, .on-dark h3, .on-dark h4 { color: #fff; }

/* The section header stack: eyebrow, H2, lede. BELOW THE FOLD ONLY.
   Design law conflict 4: applied to the hero this stack produces a
   fourth above-fold element, which Policy 3.2 forbids. */
.sec-head { max-width: 62ch; margin-bottom: clamp(28px, 3vw, 44px); }
.sec-head.center { margin-inline: auto; text-align: center; }
/* ...and centre the CHILDREN'S BOXES too, not just the text inside them.
   This rule centred .sec-head itself but every child carries its own
   measure (p/li cap at 68ch, .sec-head at 62ch) with margin 0, so the
   eyebrow and the h2 wrapper sat flush LEFT inside a centred parent and
   the header read as left-aligned. That is the "centred text needs
   centred boxes" law in CLAUDE.md, hit for the fourth time; it had been
   patched per-section twice already (#place-to-shine, #stakes) instead
   of here. Fixing it at the source: `sec-head center` is used once in
   the whole site, so this is contained. Client note 2026-08-28. */
.sec-head.center > * { margin-inline: auto; }


/* =====================================================================
   THE Z ALTERNATION UTILITIES, design law B1 and B2.
   The zigzag is STRUCTURAL here, not hand placed. A section declares its
   anchor with data-anchor and the layout follows. That is what stops
   builder 74 from putting two Z-RIGHTs in a row.

   Reading the anchor attributes top to bottom on the homepage must give:
   NAV-RIGHT, HERO-ANCHOR, Z-RIGHT, Z-LEFT, Z-RIGHT, Z-LEFT, TERMINAL.
   Two of the same word in a row means the page is wrong (design law A1).
   ===================================================================== */

/* A split section. Media panel is 42 to 55 pct of the container
   (design law F5), text is the partner, never a caption. */
.split {
  display: grid;
  grid-template-columns: 1fr minmax(0, 46%);
  gap: var(--gap-split);
  align-items: center;
}
.split.rev { grid-template-columns: minmax(0, 46%) 1fr; }
.split.rev > .split-copy  { order: 2; }
.split.rev > .split-img,
.split.rev > .split-media { order: 1; }

.split-copy > :last-child { margin-bottom: 0; }

/* .split-media is the media column with NO frame, used when the anchor
   is a built element (the structure number block) rather than a photo.
   .split-img is the framed, aspect locked version. */
.split-media { align-self: center; }

/* The media panel. Aspect locked, so CLS is 0 before the asset lands. */
.split-img {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--client-primary-soft);
}
.split-img.wide { aspect-ratio: 16 / 11; }
.split-img.card { aspect-ratio: 3 / 2; }
.split-img.tile { aspect-ratio: 1 / 1; }
.split-img > img,
.split-img > svg { width: 100%; height: 100%; object-fit: cover; }

/* HANDOFF H2 LEAD, design law C3 and 02 section 3.2.
   The panel's bottom edge sits 48px below the section's text baseline,
   so its lower corner is already visible inside the next section's top
   padding. On the WRAPPER, never on the section. */
.split-img.leads { margin-bottom: -48px; }

/* Anchor side helpers. They align the header stack away from the
   anchor, which is design law B1's AD-2 asymmetry device. */
/* AD-2 OFF-CENTER HEADER. The header BLOCK sits on the anchor side so it
   visibly does not fill the container. The TEXT inside it stays
   left-aligned always. An earlier version set `text-align: right` on
   z-left sections, which ragged-lefted the eyebrow, the H2 and the 40 to
   60 word direct answer. Body copy is never right-aligned: a ragged left
   edge costs the reader a re-scan on every line, which is the opposite of
   "easy to follow at every step". Off-center is a POSITION, not an
   alignment. */
[data-anchor="z-left"]  .sec-head { margin-left: 0; margin-right: auto; text-align: left; }
[data-anchor="z-right"] .sec-head { margin-left: auto; margin-right: 0; text-align: left; }
[data-anchor="z-left"]  .sec-head .eyebrow,
[data-anchor="z-right"] .sec-head .eyebrow { text-align: left; }

/* Card grids. Collapse to one column at 768. */
.grid-2, .grid-3, .grid-4 { display: grid; gap: var(--gap-card); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }

/* HANDOFF H3 OVERLAP, design law C3. The next section's leading element
   is pulled up over the boundary. On a WRAPPER, never on the section
   itself, never on the LCP element, never above the fold. The sending
   section's bottom padding drops by the same 64px (design law F2). */
.overlaps-up { position: relative; z-index: 2; margin-top: -64px; }

/* GUARD. C3 OVERLAP pulls a section's LEADING element up over the
   boundary. Put it on a later block sibling instead and the negative
   margin collapses with the previous sibling's margin-bottom
   (43.2px + -64px = -20.8px), so the element lands ABOVE the copy it was
   meant to follow and its opaque cards paint over live text. That shipped
   once. This makes it impossible: inside a block flow the pull only
   applies to the leading element. Grid and flex children never
   margin-collapse, so .split figures are unaffected. */
.sec-head + .overlaps-up,
.answer   + .overlaps-up { margin-top: 0; }

/* --- Rhythm utilities. They exist so no section carries an inline
       style: inline padding and margin overrides are exactly why the
       worked builds drifted out of Policy compliance (04 section 7.5
       rule 3). Use these, or add a named class. Never style="". ---- */
.mt-xs { margin-top: 8px; }
.mt-sm { margin-top: 14px; }
.mt-md { margin-top: 20px; }
.mt-lg { margin-top: 28px; }
.mt-sub   { margin-top: 1.6em; }
.mt-block { margin-top: 2.4em; }
.mb-sm { margin-bottom: 12px; }
.mb-md { margin-bottom: 20px; }
.no-mb { margin-bottom: 0; }

/* AD-1 FEATURED SPAN, design law B1.
   The featured card spans two columns AND sits on the anchor side, so it
   reinforces the section's one anchor instead of creating a second one
   (02 D-05). grid-column: 2 / -1 is what puts it on the right. */
.grid-3 > .featured { grid-column: 2 / -1; }


/* =====================================================================
   BUTTONS AND LINKS, 04 section 7.4
   One primary CTA per section. Never two primary CTAs in one viewport.
   ===================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5em;
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 15px 28px;
  border: 2px solid transparent;
  border-radius: var(--radius);
  font-family: var(--display);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--dur-ui) var(--ease),
              background-color var(--dur-ui) var(--ease),
              color var(--dur-ui) var(--ease);
}
.btn-primary {
  background: var(--accent);          /* derived ink variant, never the light hue */
  color: #fff;                        /* measured 10.7:1 on #711E3E */
}
.btn-primary:hover { transform: translateY(-2px); }

/* Outlined. Never in the same viewport as a .btn-primary (04 7.4). */
.btn-ghost {
  background: transparent;
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.btn-ghost:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }

/* Secondary action. A text link with a trailing arrow, NEVER a second
   button (design law G1). There is no secondary action above the fold
   at all (Policy 3.2). */
/* G1 ACCENT BUDGET. .text-link carries the accent, .svc-link does not.
   A .svc-link sits INSIDE a card that is already the affordance, so
   accenting it spends the section's accent budget once per card. The
   Product section ran ten accent elements against a cap of three, which
   is what makes a page read busy rather than expensive. Card links are
   ink; the arrow picks up the accent on hover only, where it is a
   response rather than decoration. */
.text-link,
.svc-link {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  min-height: var(--tap);
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  border-bottom: 2px solid color-mix(in srgb, var(--accent) 30%, transparent);
  transition: border-color var(--dur-ui) var(--ease);
}
/* Card links are ink, not accent. See the G1 note above. */
.svc-link {
  color: color-mix(in srgb, var(--ink) 82%, transparent);
  border-bottom-color: color-mix(in srgb, var(--ink) 22%, transparent);
}
.svc-link:hover { color: var(--ink); }
.text-link:hover, .svc-link:hover { border-bottom-color: var(--accent); }
.text-link .arw, .svc-link .arw { transition: transform var(--dur-ui) var(--ease); }
.text-link:hover .arw, .svc-link:hover .arw { transform: translateX(3px); }

/* A small non interactive fact tag. */
.chip {
  display: inline-flex;
  align-items: center;
  padding: .35em .75em;
  border-radius: 999px;
  font-size: var(--t-caption);
  font-weight: 600;
  color: var(--ink);
  background: color-mix(in srgb, var(--accent-soft) 40%, transparent);
}


/* =====================================================================
   SECTION 0, MAIN NAVIGATION
   Policy 3.1 section 1. Design law zone: NAV-RIGHT (A3).
   The nav CTA is right aligned flush to the container edge at x 1320
   because the end of F-BAR 1 is the first place the eye rests after the
   logo (design law A1).
   ===================================================================== */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  transition: box-shadow var(--dur-ui) var(--ease),
              border-color var(--dur-ui) var(--ease);
  border-bottom: 1px solid transparent;
}
/* The nav's ONLY permitted state change is a colour transition, not
   motion (design law E2). */
.nav.scrolled {
  border-bottom-color: color-mix(in srgb, var(--ink) 10%, transparent);
  box-shadow: 0 6px 24px color-mix(in srgb, var(--ink) 6%, transparent);
}

.nav-inner {
  position: relative;   /* containing block for the NAV-RIGHT overlay zone */
  display: flex;
  align-items: center;
  gap: clamp(16px, 2vw, 32px);
  height: var(--nav-h);
}
.nav-logo { display: inline-flex; align-items: center; min-height: var(--tap); }
.nav-logo svg { height: 34px; width: auto; }

.nav-links {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.6vw, 26px);
  margin-left: clamp(8px, 2vw, 40px);
  margin-right: auto;                 /* pushes phone plus CTA to NAV-RIGHT */
}
.nav-links a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  font-size: var(--t-small);
  font-weight: 600;
  text-decoration: none;
  color: color-mix(in srgb, var(--ink) 70%, transparent);
  border-bottom: 2px solid transparent;
  transition: color var(--dur-ui) var(--ease);
}
.nav-links a:hover { color: var(--ink); }
.nav-links a[aria-current="page"] {
  color: var(--ink);
  border-bottom-color: var(--accent);
}

/* The phone is a plain tel: TEXT LINK immediately left of the CTA.
   Never a button: a tel: button beside the trial button is a second
   primary action, which Policy 3.2 prohibits (02 section 6.3). */
.nav-phone {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  font-size: var(--t-small);
  font-weight: 600;
  text-decoration: none;
  color: color-mix(in srgb, var(--ink) 70%, transparent);
}
.nav-phone:hover { color: var(--ink); }

.nav-cta { padding: 11px 20px; font-size: var(--t-small); }

/* Mobile hamburger. It sits to the LEFT of the CTA, never the right,
   because the CTA must hold the true right edge (design law A3). */
.menu-btn {
  display: none;
  width: var(--tap); height: var(--tap);
  align-items: center; justify-content: center;
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  border-radius: 10px;
  cursor: pointer;
  color: var(--ink);
}
.menu-btn svg { width: 20px; height: 20px; }

/* The mobile panel. Opacity and transform only, 240ms. Never a slide
   down that pushes content, which causes CLS (04 section 5.4). */
.menu-panel {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 96px 24px calc(24px + env(safe-area-inset-bottom));
  background: var(--paper);
  opacity: 0;
  transform: translate3d(0, -8px, 0);
  visibility: hidden;
  transition: opacity var(--dur-ui) var(--ease),
              transform var(--dur-ui) var(--ease),
              visibility 0s linear var(--dur-ui);
}
.menu-panel.open {
  opacity: 1;
  transform: none;
  visibility: visible;
  transition-delay: 0s;
}
.menu-panel a {
  display: flex;
  align-items: center;
  min-height: var(--tap-mobile);
  font-family: var(--display);
  font-size: 1.35rem;
  font-weight: 700;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
}
.menu-panel .btn { margin-top: 20px; width: 100%; min-height: var(--tap-mobile); }
.menu-close {
  position: absolute; top: 16px; right: 20px;
  width: var(--tap); height: var(--tap);
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; cursor: pointer; color: var(--ink);
}
body.menu-open { overflow: hidden; }


/* =====================================================================
   SECTION 1, HERO. Policy 3.1 section 2, Policy 3.2.
   EXACTLY THREE ELEMENTS: the h1, ONE primary CTA, the hero visual.
   Zones: HEADLINE BLOCK (x 120 to 840, y 220 to 460) and
   HERO-ANCHOR-C (x 480 to 960, y 560 to 760), design law A3.
   Nothing in this section animates (design law E2): the h1 is the LCP
   element and an opacity-0 start pushes LCP past 2.5s.
   ===================================================================== */
.hero {
  position: relative;
  display: grid;
  min-height: calc(100vh - var(--nav-h));
  min-height: calc(100svh - var(--nav-h));   /* svh where supported */
  background: var(--paper);
  overflow: clip;
}
.hero-inner {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-rows: auto 1fr auto;
  width: var(--container);
  margin-inline: auto;
  padding-block: clamp(48px, 15.5vh, 140px);
}
/* HEADLINE BLOCK. Left half of the viewport, y 24 pct to 51 pct. The h1
   mass overlaps DEAD CENTER (x 432 to 1008, y 270 to 630), which is what
   passes design law C2 on a variant C hero. */
.hero-copy { grid-row: 1; max-width: 720px; }
.hero-copy h1 { margin-bottom: 0; }

/* HERO-ANCHOR-C. Centre third horizontally, lower band vertically.
   Policy 3.2 permits centre or bottom right and nothing else. */
.hero-cta-zone {
  grid-row: 3;
  display: flex;
  justify-content: center;
  /* align-items MUST be set. A flex container defaults to `stretch`, and
     because .hero-inner is a grid whose auto rows stretch to fill the
     100svh hero, the row grew and the button grew with it: 179px tall at
     390x844, a slab rather than a button. A CTA has one correct height. */
  align-items: center;
}
.hero-cta-zone .btn { min-height: 56px; font-size: 1.05rem; }

/* Variant C hero visual: the studio's own mark, inline SVG, flat, at
   full opacity, bleeding off exactly ONE edge (02 section 2.5 rule 2).
   A fully contained mark reads as a logo placed on a page. A bleeding
   mark reads as a brand the page is built out of.
   This is NOT the depth glyph: the glyph is 6 to 14 pct opacity behind a
   below-fold section, and there is ZERO glyph in the hero (design law E1). */
.hero-mark {
  position: absolute;
  z-index: 1;
  right: -8%;
  top: 8%;
  width: min(60vw, 760px);
  aspect-ratio: 1 / 1;
  pointer-events: none;
}
.hero-mark svg { width: 100%; height: 100%; }

/* Scrim recipes, design law D5. Present for variants A and B. There is
   no photograph in variant C, so no scrim is applied here. If you find
   yourself adding one, you have added a background image and you are
   building variant B badly (02 section 2.5).
   Directional gradient anchored to the text side, client ink, never
   #000, never flat, never above 0.90. */
.hero-scrim { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.hero-scrim.recipe-1 {   /* pairs with HERO-ANCHOR-BR */
  background: linear-gradient(to top right,
    rgba(11, 10, 14, 0.82) 0%,
    rgba(11, 10, 14, 0.55) 34%,
    rgba(11, 10, 14, 0.10) 62%,
    rgba(11, 10, 14, 0.00) 82%);
}
.hero-scrim.recipe-2 {   /* pairs with HERO-ANCHOR-C, top stop raised to 0.55 per CL-2 */
  background: linear-gradient(180deg,
    rgba(11, 10, 14, 0.55) 0%,
    rgba(11, 10, 14, 0.00) 28%,
    rgba(11, 10, 14, 0.58) 70%,
    rgba(11, 10, 14, 0.90) 100%);
}

/* Video hero (variant A). Poster carries the LCP and the composition. */
.video-hero video {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
}


/* =====================================================================
   THE ASSET SLOT MARKER
   Every image slot in the layout is present as a correctly sized,
   aspect locked container with a clear marker of what real asset drops
   in there. Reference build only: delete with the demo chrome.
   ===================================================================== */
.slot {
  position: relative;
  display: grid;
  place-items: center;
  height: 100%;
  padding: var(--pad-card);
  text-align: center;
}
.slot-art { position: absolute; inset: 0; }
.slot-art svg { width: 100%; height: 100%; }
.slot-note {
  position: relative;
  z-index: 1;
  max-width: 30ch;
  font-size: var(--t-caption);
  line-height: 1.45;
  font-style: italic;
  color: color-mix(in srgb, var(--ink) 72%, transparent);
  background: color-mix(in srgb, var(--card) 86%, transparent);
  border: 1px dashed color-mix(in srgb, var(--ink) 35%, transparent);
  border-radius: 10px;
  padding: 10px 14px;
}


/* =====================================================================
   SECTION 2, PRODUCT. Policy 3.1 section 3. Anchor Z-RIGHT.
   ===================================================================== */

/* The entry point row. The dance SOP's "intake strip", demoted to
   navigation: four TEXT LINKS in one neutral treatment, and only the
   free class link carries the accent and the conversion destination
   (07 register decision D-06). No buttons here: a button competes with
   the hero CTA in the grayscale test at the section boundary. */
.entry-row {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(14px, 2vw, 32px);
  align-items: center;
  padding-block: 18px;
  margin-bottom: clamp(28px, 3.5vw, 48px);
  border-top: var(--hairline);
  border-bottom: var(--hairline);
}
.entry-row a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  font-size: var(--t-small);
  font-weight: 600;
  text-decoration: none;
  color: color-mix(in srgb, var(--ink) 70%, transparent);
  border-bottom: 2px solid transparent;
}
.entry-row a:hover { color: var(--ink); border-bottom-color: color-mix(in srgb, var(--ink) 30%, transparent); }
.entry-row a.is-accent {
  color: var(--accent);
  border-bottom-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.entry-row a.is-accent:hover { border-bottom-color: var(--accent); }

/* The generic offering card. Flat fill, 1px hairline ring, no decorative
   shadow (07 section 1.4). Hover shadow only because the card is a link. */
.svc-card,
.info-card {
  background: var(--card);
  border: var(--hairline);
  border-radius: var(--radius);
  padding: var(--pad-card);
  transition: transform var(--dur-ui) var(--ease),
              box-shadow var(--dur-ui) var(--ease);
}
.svc-card:hover { transform: translateY(-3px); box-shadow: 0 10px 30px color-mix(in srgb, var(--ink) 8%, transparent); }
.svc-card p { color: color-mix(in srgb, var(--ink) 70%, transparent); margin-bottom: .8rem; }
.svc-card .svc-meta { font-size: var(--t-caption); color: color-mix(in srgb, var(--ink) 60%, transparent); }

/* AD-1: the featured card carries the accent field. Its title and copy
   are ink, not white, because ink on this tint measures well above the
   floor while white would not (07 section 1.5 rule 5). */
.svc-card.featured {
  background: var(--client-primary-soft);
  border-color: color-mix(in srgb, var(--accent) 25%, transparent);
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.svc-card.featured h4 { color: var(--accent); font-size: 1.35rem; }


/* =====================================================================
   SECTION 3, PROMISE. Policy 3.1 section 4. Anchor Z-LEFT.
   HANDOFF OUT: H3 OVERLAP. The Proof section's first card is pulled up
   64px and this section's bottom padding drops by the same 64px, so the
   net rhythm is unchanged (design law F2 and C3).
   ===================================================================== */
.promise { padding-bottom: calc(var(--pad-section) - 64px); }

/* Structure numbers. These describe WHAT THE FAMILY RECEIVES. They are
   not trust signals and they never cross with Proof's track record
   numbers (02 section 1.1, THE NUMBERS RULE). */
.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap-card);
  margin: 0 0 1.25rem;
}
.stats > div { padding-right: 12px; border-left: 3px solid var(--accent); padding-left: 14px; }
.stats dt { margin: 0; }
.stats dd {
  margin: .35rem 0 0;
  font-size: var(--t-caption);
  line-height: 1.35;
  color: color-mix(in srgb, var(--ink) 60%, transparent);
}

/* The structure number block used as the Z-LEFT media anchor when the
   studio has no usable film (02 section 1.1, Promise). */
.number-block {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  background: var(--client-primary-soft);
  overflow: hidden;
  display: grid;
  align-content: center;
  gap: clamp(18px, 2.6vw, 34px);
  padding: clamp(24px, 3.4vw, 48px);
}
.number-block .nb-row { position: relative; z-index: 1; }
.number-block .stat { display: block; }
.number-block .nb-label {
  font-size: var(--t-small);
  font-weight: 600;
  color: color-mix(in srgb, var(--ink) 70%, transparent);
}
.number-block .nb-art { position: absolute; inset: 0; z-index: 0; }


/* =====================================================================
   SECTION 4, PROOF. Policy 3.1 section 5. Anchor Z-RIGHT.
   Everything trust shaped on the whole homepage lives here and only here
   (Policy 3.2, register decision D-04).
   Maximum 3 testimonials (Policy 3.5).
   ===================================================================== */
.reviews-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap-card);
  align-items: stretch;
}
.review {
  background: var(--card);
  border: var(--hairline);
  border-radius: var(--radius);
  padding: var(--pad-card);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* AD-1 featured span on the ANCHOR side, Z-RIGHT: columns 2 to 3, and it
   runs both rows so the two plain cards stack on the left. */
.review.featured {
  grid-column: 2 / -1;
  grid-row: 1 / span 2;
  background: var(--client-primary-soft);
  border-color: color-mix(in srgb, var(--accent) 25%, transparent);
  justify-content: center;
}
.review blockquote { margin: 0; font-size: var(--t-lede); line-height: 1.5; }
/* Connor 2026-08-11: all three review quotes run identical type; the
   featured card keeps its size and brightness, not a serif upgrade. */
.review figcaption {
  font-size: var(--t-caption);
  color: color-mix(in srgb, var(--ink) 60%, transparent);
  display: flex; align-items: center; gap: 10px;
}
/* The monogram chip. What a testimonial card gets when there is no
   parent headshot: real initials, never a stock face, never an AI face
   (02 section 5.2). */
.mono {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex: 0 0 40px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
  font-family: var(--display); font-weight: 700; font-size: .95rem;
}

/* Track record numbers. Studio facts, not family facts. */
.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gap-card); }
.stats-grid > div {
  background: var(--card);
  border: var(--hairline);
  border-radius: var(--radius);
  padding: var(--pad-card);
}
.stat-num { font-family: var(--display); font-size: clamp(2rem, 3vw, 2.6rem); line-height: 1; font-weight: 700; color: var(--accent); }
.stat-label { display: block; margin-top: .4rem; font-size: var(--t-caption); color: color-mix(in srgb, var(--ink) 60%, transparent); }

/* Faculty. No headshots means a name chip, never a stand in face (L1-08). */
.team-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap-card); }
.person { background: var(--card); border: var(--hairline); border-radius: var(--radius); padding: var(--pad-card); }
/* Faculty card titles are NOT h4: 02 section 8.4 restricts h4 on this
   page to the Product card grid, and the page's heading census is a
   checked number. Same type role, no heading level. */
.person-name { font: 600 1.0625rem/1.35 var(--display); letter-spacing: -0.01em; margin: 0 0 .4em; }
.chip-row { display: flex; flex-wrap: wrap; gap: 10px; }

/* Trust row. Rating and member bodies, bottom of the section, grayscale. */
.trust-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: clamp(16px, 2.5vw, 40px);
  padding-top: 24px; margin-top: 28px; border-top: var(--hairline);
}
.trust-row img { filter: grayscale(1); opacity: .7; }


/* =====================================================================
   SECTION 5, PROCESS. Policy 3.1 section 6. Anchor Z-LEFT, the numeral
   rail. EXACTLY 3 STEPS (SOP 3.6). Never 4.
   HANDOFF IN: H2 LEAD, the oversized numeral starts inside Proof's
   bottom padding (design law C3).
   ===================================================================== */
.process { position: relative; }
.process-numeral {
  position: absolute;
  left: 0; top: -0.42em;
  z-index: 0;
  font-family: var(--display);
  font-size: clamp(180px, 14vw, 240px);
  line-height: .8;
  font-weight: 700;
  color: color-mix(in srgb, var(--accent) 12%, transparent);
  pointer-events: none;
  user-select: none;
}

.steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);   /* EXACTLY 3. Never repeat(4,1fr). */
  gap: var(--gap-card);
  counter-reset: step;
  margin-block: clamp(28px, 3vw, 44px);
}
.step {
  position: relative;
  counter-increment: step;
  background: var(--card);
  border: var(--hairline);
  border-radius: var(--radius);
  padding: var(--pad-card);
  padding-top: calc(var(--pad-card) + 18px);
}
.step::before {
  content: counter(step);
  position: absolute;
  top: calc(var(--pad-card) - 4px);
  left: var(--pad-card);
  font-family: var(--display);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1;
  color: color-mix(in srgb, var(--ink) 40%, transparent);
}
/* AD-3 asymmetry device: step 1 carries the accent fill, steps 2 and 3
   are plain, and step 1 sits on the anchor side (design law B2 row 5). */
.step.is-accent { background: var(--client-primary-soft); border-color: color-mix(in srgb, var(--accent) 25%, transparent); }
.step.is-accent::before { color: var(--accent); }
.step h3 { margin-bottom: .35em; }
.step p { color: color-mix(in srgb, var(--ink) 70%, transparent); font-size: var(--t-small); }

/* HANDOFF OUT: H1 POINT. Step 3's chevron aims down at the TERMINAL
   button (design law C3 and 02 section 3.2). */
.point-down {
  display: block;
  width: 34px; height: 34px;
  margin-top: 14px;
  color: var(--accent);
}


/* =====================================================================
   SECTION 6, FINAL CTA. Policy 3.1 section 7, Policy 3.3.
   Zone TERMINAL. The ONLY permitted symmetric section (design law B1),
   because the eye has nowhere left to go and centring concentrates
   attention on the single button.
   Identical label and destination to the hero CTA. The BAND changes,
   the button never does (resolution of P-23).
   ===================================================================== */
.cta-final {
  position: relative;
  padding-block: var(--pad-band);
  background: var(--client-terminal);   /* CTA fill on this band = 7.2:1 */
  text-align: center;
}
.cta-final .container { display: grid; justify-items: center; gap: 18px; }
.cta-final h2 { max-width: 20ch; margin-bottom: 0; }
.cta-final .friction { font-size: var(--t-lede); color: color-mix(in srgb, var(--ink) 70%, transparent); max-width: 46ch; margin: 0; }
.cta-final .cta-btn { min-height: 58px; font-size: 1.1rem; padding-inline: 40px; }


/* =====================================================================
   FOOTER, 04 section 6
   ===================================================================== */
.footer {
  background: var(--client-band-dark);
  color: #fff;
  padding-block: clamp(56px, 6vw, 80px) 32px;
}
.foot-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: clamp(28px, 3.4vw, 56px);
}
.foot-h {
  font-family: var(--display);
  font-size: var(--t-caption);
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: color-mix(in srgb, #fff 65%, transparent);
  margin: 0 0 14px;
}
.footer ul li { margin-bottom: 4px; }
.footer a {
  display: inline-flex; align-items: center;
  min-height: 36px;
  font-size: var(--t-small);
  color: color-mix(in srgb, #fff 82%, transparent);
  text-decoration: none;
}
.footer a:hover { color: #fff; text-decoration: underline; }

/* The NAP block. Character for character identical to the Google
   Business Profile, the contact page, and the LocalBusiness schema.
   Run the four way diff at QA (04 section 6.2). */
.nap-name { font-family: var(--display); font-weight: 700; font-size: 1.15rem; margin: 0 0 10px; }
.nap address { font-style: normal; font-size: var(--t-small); line-height: 1.7; color: color-mix(in srgb, #fff 82%, transparent); }
.nap .hours { font-size: var(--t-small); color: color-mix(in srgb, #fff 65%, transparent); }
.nap .social a { margin-right: 12px; }

/* The house signature close, 2026-08-20 (Connor): every Sites at Scale dance
   build ends on the client's wordmark, enormous and cropped by the page edge
   — Stage One's script letters, Encore's giant "Encore". Elevate's is the
   eleVate mark at ~5x its nav size, ghosted to 14% so it reads as texture,
   with roughly the bottom fifth bled off (Connor 2026-08-20: the deeper crop hid the word). The negative margin eats the footer's own
   32px bottom padding so the crop lands on the true page edge, not above a
   strip of plum. aria-hidden + empty alt: decorative, the real logo already
   opens the footer. */
.foot-wordmark {
  overflow: hidden;
  margin-top: clamp(40px, 5vw, 72px);
  margin-bottom: -32px;
  height: clamp(120px, 24vw, 368px);
}
.foot-wordmark img {
  display: block;
  width: min(78vw, 980px);
  height: auto;
  margin-inline: auto;
  opacity: .17;
}

.foot-note {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px;
  margin-top: clamp(36px, 4vw, 56px);
  padding-top: 20px;
  border-top: 1px solid color-mix(in srgb, #fff 22%, transparent);
  font-size: var(--t-caption);
  color: color-mix(in srgb, #fff 65%, transparent);
}


/* =====================================================================
   THE STICKY MOBILE CALL AND TEXT BAR, 04 section 5.5
   Mobile only. Appears after 55 pct of one viewport height of scroll
   (design law B3). Hides when .cta-final is in view, otherwise it covers
   the page's whole ending.
   ===================================================================== */
.callbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 60;
  display: none;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  padding-bottom: env(safe-area-inset-bottom);
  background: color-mix(in srgb, var(--ink) 12%, transparent);
  transform: translate3d(0, 110%, 0);
  transition: transform var(--dur-ui) var(--ease);
}
.callbar.is-visible { transform: none; }
.callbar.is-hidden  { transform: translate3d(0, 110%, 0); }
.callbar a {
  display: flex; align-items: center; justify-content: center;
  min-height: var(--tap-mobile);
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--t-small);
  text-decoration: none;
}
.callbar .cb-call { background: var(--card); color: var(--ink); }
.callbar .cb-text { background: var(--accent); color: #fff; }


/* =====================================================================
   INNER PAGE COMPONENTS
   These are in this file so the same stylesheet serves the plug and play
   pages (04 section 7). The homepage does not use all of them.
   Coach Cav, 2026-07-28: "your home page should be a wow moment, and
   then all your inner pages should just be simple structure, the same
   for everybody."
   ===================================================================== */

/* The fixed inner page opening block. Text Z-LEFT, media Z-RIGHT.
   Always. Never flipped: it is beat 0 and it does not alternate. */
.page-hero {
  display: grid;
  grid-template-columns: 1fr minmax(0, 44%);
  gap: var(--gap-split);
  align-items: center;
  padding-block: 150px 70px;
}
.page-hero-media { aspect-ratio: 16 / 11; border-radius: var(--radius); overflow: hidden; background: var(--client-primary-soft); }

/* The in page anchor rail on hub pages. */
.hub-jumps { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
.hub-jumps a {
  display: inline-flex; align-items: center;
  min-height: var(--tap);
  padding: 8px 16px;
  border: var(--hairline);
  border-radius: 999px;
  font-size: var(--t-small); font-weight: 600; text-decoration: none;
}

.info-cards  { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap-card); }
/* About #promise runs FIVE cards in the 3-col grid, which left the last two
   stuck left beside an empty cell (Connor, 2026-08-20: center them). Six
   tracks, every card spans two: row one lands 3-up exactly as before, and
   cards 4 and 5 start at tracks 2 and 4, centering the bottom row. At the
   1479 breakpoint the grid goes 2-up and these overrides reset. */
#promise .info-cards { grid-template-columns: repeat(6, 1fr); }
#promise .info-card { grid-column: span 2; }
#promise .info-card:nth-child(4) { grid-column: 2 / span 2; }
#promise .info-card:nth-child(5) { grid-column: 4 / span 2; }
.info-strip  { display: flex; flex-wrap: wrap; gap: clamp(16px, 2.4vw, 40px); padding: var(--pad-card); background: var(--card); border: var(--hairline); border-radius: var(--radius); }

/* The age band ladder. */
.ladder { display: grid; gap: var(--gap-card); }
.rung { display: grid; grid-template-columns: 140px 1fr minmax(0, 30%); gap: var(--gap-card); align-items: center; padding: var(--pad-card); background: var(--card); border: var(--hairline); border-radius: var(--radius); }
.rung-age { font-family: var(--display); font-size: 1.6rem; font-weight: 700; color: var(--accent); }
.rung-img { aspect-ratio: 3 / 2; border-radius: 10px; overflow: hidden; background: var(--client-primary-soft); }

.styles-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gap-card); }
.style-card  { display: block; padding: var(--pad-card); background: var(--card); border: var(--hairline); border-radius: var(--radius); text-decoration: none; }

/* The schedule table and its horizontal scroll wrapper. Wide content
   scrolls inside its own container so the page body never scrolls
   sideways. */
.sched-wrap { overflow-x: auto; border: var(--hairline); border-radius: var(--radius); }
.sched { width: 100%; border-collapse: collapse; font-size: var(--t-small); min-width: 640px; }
.sched th, .sched td { text-align: left; padding: 12px 16px; border-bottom: var(--hairline); }
.sched th { font-family: var(--display); font-weight: 700; background: var(--paper-tint); }

.tiers { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap-card); }
.tier { padding: var(--pad-card); background: var(--card); border: var(--hairline); border-radius: var(--radius); }
.price-note { font-size: var(--t-caption); color: color-mix(in srgb, var(--ink) 60%, transparent); }

/* The registration window pill. BELOW the H1, never above it: above the
   fold it would be a fourth element and Policy 3.2 permits three. */
.season-pill {
  display: inline-flex; align-items: center; gap: .5em;
  padding: .5em 1em; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  font-size: var(--t-caption); font-weight: 700;
}

.timeline { display: grid; gap: 0; border-left: 3px solid color-mix(in srgb, var(--accent) 30%, transparent); padding-left: 22px; }
.timeline > li { padding-block: 14px; border-bottom: var(--hairline); }

.faq > h3 { margin-top: 1.6em; }
.dress-grid, .xtras-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap-card); }
.dress-card, .xtra-card { padding: var(--pad-card); background: var(--card); border: var(--hairline); border-radius: var(--radius); }

/* The registration portal handoff card and the lazy iframe wrapper. */
.portal-handoff { padding: var(--pad-card); background: var(--paper-tint); border: var(--hairline); border-radius: var(--radius); }
.reg-embed { position: relative; aspect-ratio: 4 / 3; border: var(--hairline); border-radius: var(--radius); overflow: hidden; }
.reg-embed iframe { width: 100%; height: 100%; border: 0; }

/* The 5 field lead form. Five fields maximum (06 section 8.2). */
.form-lead { display: grid; gap: 14px; }
.form-lead label { font-size: var(--t-small); font-weight: 600; }
.form-lead input, .form-lead select {
  width: 100%; min-height: var(--tap);
  padding: 12px 14px;
  font: inherit; font-size: var(--t-body);
  color: var(--ink);
  background: var(--card);
  border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
  border-radius: 10px;
}

/* Click to play film facade: local poster, player injected on click.
   Real footage, zero load cost. */
.film-slot, .clip-slot { position: relative; aspect-ratio: 16 / 9; border-radius: var(--radius); overflow: hidden; background: var(--client-primary-soft); }
.yt-facade { position: absolute; inset: 0; border: 0; padding: 0; cursor: pointer; background: transparent; }
.yt-facade::after {
  content: "";
  position: absolute; left: 50%; top: 50%;
  width: 72px; height: 72px;
  transform: translate(-50%, -50%);
  border-radius: 999px;
  background: var(--accent);
  clip-path: polygon(38% 28%, 38% 72%, 72% 50%);
}


/* =====================================================================
   MOTION, design law E1 and E4.
   ONE signature move: the DROP-THROUGH. Nothing else on the page
   animates on scroll. One move repeated is a signature. Five moves is
   noise, and noise is what makes a site feel cheap.
   transform and opacity only, GPU composited. No property that triggers
   layout, because Policy 3.6 requires 0 CLS.
   ===================================================================== */

/* PART B, the content reveal. BELOW THE FOLD ONLY. */
.reveal {
  opacity: 0;
  transform: translate3d(0, 24px, 0);   /* never more than 32px */
  transition: opacity var(--dur-reveal) var(--ease),
              transform var(--dur-reveal) var(--ease);
  transition-delay: var(--d, 0s);
  will-change: transform, opacity;
}
.reveal.in { opacity: 1; transform: none; }

/* The media panel enters from the side it belongs to. That IS the
   visual handoff, not decoration. */
.reveal-media            { transition-duration: var(--dur-media); }
.reveal-media.from-right { transform: translate3d(20px, 24px, 0); }
.reveal-media.from-left  { transform: translate3d(-20px, 24px, 0); }

/* Stagger: 80ms per child, CAPPED AT 4 STEPS. Children 5 and later share
   child 4's delay of 320ms. The cap exists because Coach Cav's complaint
   was "it's just too late", and an uncapped stagger on a 7 item grid puts
   the last item 560ms behind. Done in CSS so no inline styles are needed. */
.reveal-group > .reveal:nth-child(1) { --d: 0s; }
.reveal-group > .reveal:nth-child(2) { --d: .08s; }
.reveal-group > .reveal:nth-child(3) { --d: .16s; }
.reveal-group > .reveal:nth-child(4) { --d: .24s; }
.reveal-group > .reveal:nth-child(n+5) { --d: .32s; }

/* PART A, the depth element. One oversized brand glyph per below-fold
   section, on the section's OFF ANCHOR side, never inside DEAD CENTER,
   zero in the hero. Travel -40px to +40px, rotation -6deg to +6deg
   across the section's traverse of the viewport (design law E1). */
.depth-glyph {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  width: clamp(200px, 32vw, 520px);
  opacity: var(--glyph-op);
  color: var(--accent);
  will-change: transform;
  transform: translate3d(0, 0, 0) rotate(0deg);
}
.depth-glyph svg { width: 100%; height: auto; }
.depth-glyph.off-left  { left: -4%;  top: 12%; }
.depth-glyph.off-right { right: -4%; top: 12%; }

/* The travel is driven by a scroll progress timeline rather than a
   scroll listener, so main.js stays at three jobs and the work runs on
   the compositor. Browsers without support render the glyph static,
   which is a correct fallback: the glyph is decoration, not content. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .depth-glyph {
      animation: glyph-drop linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
  }
}
@keyframes glyph-drop {
  from { transform: translate3d(0, -40px, 0) rotate(-6deg); }
  to   { transform: translate3d(0,  40px, 0) rotate(6deg); }
}

/* THE FINAL CTA SIGNATURE REVEAL, design law E4. Policy 3.3 requires
   "a signature reveal animation on scroll" and does not define it. It is
   defined once, here, and every dance build does exactly this.
   920ms total: H2 at 0ms, friction reducers at 80ms, button at 200ms,
   one settle at 620ms. The settle runs EXACTLY ONCE and never loops: a
   looping pulse reads as a discount banner and cheapens the page. */
.cta-final h2,
.cta-final .friction {
  opacity: 0;
  transform: translate3d(0, 20px, 0);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
}
.cta-final .friction { transition-delay: .08s; }
.cta-final .cta-btn {
  opacity: 0;
  transform: scale(.94);
  transform-origin: center;    /* the layout box never changes, CLS stays 0 */
  transition: opacity .42s var(--ease) .2s,
              transform .42s var(--ease) .2s;
}
.cta-final.in h2,
.cta-final.in .friction { opacity: 1; transform: none; }
.cta-final.in .cta-btn {
  opacity: 1;
  transform: scale(1);
  animation: cta-settle .3s ease-in-out .62s 1;
}
@keyframes cta-settle { 50% { transform: scale(1.03); } }


/* =====================================================================
   PREFERS-REDUCED-MOTION. Non negotiable, design law E3.
   Everything no-ops. If the page stops making sense without motion, the
   motion was doing the layout's job.
   The JS guard matches this: the observer is NEVER ATTACHED when reduced
   motion is set, because attaching it and no-oping inside costs frames.
   ===================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .reveal, .reveal-media {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .depth-glyph { transform: none !important; animation: none !important; }
  .video-hero video { display: none; }   /* the poster carries the composition */

  .cta-final h2,
  .cta-final .friction,
  .cta-final .cta-btn {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }

  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}


/* =====================================================================
   TABLET, 1024 and below
   ===================================================================== */
@media (max-width: 1024px) {
  .stats-grid, .styles-grid { grid-template-columns: repeat(2, 1fr); }
  .team-grid, .info-cards, .tiers, .dress-grid, .xtras-grid { grid-template-columns: repeat(2, 1fr); }
  #promise .info-cards { grid-template-columns: repeat(2, 1fr); }
  #promise .info-card, #promise .info-card:nth-child(4), #promise .info-card:nth-child(5) { grid-column: auto; }
  .hero-mark { width: min(72vw, 620px); right: -14%; }
}


/* =====================================================================
   MOBILE, 768 and below. Design law B3.
   The Z stops existing. Every split and grid collapses to one column.
   The zigzag is replaced by two devices:
     Device 1, the LEADING ELEMENT alternates picture, words, picture,
     words down the page.
     Device 2, vertical rhythm is the handoff, plus a persistent 3px
     accent rail so the eye has a vertical line to run down.
   ===================================================================== */
@media (max-width: 768px) {
  :root { --nav-h: var(--nav-h-sm); }

  .nav-links, .nav-phone { display: none; }
  .menu-btn { display: inline-flex; }
  .nav-inner { gap: 10px; }
  .nav-logo { margin-right: auto; }
  .nav-cta { padding: 10px 14px; font-size: .84rem; }

  /* Hero stacks: media, then H1, then the CTA last in its section
     (SOP 3.1 and design law A3). */
  /* Hero at 390 x 844: the mark leads, then the H1, then the CTA full
     width and last in its section (02 section 2.5 mobile wireframe). */
  .hero-inner { padding-block: 20px 44px; grid-template-rows: auto auto; }
  .hero-copy { max-width: none; padding-top: calc(56vw + 26px); }
  .hero-cta-zone { grid-row: 2; margin-top: 34px; }
  .hero-cta-zone .btn { width: 100%; min-height: var(--tap-mobile); }
  .hero-mark { width: 52vw; right: auto; left: 50%; transform: translateX(-50%); top: 14px; }

  .split, .split.rev, .page-hero {
    grid-template-columns: 1fr;
    gap: 28px;
  }
  .split.rev > .split-copy,
  .split.rev > .split-img,
  .split.rev > .split-media { order: 0; }

  /* Device 1, the alternating leading element. In a single column the
     only thing the eye can be handed is "what comes first", so it goes
     picture, words, picture, words down the page (design law B3).
     Homepage: Product media, Promise words, Proof media, Process words. */
  .lead-media > .split-img,
  .lead-media > .split-media { order: -1; }
  .lead-words > .split-copy  { order: -1; }
  .reviews-grid.lead-media > .featured { order: -1; }

  .grid-2, .grid-3, .grid-4,
  .reviews-grid, .stats-grid, .steps, .team-grid,
  .info-cards, .styles-grid, .tiers, .dress-grid, .xtras-grid,
  .foot-grid, .stats {
    grid-template-columns: 1fr;
  }
  .grid-3 > .featured,
  .review.featured { grid-column: auto; grid-row: auto; }
  .rung { grid-template-columns: 1fr; }

  /* Device 2, the persistent left edge. Without it a single column at
     390px reads as an undifferentiated scroll and the visitor bails. */
  .section > .container { border-left: 3px solid var(--accent); padding-left: 16px; }
  .cta-final .container { border-left: 0; padding-left: 0; }

  /* Mobile gaps carry meaning (design law B3, device 2):
     12px inside a unit, 20px between units, 40px across a declared
     handoff, 64px only at a hard break before TERMINAL. */
  .sec-head { margin-bottom: 20px; }
  .split-img.leads { margin-bottom: -24px; }

  /* Every CTA is full width, at least 52px tall, and LAST in its
     section. There is no right side to park it on. */
  .cta-final .cta-btn, .page-hero .btn { width: 100%; min-height: var(--tap-mobile); }

  .promise { padding-bottom: calc(var(--pad-section) - 40px); }
  .process-numeral { font-size: 140px; top: -0.3em; opacity: .8; }

  .callbar { display: grid; }
  body { padding-bottom: 0; }

  .foot-note { flex-direction: column; }
}

@media (max-width: 520px) {
  .entry-row { flex-direction: column; align-items: flex-start; gap: 0; }
  .trust-row { flex-direction: column; align-items: flex-start; }
}


/* =====================================================================
   =====================================================================
   ABOUT PAGE COMPONENTS, added for about.html
   03-about-template.md, treatment T-1 THE STORY.
   ---------------------------------------------------------------------
   WHY THIS BLOCK IS HERE AND NOT AT THE VERY END OF THE FILE

/* =====================================================================
   ELEVATE BUILD ADDITIONS. End of shippable stylesheet per the
   reference-build README section 8 note: additions live here, above
   nothing (demo chrome was stripped). Three template completions the
   reference build did not need, plus one Policy s5 exception:
     1. Variant B photo hero, split-editorial arrangement (BRAND.md
        "Hero variant selection"): copy on paper, one composed
        three-class panel bleeding off the right edge.
     2. Photo tops for .svc-card (client feedback 868kchz7p asked for
        class photos on the age cards).
     3. The Proof testimonial carousel, explicit written direction from
        the Policy Owner (Connor, build brief 2026-07-29): slow
        continuous drift, center card bright, flanks dimmed. Static
        3-card grid under prefers-reduced-motion and without JS.
     4. Text wordmark (logo SVGs pending in GAPS.md).
   ===================================================================== */

/* ---- 1. H-1 THE IMMERSIVE FRAME (standards/01-HERO-SYSTEM.md) -------
   Replaces the split-editorial hero, which reproduced the rejected v1
   composition: type in a left column beside a contained photo on a pale
   page. Quality Standard Q1 requires the fold to commit to one field
   with the type set ON it. ------------------------------------------ */
.hero.hero-immersive {
  position: relative;
  display: grid;
  min-height: min(100svh, 880px);
  background: var(--client-band-dark);
  overflow: clip;
  align-items: end;              /* the type block sits low in the field */
}
.hero-immersive .hero-media {
  position: absolute;
  inset: 0;
  margin: 0;
  z-index: 0;
}
.hero-immersive .hero-media img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 54% 34%;
  display: block;
}
/* D5 scrim: directional, built from the client's own ink, never #000,
   never flat, never past 0.90. Anchored to the type side. */
.hero-immersive .hero-scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  /* Standard 01 section 5: directional, client ink, never #000, no stop above 0.90.
     The 0.94 stop the v1 build carried was over that ceiling and is corrected here.
     Tuned for a high-key frame: the shelf is the bottom third, the faces are left
     alone, so the blush tulle keeps its colour instead of going grey. */
  background:
    linear-gradient(0deg,
      rgba(20, 9, 24, 0.90) 0%,
      rgba(20, 9, 24, 0.80) 19%,
      rgba(20, 9, 24, 0.60) 34%,
      rgba(20, 9, 24, 0.12) 52%,
      rgba(20, 9, 24, 0.00) 70%),
    linear-gradient(180deg,
      rgba(20, 9, 24, 0.62) 0%,
      rgba(20, 9, 24, 0.34) 20%,
      rgba(20, 9, 24, 0.08) 34%,
      rgba(20, 9, 24, 0.00) 44%);
}
.hero-immersive .hero-inner {
  position: relative; z-index: 2;
  width: var(--container);
  margin-inline: auto;
  padding-block: clamp(56px, 12vh, 120px) clamp(44px, 7vh, 82px);
}
.hero-immersive .hero-copy {
  max-width: 60rem;
  margin-inline: auto;
  text-align: center;
}

/* Q2: the H1 as a designed object. One deliberate move, a scale break
   across the client-approved sentence, which ships verbatim. */
.hero-immersive h1 {
  margin: 0;
  color: #fff;
  font-family: var(--client-display);
  text-wrap: balance;
}
.h1-lead {
  display: block;
  font-family: var(--client-body);
  font-size: clamp(0.78rem, 1.05vw, 0.95rem);
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.78);
  margin-bottom: clamp(14px, 1.8vh, 22px);
}
.h1-hero {
  display: block;
  font-size: clamp(3.6rem, 7.4vw, 6.6rem);   /* 57.6px to 105.6px, floor is 72px at 1440 */
  line-height: 0.94;
  letter-spacing: -0.025em;
  font-weight: 700;
}
.h1-tail {
  display: block;
  margin-top: clamp(8px, 1.2vh, 16px);
  font-size: clamp(1.5rem, 2.5vw, 2.2rem);
  line-height: 1.16;
  letter-spacing: -0.012em;
  font-weight: 600;
  font-style: italic;
  color: rgba(255, 255, 255, 0.92);
}
.hero-immersive .hero-sub {
  max-width: 40rem;
  margin-inline: auto;
  margin-top: clamp(18px, 2.6vh, 30px);
  font-size: var(--t-lede);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.82);
}
/* HERO-ANCHOR-BR: the CTA holds the bottom right, where F-BAR 2
   terminates, while the headline holds the bottom middle. */
.hero-immersive .hero-cta-zone {
  display: flex;
  justify-content: flex-end;
  margin-top: clamp(22px, 3vh, 34px);
}
.hero-immersive .hero-cta-zone .btn { min-height: 58px; font-size: 1.05rem; }

/* Scroll cue, the sixth permitted fold element (departure D-1). */
.hero-cue {
  position: absolute; z-index: 2;
  left: 50%; bottom: 26px;
  transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  font-family: var(--client-body);
  font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.22em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
  text-decoration: none;
}
.hero-cue svg { width: 18px; height: 18px; }
@media (prefers-reduced-motion: no-preference) {
  .hero-cue svg { animation: cue-drift 2.4s ease-in-out infinite; }
}
@keyframes cue-drift {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(5px); }
}

@media (max-width: 768px) {
  /* Same full-field model as desktop, not a stacked band: the image
     fills the viewport, the type sits in the lower third over a
     bottom-weighted scrim. This is what keeps the H1 and the CTA above
     the fold at 390x844 (Policy 3.6, measured with measure.html). */
  .hero.hero-immersive {
    min-height: 100svh;
    align-items: end;
    margin-top: calc(-1 * var(--nav-h));
  }
  .hero-immersive .hero-media { position: absolute; inset: 0; aspect-ratio: auto; }
  .hero-immersive .hero-media img { object-position: 62% 42%; }
  .hero-immersive .hero-scrim {
    background: linear-gradient(0deg,
      rgba(20, 9, 24, 0.96) 0%,
      rgba(20, 9, 24, 0.90) 36%,
      rgba(20, 9, 24, 0.52) 60%,
      rgba(20, 9, 24, 0.18) 78%,
      rgba(20, 9, 24, 0.34) 100%);
  }
  .hero-immersive .hero-inner {
    padding-top: var(--nav-h);
    padding-bottom: 34px;
  }
  .hero-immersive .hero-copy { max-width: none; text-align: center; }
  .h1-lead { font-size: 0.72rem; letter-spacing: 0.24em; margin-bottom: 10px; }
  .h1-hero { font-size: clamp(2.9rem, 13.5vw, 3.7rem); }
  .h1-tail { font-size: 1.28rem; margin-top: 6px; }
  .hero-immersive .hero-sub { margin-top: 14px; font-size: 0.95rem; }
  .hero-immersive .hero-cta-zone { margin-top: 20px; }
  .hero-immersive .hero-cta-zone .btn { width: 100%; }
  .hero-cue { display: none; }
}

/* ---- 2. Photo tops for service cards ------------------------------ */
.svc-card .svc-img {
  aspect-ratio: 4 / 5;
  border-radius: calc(var(--radius) - 6px);
  overflow: hidden;
  margin-bottom: 14px;
  background: var(--client-primary-soft);
}
.svc-card.featured .svc-img { aspect-ratio: 16 / 11; }
.svc-card .svc-img img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- 3. The Proof carousel (Policy s5 exception, see BRAND.md) ---- */
.reviews-carousel { position: relative; }
.car-viewport {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
}
.car-track {
  display: flex;
  gap: var(--gap-card);
  width: max-content;
  will-change: transform;
}
.car-track .review {
  flex: 0 0 400px;
  transition: none;             /* per-frame styling comes from JS */
}
@media (max-width: 768px) { .car-track .review { flex-basis: 300px; } }
/* Fallback and reduced motion: a static 3-up grid, duplicates hidden.
   .carousel-on is added by carousel.js only when motion is allowed. */
.reviews-carousel:not(.carousel-on) .car-viewport { mask-image: none; -webkit-mask-image: none; }
.reviews-carousel:not(.carousel-on) .car-track {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  width: auto;
}
.reviews-carousel:not(.carousel-on) .review { flex: none; }
.reviews-carousel:not(.carousel-on) .review.car-dup { display: none; }
@media (max-width: 768px) {
  .reviews-carousel:not(.carousel-on) .car-track { grid-template-columns: 1fr; }
}

/* ---- 4. Text wordmark --------------------------------------------- */
.nav-logo { display: inline-flex; flex-direction: column; line-height: 1; text-decoration: none; }
.logo-word {
  font-family: var(--client-display);
  font-style: italic;
  font-weight: 600;
  font-size: 1.55rem;
  letter-spacing: -0.01em;
  color: var(--client-primary-ink);
}
.logo-sub {
  margin-top: 3px;
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--ink) 60%, transparent);
}
.footer .logo-word { color: #fff; }
.footer .logo-sub { color: color-mix(in srgb, #fff 65%, transparent); }

/* =====================================================================
   =====================================================================
   ABOUT PAGE COMPONENTS, added for about.html
   03-about-template.md, treatment T-1 THE STORY.
   ---------------------------------------------------------------------
   WHY THIS BLOCK IS HERE AND NOT AT THE VERY END OF THE FILE
   The DEMO CHROME banner below carries a standing instruction: "delete
   everything below this banner for a client build." Appending shippable
   components after it would put real components inside the delete range.
   So the end of the SHIPPABLE stylesheet is here, and demo chrome stays
   last. Anything you add later goes in this block, not below.

   These are the only components the About page needs that the homepage
   did not. Everything else on about.html reuses the vocabulary above:
   .nav, .hero, .section, .split, .sec-head, .eyebrow, .answer, .grid-3,
   .svc-card, .steps, .step, .person, .mono, .cta-final, .footer,
   .callbar, .reveal, .reveal-media, .depth-glyph, .overlaps-up, .slot.
   Nothing above this banner was edited.
   ===================================================================== */

/* --- Breadcrumb ------------------------------------------------------
   Policy 3.4, adopted: schema must mirror visible text, so a page that
   emits BreadcrumbList renders a breadcrumb a person can see and click.
   It sits at the TOP OF THE FIRST SECTION, never in the hero: above the
   fold it would be a fourth element and Policy 3.2 permits three. */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 22px;
  font-size: var(--t-caption);
  color: color-mix(in srgb, var(--ink) 60%, transparent);
}
.crumbs li { max-width: none; }
.crumbs a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;      /* chrome, not a conversion action, so not 44px */
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
}
.crumbs a:hover { color: var(--ink); }
.crumbs [aria-current="page"] { color: color-mix(in srgb, var(--ink) 80%, transparent); }

/* --- Header alignment on a Z-LEFT section ----------------------------
   Design law B1, AD-2: the header stack is aligned TO THE ANCHOR SIDE so
   it visibly does not fill the container. On a Z-LEFT section that is
   the left, which is what 03 section 5.2's T-1 wireframe draws for
   MISSION, VALUES and PROCESS. The [data-anchor="z-left"] .sec-head
   utility above used to align it the other way. That has been fixed at
   source, so no page-level opt-out is needed any more. */
/* RETIRED. This class existed only to opt out of the backwards AD-2
   utility above. That utility is fixed, so .head-left is now a no-op kept
   as a harmless alias in case a page still carries it. Do not add it to
   new pages. */
.head-left { }

/* --- The founder portrait pair, 03 section 4 -------------------------
   Authority 4/5 and empathy 3/2, side by side, in the Origin section's
   single Z anchor column. The empathy frame is offset 40px DOWN and sits
   to the right, the two overlap by 32px horizontally (inside the 24 to
   40px rule), and the overlap edge carries a 4px ring in the page
   background so two frames read as two frames.
   The offsets are STATIC MARGINS, never animated, so CLS stays 0.
   ONE caption under the pair, never two: two captions turn a
   composition into a contact sheet. */
.portrait-pair {
  display: grid;
  grid-template-columns: minmax(0, 58%) minmax(0, 42%);
  align-items: start;
}
.portrait-authority,
.portrait-empathy {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--client-primary-soft);
}
.portrait-authority { aspect-ratio: 4 / 5; }   /* 58 pct of the column */
.portrait-empathy {
  aspect-ratio: 3 / 2;                          /* 42 pct of the column */
  z-index: 1;
  margin-top: 40px;
  margin-left: -32px;
  box-shadow: 0 0 0 4px var(--paper);
}
.portrait-pair .portrait-caption {
  grid-column: 1 / -1;
  margin-top: 18px;
}

/* --- The pull quote, 03 section 3.3 ----------------------------------
   ONE per page, and it is the beat 5 sentence. It is the only element on
   the About page promoted to the 900ms media duration (03 section 5.5
   part B), entering from the section's anchor side via the existing
   .reveal-media.from-right class. No new motion is introduced. */
.pull-quote {
  margin: clamp(28px, 3vw, 40px) 0 0;
  padding-left: clamp(18px, 2vw, 28px);
  border-left: 4px solid var(--accent);
  font-family: var(--display);
  font-size: clamp(1.375rem, 2.1vw, 1.75rem);
  line-height: 1.28;
  letter-spacing: -0.015em;
  max-width: 34ch;
}
.pull-quote p { max-width: none; margin: 0; }
.pull-quote cite { display: block; margin-top: 12px; font: 600 var(--t-caption)/1.4 var(--body); font-style: normal; color: color-mix(in srgb, var(--ink) 60%, transparent); }

/* --- The mission statement line, 03 section 2.1 row 2 ----------------
   The Mission band has no media. Type does the work, so the statement
   gets a display-face size of its own, capped at 58ch. */
.statement {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.5rem, 2.6vw, 2.125rem);
  line-height: 1.24;
  letter-spacing: -0.015em;
  max-width: 58ch;
  margin: 0 0 1rem;
}

/* --- The anti-positioning line, 03 section 6.3 -----------------------
   MAGNET NOT STEEL. Last element of the Values section, a single
   labelled line, never a card. A card would make it one value among
   several, and it is not a value, it is the boundary around all of them. */
.anti {
  margin-top: clamp(24px, 2.6vw, 36px);
  padding: 16px 0 16px 18px;
  border-left: 3px solid var(--accent);
  max-width: 68ch;
}
.anti strong { font-family: var(--display); }

/* --- AD-3 on a card grid, without the AD-1 span ----------------------
   Design law B1 permits exactly ONE asymmetry device per section. The
   .featured class above carries both the accent fill AND the two column
   span, which is AD-1. About's Product section uses AD-3 instead: one
   card carries the accent, the other two are plain, and the accented
   card sits on the anchor side. So it needs the fill without the span. */
.svc-card.is-accent {
  background: var(--client-primary-soft);
  border-color: color-mix(in srgb, var(--accent) 25%, transparent);
}
.svc-card.is-accent h3 { color: var(--accent); }

/* The card media slot. 3/2 only, aspect locked so CLS is 0 before the
   photo lands, and capped at 3 per viewport slice (design law F5). */
.card-art {
  position: relative;
  aspect-ratio: 3 / 2;
  border-radius: 10px;
  overflow: hidden;
  background: var(--client-primary-soft);
  margin-bottom: 16px;
}

/* --- Leadership cards, 03 section 2.1 row 7 --------------------------
   AD-1: the founder or artistic director card spans two columns AND
   sits on the anchor side, Z-RIGHT, so it reinforces the section's one
   anchor instead of creating a second one. .grid-3 > .featured above
   already does the span. This adds the accent field. */
.person.featured {
  background: var(--client-primary-soft);
  border-color: color-mix(in srgb, var(--accent) 25%, transparent);
}

/* --- The faculty teaser, 03 section 2.1 row 7 ------------------------
   Up to 8 teachers, named, linking on to the Team page. Where there is
   no headshot it is a monogram chip with real initials, NEVER a stock
   face and never an AI face (07 rule L1-08, never-do.md rule 5). This
   reference build has no photographs at all, so every chip is a
   monogram and the whole row is marked illustrative. */
.faculty-teaser {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.fac-chip {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: var(--tap);
  padding: 6px 16px 6px 6px;
  background: var(--card);
  border: var(--hairline);
  border-radius: 999px;
  font-size: var(--t-small);
  font-weight: 600;
}
.fac-chip .mono { width: 32px; height: 32px; flex: 0 0 32px; font-size: .8rem; }

/* --- Handoff utilities the homepage did not need ---------------------
   Design law C3. Every boundary declares one of three types, and the
   sending section's padding drops by the same number the receiving
   element is pulled up by, so the net vertical rhythm is unchanged
   (design law F2).
   .overlaps-up above is -64px. About's Values to Product boundary is
   specified at 48px (03 section 2.1 row 4), so it gets a modifier
   rather than a second class. */
.overlaps-up.up-48   { margin-top: -48px; }
.overlap-out         { padding-bottom: calc(var(--pad-section) - 64px); }
.overlap-out.out-48  { padding-bottom: calc(var(--pad-section) - 48px); }
.band.overlap-out    { padding-bottom: calc(var(--pad-band) - 64px); }

/* --- Mobile, 768 and below. Design law B3 ---------------------------
   The Z stops existing. The portrait pair stacks with the 12px "these
   are one thing" gap, the negative offsets are removed because there is
   no second column to overlap into, and the overlap handoffs halve so
   they stay inside the 96px mobile boundary limit. */
@media (max-width: 768px) {
  .portrait-pair { grid-template-columns: 1fr; gap: 12px; }
  .portrait-empathy { margin-top: 0; margin-left: 0; box-shadow: none; }
  .portrait-pair .portrait-caption { margin-top: 6px; }

  /* Device 1, the alternating leading element: the Team section leads
     with the featured leader card, per 03 section 5.2's 390 wireframe. */
  .leaders.lead-media > .featured { order: -1; }

  .overlaps-up { margin-top: -32px; }
  .overlaps-up.up-48 { margin-top: -24px; }
  .overlap-out { padding-bottom: calc(var(--pad-section) - 32px); }
  .overlap-out.out-48 { padding-bottom: calc(var(--pad-section) - 24px); }
  .band.overlap-out { padding-bottom: calc(var(--pad-band) - 32px); }

  .pull-quote { max-width: none; }
}


/* Real client logo SVG in the nav (logo-1 from the brand's Logos set).
   The footer keeps the text wordmark until the white logo variant
   (logo 3 wht) is retrieved. */
.nav-logo img { display: block; height: 52px; width: auto; }
@media (max-width: 768px) { .nav-logo img { height: 44px; } }
.nap-name img { display: block; height: 60px; width: auto; }


/* =====================================================================
   FACULTY STACK. team.html only. Added 2026-07-29.

   Base state = the plain faculty list. That is what ships if JS is off,
   if prefers-reduced-motion is set, or if faculty-stack.js fails. It is
   readable, complete, and needs no JavaScript.
   `.is-enhanced` is added by the script and is the ONLY thing that turns
   on the two-column stage and the card pile.
   ===================================================================== */

/* ---- Base: the plain list. No JS required. ---------------------- */
.fac-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gap-card); }
.fac-item { background: var(--card); border: var(--hairline); border-radius: var(--radius); padding: var(--pad-card); }
.fac-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.fac-name { font: 700 1.25rem/1.25 var(--display); letter-spacing: -0.01em; margin: 0 0 .25em; }
.fac-role { font-size: var(--t-small); color: color-mix(in srgb, var(--ink) 62%, transparent); margin: 0 0 1em; }
.fac-bio p { margin: 0 0 .9em; }
.fac-bio p:last-child { margin-bottom: 0; }

/* The pile is never shown until the script says so. */
.fac-stack[hidden] { display: none; }
.fac-controls { display: none; }

/* ---- Enhanced: two columns, pile on the anchor side -------------- */
.fac-stage.is-enhanced {
  display: grid;
  /* Column 1 is wider than the pile on purpose. The cards fan up and to
     the LEFT (negative x offsets), so the column needs slack or the back
     of the pile clips off the page edge. 380 minus a 250 pile leaves
     65px each side, and the deepest visible card sits at -48px. */
  grid-template-columns: minmax(0, 380px) minmax(0, 1fr);
  gap: var(--gap-split);
  align-items: start;
}
/* Explicit placement. Without this the controls become the second grid
   item and land in column 2, next to the bio instead of under the pile. */
.fac-stage.is-enhanced .fac-stack    { grid-column: 1; grid-row: 1; }
.fac-stage.is-enhanced .fac-controls { grid-column: 1; grid-row: 2; }
.fac-stage.is-enhanced .fac-side     { grid-column: 2; grid-row: 1 / span 2; }
.fac-stage.is-enhanced .fac-list { display: block; }
/* The items are <li> moved into a plain div, so they keep their default
   marker unless we say otherwise. */
.fac-stage.is-enhanced .fac-item { border: 0; background: transparent; padding: 0; list-style: none; }
.fac-panel > .fac-item { list-style: none; }

.fac-stack {
  position: relative;
  width: 100%;
  max-width: 250px;
  aspect-ratio: 5 / 7;      /* aspect-locked: CLS stays 0 */
  margin-inline: auto;
  touch-action: pan-y;      /* let the page scroll; we only read x-drag */
  cursor: grab;
}
.fac-stack:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; border-radius: var(--radius); }

.fac-card {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--client-primary-soft, color-mix(in srgb, var(--accent) 12%, transparent));
  border: var(--hairline);
  box-shadow: 0 10px 30px -12px rgba(14, 10, 18, .28);
  display: flex; align-items: center; justify-content: center;
  transform-origin: bottom center;
  /* transform and opacity only: GPU composited, no layout, no CLS */
  transition: transform .42s cubic-bezier(.22,.61,.36,1), opacity .3s linear;
  will-change: transform;
}
.fac-card.is-dragging { transition: none; cursor: grabbing; }
.fac-card img { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }

/* Monogram fallback. Real initials on a brand-tinted field. This is the
   blessed stand-in while portraits are outstanding: never a stock face,
   never an AI face (never-do.md rule 5, brand rule L1-08). */
.fac-card-mono {
  font: 700 clamp(2.5rem, 7vw, 4rem)/1 var(--display);
  color: var(--accent);
  letter-spacing: .02em;
}

.fac-controls {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  max-width: 250px; margin: 18px auto 0;
}
.fac-nav {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--tap); height: var(--tap);       /* 44px minimum */
  min-width: 44px; min-height: 44px;
  border-radius: 999px;
  border: var(--hairline);
  background: var(--card);
  color: var(--ink);
  font-size: 1.1rem; line-height: 1;
  cursor: pointer;
}
.fac-nav:hover { border-color: var(--accent); color: var(--accent); }
.fac-nav:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fac-counter { margin: 0; font-size: var(--t-caption); color: color-mix(in srgb, var(--ink) 60%, transparent); font-variant-numeric: tabular-nums; }

/* The name rail. Real buttons, the keyboard route to every instructor. */
.fac-rail { list-style: none; margin: 0 0 22px; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.fac-rail-btn {
  display: inline-flex; align-items: center;
  min-height: var(--tap); padding: 6px 16px;
  border-radius: 999px;
  border: var(--hairline);
  background: var(--card);
  color: color-mix(in srgb, var(--ink) 78%, transparent);
  font: 600 var(--t-small)/1 var(--body);
  cursor: pointer;
}
.fac-rail-btn:hover { color: var(--ink); border-color: color-mix(in srgb, var(--ink) 30%, transparent); }
.fac-rail-btn.is-on { background: var(--accent); border-color: var(--accent); color: #fff; }
.fac-rail-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.fac-panel { min-height: 1px; }

/* ---- Mobile: pile above, bio below. Z stops existing (design law B3). */
@media (max-width: 768px) {
  .fac-stage.is-enhanced { grid-template-columns: 1fr; gap: 26px; }
  .fac-stack { max-width: 240px; }
  .fac-controls { max-width: 240px; }
  .fac-rail { gap: 6px; }
}

/* ---- Reduced motion. The script does not run, so this is belt and
       braces for anything that slips through: no transitions, no tilt. */
@media (prefers-reduced-motion: reduce) {
  .fac-card { transition: none !important; transform: none !important; }
  .fac-stack { display: none !important; }
  .fac-controls { display: none !important; }
  .fac-stage.is-enhanced { grid-template-columns: 1fr; }
}

/* ---- Nav over an immersive hero --------------------------------------
   The fold commits to one field (Q1), so a solid nav bar sitting on top
   of it breaks the field in two. Over an H-1 hero the nav is
   transparent with white content until the page scrolls, then it
   resolves to the normal paper nav. Colour transition only, no motion
   (design law E2). ---------------------------------------------------- */
.nav.nav-over:not(.scrolled) {
  background: transparent;
  border-bottom-color: transparent;
  box-shadow: none;
}
.nav.nav-over:not(.scrolled) .nav-links a,
.nav.nav-over:not(.scrolled) .nav-phone {
  color: rgba(255, 255, 255, 0.92);
}
.nav.nav-over:not(.scrolled) .nav-links a[aria-current="page"] { color: #fff; }
.nav.nav-over:not(.scrolled) .menu-btn { color: #fff; }
.nav.nav-over:not(.scrolled) .btn-primary {
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.45);
  backdrop-filter: blur(6px);
}
.nav.nav-over:not(.scrolled) .btn-primary:hover {
  background: rgba(255, 255, 255, 0.24);
}
/* Logo swap: the white mark over the image, the purple mark once solid. */
.nav-logo .logo-on-dark { display: none; }
.nav.nav-over:not(.scrolled) .nav-logo .logo-on-light { display: none; }
.nav.nav-over:not(.scrolled) .nav-logo .logo-on-dark  { display: block; }

/* The hero starts under the nav so the image runs to the top edge. */
.hero.hero-immersive { margin-top: calc(-1 * var(--nav-h)); }
.hero-immersive .hero-inner { padding-top: calc(var(--nav-h) + clamp(56px, 12vh, 120px)); }

/* ---- Committed fields beyond the fold --------------------------------
   standards/02-VISUAL-CRAFT.md section 3.1 requires at least one
   full-bleed committed field below the fold, and section 2 requires the
   page to carry depth rather than reading as one plane of cards on
   paper. The Proof section takes the dark field because it is the
   page's emotional peak, and the terminal band resolves to the same
   deep plum so the page opens and closes on the brand's dark. -------- */
.section.proof.field-dark {
  background:
    radial-gradient(120% 90% at 78% 8%, rgba(109, 39, 121, 0.55) 0%, rgba(109, 39, 121, 0) 62%),
    var(--client-band-dark);
  color: #fff;
}
.field-dark .eyebrow { color: color-mix(in srgb, var(--client-primary) 70%, #fff); }
.field-dark h2, .field-dark h3 { color: #fff; }
.field-dark .answer, .field-dark p { color: rgba(255, 255, 255, 0.84); }
.field-dark .review {
  background: rgba(255, 255, 255, 0.055);
  border: 1px solid rgba(255, 255, 255, 0.14);
  backdrop-filter: blur(2px);
}
.field-dark .review blockquote { color: rgba(255, 255, 255, 0.95); }
.field-dark .review figcaption { color: rgba(255, 255, 255, 0.66); }
.field-dark .review.featured {
  background: rgba(255, 255, 255, 0.11);
  border-color: rgba(255, 255, 255, 0.26);
}
.field-dark .review .mono {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}
.field-dark .stats-grid > div {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.12);
}
.field-dark .stat-num { color: color-mix(in srgb, var(--client-primary) 55%, #fff); }
.field-dark .stat-label { color: rgba(255, 255, 255, 0.7); }
.field-dark .trust-row { border-top-color: rgba(255, 255, 255, 0.18); }
.field-dark .text-link { color: #fff; }
.field-dark .depth-glyph { color: #fff; opacity: 0.07; }
.field-dark :focus-visible { outline-color: #fff; }

/* Terminal band: the page closes on the brand dark, not a pale tint. */
.cta-final {
  background:
    radial-gradient(90% 120% at 50% 0%, rgba(109, 39, 121, 0.62) 0%, rgba(109, 39, 121, 0) 68%),
    var(--client-band-dark);
}
.cta-final h2 { color: #fff; }
.cta-final .friction { color: rgba(255, 255, 255, 0.8); }
.cta-final :focus-visible { outline-color: #fff; }

/* ---- The action color on dark surfaces -------------------------------
   Q5 requires the CTA to be the first thing the eye lands on. On the
   dark plum field the standard fill #6D2779 measures only 1.85:1
   against the background: its label is legible but the button does not
   separate, so it loses the grayscale test.
   Derived on-dark pair, brand hue 291deg held, saturation held,
   lightness raised: fill #C474D3 with the plum ink label.
   Measured: fill vs band-dark 5.65:1, ink label on fill 5.65:1.
   Recorded under "Contrast remediations" in BRAND.md. -------------- */
.hero-immersive .btn-primary,
.field-dark .btn-primary,
.cta-final .cta-btn {
  background: #C474D3;
  color: #241329;
  border-color: #C474D3;
}
.hero-immersive .btn-primary:hover,
.field-dark .btn-primary:hover,
.cta-final .cta-btn:hover {
  background: #D291DE;
}


/* =====================================================================
   HERO H1, WORDMARK LEDE.  Directed by Connor 2026-07-30.
   "Elevate: Where Community Builds Confidence", with Elevate set as the
   studio's own wordmark. Q2's deliberate move is the logo geometry itself
   (Standard 01 section 4). The mark is drawn paths, not a webfont, so the
   page still carries exactly two families, Lora and Montserrat.
   The visible colon is dropped: the line break does that work. The colon
   stays in the accessible text so the sentence reads correctly.
   ===================================================================== */
.hero-immersive h1.h1-wordmark-lede {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(14px, 2.1vh, 26px);
}
.h1-mark { display: block; width: 100%; }
/* Dominant element in the fold. 789x342 intrinsic, so a 700px width renders
   303px tall, far above Standard 01's 72px floor for the dominant line. */
.h1-mark-svg {
  display: block;
  /* Measured, not chosen. The graded frame plus the scrim leaves exactly one band
     where white type clears 4.5:1 at 1440x900: y 615 to 860, about 250px. With the
     CTA lifted out of the vertical stack (below) the mark gets ~175px of that, so
     404px wide. Still 2.4x Standard 01's 72px floor for the dominant line. */
  width: min(23rem, 32vw);
  height: auto;
  margin-inline: auto;
  /* The mark is white artwork on a dark field; a soft plum drop keeps the
     counters open where the tulle highlights sit directly behind them. */
  filter: drop-shadow(0 2px 18px rgba(20, 9, 24, 0.72));
}
.h1-wordmark-lede .h1-tail {
  display: block;
  margin-top: 0;
  font-family: var(--client-display);
  font-size: clamp(1.35rem, 2.85vw, 2.6rem);
  font-style: normal;
  font-weight: 600;
  line-height: 1.14;
  letter-spacing: -0.014em;
  text-wrap: balance;
  color: #fff;
  text-shadow: 0 1px 14px rgba(20, 9, 24, 0.66);
}

@media (max-width: 768px) {
  .hero-immersive h1.h1-wordmark-lede { gap: 12px; }
  .h1-mark-svg { width: min(19rem, 76vw); }
  .h1-wordmark-lede .h1-tail { font-size: clamp(1.15rem, 5.6vw, 1.5rem); }
}


/* HERO-ANCHOR-BR, retuned 2026-07-30. The CTA leaves the vertical flow so the
   wordmark can own the full type-safe shelf. Same anchor, same single CTA. */
.hero-immersive .hero-inner { position: relative; padding-bottom: clamp(30px, 5vh, 58px); }
.hero-immersive .hero-cta-zone {
  /* Anchored to hero-inner, which IS the container, so the CTA's right edge lines
     up with the nav CTA above it. Viewport maths put it 144px inboard of that. */
  position: absolute;
  z-index: 2;
  right: 0;
  bottom: clamp(30px, 5vh, 58px);
  margin-top: 0;
}
.hero-immersive .hero-copy { padding-bottom: clamp(4px, 1.2vh, 14px); }
.h1-wordmark-lede .h1-tail { font-size: clamp(1.15rem, 1.95vw, 1.7rem); }

@media (max-width: 768px) {
  /* Mobile keeps the CTA in flow and full width (Standard 01 section 7). */
  .hero-immersive .hero-cta-zone {
    position: static;
    right: auto; bottom: auto;
    margin-top: 20px;
  }
  .hero-immersive .hero-cta-zone .btn { width: 100%; min-height: var(--tap-mobile); }
}


/* =====================================================================
   HERO SEQUENCE, pattern H-3.  Directed by Connor 2026-07-31.
   Frames stack in one grid cell and crossfade on opacity alone, so no
   frame ever affects layout and CLS stays 0. The scrim, the type and the
   CTA all sit above this and never move.
   ===================================================================== */
.hero-immersive .hero-media.hero-seq { display: grid; }
.hero-seq .seq-frame {
  grid-area: 1 / 1;
  display: block;
  min-width: 0;
  opacity: 0;
  /* Dissolve only, never a slide or a push (Standard 01 H-3). 700ms is paired
     to the 2500ms hold in hero-sequence.js: at the old 1400ms more than half of
     every step was spent mid-fade and the fold never settled. */
  transition: opacity 700ms ease-in-out;
  will-change: opacity;
}
.hero-seq .seq-frame.is-on { opacity: 1; }
.hero-seq .seq-frame img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 54% 34%;
  display: block;
}

/* Reduced motion: frame one only, no transition, no script (the JS returns
   early). The fold is then identical to the single-frame build. */
@media (prefers-reduced-motion: reduce) {
  .hero-seq .seq-frame { transition: none; }
  .hero-seq .seq-frame:not(.is-on) { display: none; }
}

@media (max-width: 768px) {
  .hero-seq .seq-frame img { object-position: 62% 42%; }
}


/* =====================================================================
   HERO LAYOUT v3.  Directed by Connor 2026-07-31.
   Full logo lockup at the TOP of the fold, the promise line at the BOTTOM,
   the CTA directly beneath it. Replaces the wordmark-lede block above.
   The h1 is still one element carrying the whole sentence; display:contents
   drops its two spans into this grid so the mark and the line can sit at
   opposite ends of the fold without splitting the heading.
   ===================================================================== */
.hero-immersive .hero-inner {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  /* the logo now holds the top, so the fold no longer needs the deep inset */
  padding-top: calc(var(--nav-h) + clamp(18px, 4vh, 46px));
  padding-bottom: clamp(26px, 4.5vh, 52px);
}
/* The h1 stays a real box carrying the promise line at the bottom, and pins the
   lockup to the top of the fold. `display:contents` was tried first and dropped:
   it gives the h1 no box at all, so the QA harness cannot measure the heading,
   and it has a history of dropping headings out of the accessibility tree. */
.h1-split { display: block; width: 100%; margin: 0; }

.h1-mark {
  position: absolute;
  top: calc(var(--nav-h) + clamp(18px, 4vh, 46px));
  left: 50%;
  transform: translateX(-50%);
  display: block;
}
.h1-logo {
  display: block;
  width: min(27rem, 40vw);
  height: auto;
  margin-inline: auto;
  /* white artwork on stage photography: a soft plum drop keeps the counters
     and the letterspaced tagline open where a bright star sits behind them */
  filter: drop-shadow(0 2px 20px rgba(20, 9, 24, 0.78));
}

.h1-split .h1-tail {
  display: block;
  margin: 0 0 clamp(14px, 2.2vh, 26px);
  font-family: var(--client-display);
  font-size: clamp(1.35rem, 2.6vw, 2.35rem);
  font-style: normal;
  font-weight: 600;
  line-height: 1.14;
  letter-spacing: -0.014em;
  text-align: center;
  text-wrap: balance;
  color: #fff;
  text-shadow: 0 1px 16px rgba(20, 9, 24, 0.72);
}

/* CTA returns to the flow, directly under the line, centred with it. */
.hero-immersive .hero-cta-zone {
  position: static;
  right: auto;
  bottom: auto;
  display: flex;
  justify-content: center;
  margin-top: 0;
}

/* While the fold carries the full lockup, the nav does not repeat it. The mark
   fades back in on scroll, which main.js already toggles via .scrolled. */
.nav.nav-over:not(.scrolled) .nav-logo { opacity: 0; pointer-events: none; }
.nav .nav-logo { transition: opacity 260ms ease; }

@media (max-width: 768px) {
  .hero-immersive .hero-inner {
    padding-top: calc(var(--nav-h) + 18px);
    padding-bottom: 26px;
  }
  .h1-logo { width: min(17rem, 68vw); }
  .h1-split .h1-tail { font-size: clamp(1.1rem, 5.2vw, 1.45rem); margin-bottom: 16px; }
  .hero-immersive .hero-cta-zone .btn { width: 100%; min-height: var(--tap-mobile); }
}

/* The immersive hero grid was set to `align-items: end` for the old layout,
   which shrink-wrapped .hero-inner and pushed the whole type block to the
   bottom. The v3 layout needs .hero-inner to fill the fold so its own rows can
   hold the logo at the top and the line + CTA at the bottom. */
.hero.hero-immersive { align-items: stretch; }
.hero-immersive .hero-inner { min-height: 0; }


/* The logo sits at the top of the fold over lit stage backdrop, which measured
   1.5:1 to 3.0:1 across the five frames. Standard 01 section 5 anchors the scrim
   to the type side, and the type side is now both ends, so the lockup carries its
   own local wash rather than the whole top being flattened into a grey band. It
   travels with the mark, so it stays correct at every viewport. */
/* position is set in the v3 layout block above; only isolation here, so the
   wash does not clobber the pin that holds the lockup at the top. */
.h1-mark { isolation: isolate; }
.h1-mark::before {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 165%; height: 300%;
  transform: translate(-50%, -50%);
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at center,
    rgba(20, 9, 24, 0.88) 0%,
    rgba(20, 9, 24, 0.80) 34%,
    rgba(20, 9, 24, 0.52) 56%,
    rgba(20, 9, 24, 0.18) 72%,
    rgba(20, 9, 24, 0.00) 86%);
}
.h1-logo { position: relative; z-index: 1; }


/* ---- authoritative pin for the hero lockup -------------------------------
   Declared last so no later block can reset `position` again, which is exactly
   what the wash block did and what pushed the logo down onto the promise line. */
.hero-immersive .hero-inner { position: relative; }
.hero-immersive .h1-mark {
  position: absolute;
  top: calc(var(--nav-h) + clamp(18px, 4vh, 46px));
  left: 50%;
  transform: translateX(-50%);
  width: auto;
}
@media (max-width: 768px) {
  .hero-immersive .h1-mark { top: calc(var(--nav-h) + 16px); }
}


/* =====================================================================
   THE AGE GROUPS (section 2b). Vanilla port of the supplied 21st.dev
   ImageReveal fan (React + framer-motion), the third component ported
   rather than dropped in on this build (hero panel, class sphere, now
   this): no Tailwind, no framer runtime, the client's own tokens.
   The source's two springs became two uses of one curve: entrance
   stiffness 120 / damping 12 -> cubic-bezier(.34,1.56,.64,1) at .7s,
   hover stiffness 200 / damping 15 -> the same curve at .38s.
   Entrance transforms live on .age-tile (per-tile --fd delays, keyed
   off the row's house .reveal observer state), interaction transforms
   live on .age-card (no delay), so the stagger never lags a hover.
   The class sphere this section replaces is whole in assets/retired/.
   ===================================================================== */
/* Connor 2026-08-08 vibrancy pass: the band leaves the dark plum for
   the brand's soft lilac. Measured on #F3E7F5: ink 16.4:1, ink at 74%
   7.98:1 (the 70% token measured 6.92 on this tint, under the 7:1
   body floor, so pane copy runs at 74%), primary-ink 7.92:1, ink on
   the mint chips 16.11:1, primary-ink on the white age chip 9.47:1. */
.ages-section { background: var(--client-primary-soft); color: var(--ink); overflow: clip; }
.ages-section .answer { color: color-mix(in srgb, var(--ink) 74%, transparent); }
.ages-section :focus-visible { outline-color: var(--accent); }

/* --- The fan row. Four polaroid tiles, tilted like the source's three
   (-8, 6, -6, plus a 4 for the fourth, all eased a degree for caption
   room), each pulled to the row's centre until the row's .in lands:
   the fan-out IS the entrance. z-order runs right over left, the
   OPPOSITE of the source: the source's squares carried no captions,
   ours do, and the long fourth title must never sit under a
   neighbour's corner. --- */
.ages-stack {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  margin-top: clamp(6px, 1.5vh, 18px);
  padding: 16px 0 34px;
}
.age-tile {
  --spring: cubic-bezier(.34, 1.56, .64, 1);
  position: relative;
  width: clamp(150px, 16.5vw, 214px);
  margin-inline: -8px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transform: translate(var(--fan-x, 0px), var(--fan-y, 0px)) rotate(var(--fan-r, 0deg));
  transition: opacity .35s var(--ease);
  will-change: transform;
}
.age-tile:nth-child(1) { --fan-r: -7deg;   --fan-y: 14px; --fd: .05s; --settle: 1deg;  z-index: 1; }
.age-tile:nth-child(2) { --fan-r: 5deg;    --fan-y: 0px;  --fd: .17s; --settle: 0deg;  z-index: 2; }
.age-tile:nth-child(3) { --fan-r: -5deg;   --fan-y: 20px; --fd: .29s; --settle: 2deg;  z-index: 3; }
.age-tile:nth-child(4) { --fan-r: 3.5deg;  --fan-y: 8px;  --fd: .41s; --settle: -1deg; z-index: 4; }

/* Stacked start, the source component's initial state: untilted, every
   tile at the centre of the row. translateX percentages are relative
   to the tile's own width; centres sit ~92% of a width apart at the
   -8px overlap, so 139/46 stacks all four dead centre. */
.ages-on .ages-stack.reveal:not(.in) .age-tile { --fan-r: 0deg; --fan-y: 0px; }
.ages-on .ages-stack.reveal:not(.in) .age-tile:nth-child(1) { --fan-x: 139%; }
.ages-on .ages-stack.reveal:not(.in) .age-tile:nth-child(2) { --fan-x: 46%; }
.ages-on .ages-stack.reveal:not(.in) .age-tile:nth-child(3) { --fan-x: -46%; }
.ages-on .ages-stack.reveal:not(.in) .age-tile:nth-child(4) { --fan-x: -139%; }

/* The spring rides the .in state, not the tile base, so the JS-applied
   stacking on load lands instantly and invisibly; only the fan-out
   animates, from the stack the viewer never saw assembling. */
.ages-on .ages-stack.reveal.in .age-tile {
  transition: transform .7s var(--spring) var(--fd, 0s), opacity .35s var(--ease);
}

/* The polaroid card: white frame, portrait image, caption in the deep
   border. All interactive travel happens on this element so it never
   sits behind the entrance stagger. */
.age-card {
  display: block;
  padding: 9px 9px 11px;
  background: var(--client-card);
  border-radius: var(--radius);
  box-shadow: 0 16px 38px rgba(36, 19, 41, 0.22);
  transition: transform .38s var(--spring), box-shadow .38s var(--ease);
}
.age-img {
  display: block;
  border-radius: calc(var(--radius) - 5px);
  overflow: hidden;
}
.age-img img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}
.age-cap {
  display: block;
  padding: 9px 4px 3px;
  text-align: center;
  font-family: var(--display);
  font-size: clamp(0.9rem, 0.62rem + 0.55vw, 1.04rem);
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
}
.age-cap small {
  display: block;
  margin-top: 3px;
  font-family: var(--body);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  color: color-mix(in srgb, var(--ink) 62%, transparent);
}

/* Hover, focus, pinned: the tile straightens to a degree or two of
   settle (dead straight reads mechanical), lifts, and takes the top of
   the pile; unchosen siblings step back. The source's hover spring,
   with keyboard parity. */
.ages-on .age-tile:hover .age-card,
.ages-on .age-tile:focus-visible .age-card,
.ages-on .age-tile.is-active .age-card {
  transform: rotate(calc(var(--settle, 0deg) - var(--fan-r, 0deg))) translateY(-12px) scale(1.035);
  box-shadow: 0 26px 54px rgba(36, 19, 41, 0.30);
}
.ages-on .age-tile:hover,
.ages-on .age-tile:focus-visible,
.ages-on .age-tile.is-active { z-index: 6; }
.ages-on .ages-stack.has-active .age-tile:not(.is-active):not(:hover) { opacity: 0.6; }
.age-tile:focus-visible { outline: 3px solid var(--accent); outline-offset: 5px; border-radius: var(--radius); }

/* --- The panel. One grid cell, every pane stacked in it, so the band
   never changes height when the focus moves (CLS 0). Hidden panes are
   visibility-hidden: out of the tab order and out of the live region,
   still in flow for the shared height. --- */
.ages-panel {
  display: grid;
  max-width: 640px;
  margin-inline: auto;
  text-align: center;
}
.ages-pane {
  grid-area: 1 / 1;
  /* Centred in the reserved cell (Connor 2026-08-08): the panel is as
     tall as its TALLEST pane so the band never jumps, which left the
     short default copy floating high with a void beneath it. Centring
     pulls the initial copy down into the middle of that space. */
  align-self: center;
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  transition: opacity .28s var(--ease), transform .28s var(--ease), visibility 0s linear .28s;
}
.ages-pane.is-on {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity .34s var(--ease) .05s, transform .34s var(--ease) .05s, visibility 0s;
}
/* The global h3 carries a 26ch measure, so its BOX hugged the centred
   pane's left edge and every group title sat left of centre (Connor,
   2026-08-08). The pane's blocks now centre their boxes; the type
   measures stay. */
.ages-pane h3 { max-width: none; margin: 0 auto .45em; }
.ages-pane .answer, .ages-pane .ages-copy { margin-inline: auto; }
.age-range {
  display: inline-flex;
  margin: 0 0 14px;
  padding: .38em .95em;
  border-radius: 999px;
  background: var(--client-card);
  color: var(--client-primary-ink);
  font-size: var(--t-caption);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.ages-copy {
  color: color-mix(in srgb, var(--ink) 74%, transparent);
  max-width: 52ch;
  margin: 0 auto 1em;
}
.ages-section .chip-row { justify-content: center; list-style: none; margin: 0 0 1.15em; padding: 0; }
.ages-section .chip { background: var(--client-terminal); color: var(--ink); }
.ages-link { margin: 0; }
.ages-hint {
  /* auto inline margins: the text was centred but its 68ch box was not,
     so the hint sat left of the fan it belongs under (2026-08-28). */
  margin: 14px auto 0;
  font-family: var(--body);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--ink) 62%, transparent);
}

/* age-groups.js absent or failed: every pane in flow, chips and links
   readable, the fan a plain untilted grid. Same doctrine as the sphere
   it replaced: the photographs and the class lists are the content,
   the fan is decoration. */
.ages-section:not(.ages-on) .ages-stack {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 14px;
}
.ages-section:not(.ages-on) .age-tile { width: 100%; margin: 0; transform: none; cursor: default; }
.ages-section:not(.ages-on) .ages-panel { display: block; }
.ages-section:not(.ages-on) .ages-pane { opacity: 1; visibility: visible; transform: none; margin-top: 28px; }
.ages-section:not(.ages-on) .ages-hint { display: none; }

/* Coarse pointers: a 2x2 grid instead of a 700px fan, tilts halved,
   delays halved and travel shortened per the house touch rule. */
@media (max-width: 820px) {
  .ages-stack {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 18px 12px;
    justify-items: center;
    padding: 8px 0 12px;
  }
  .age-tile { width: min(100%, 200px); margin: 0; }
  .ages-on .ages-stack.reveal.in .age-tile { transition-duration: .5s, .35s; }
  .age-tile:nth-child(1) { --fan-r: -3deg;   --fan-y: 0px; --fd: .03s; }
  .age-tile:nth-child(2) { --fan-r: 2.5deg;  --fan-y: 6px; --fd: .09s; }
  .age-tile:nth-child(3) { --fan-r: -2.5deg; --fan-y: 0px; --fd: .15s; }
  .age-tile:nth-child(4) { --fan-r: 2deg;    --fan-y: 6px; --fd: .21s; }
  .ages-on .ages-stack.reveal:not(.in) .age-tile:nth-child(1) { --fan-x: 46%;  --fan-y: 10px; }
  .ages-on .ages-stack.reveal:not(.in) .age-tile:nth-child(2) { --fan-x: -46%; --fan-y: 10px; }
  .ages-on .ages-stack.reveal:not(.in) .age-tile:nth-child(3) { --fan-x: 46%;  --fan-y: -10px; }
  .ages-on .ages-stack.reveal:not(.in) .age-tile:nth-child(4) { --fan-x: -46%; --fan-y: -10px; }
}

/* Reduced motion: the fan is a static composition (tilt is layout,
   not motion) and the chosen card still straightens and lifts, but as
   an instant state, never a travel: every transition is cut and the
   entrance starts in place. main.js already lands .in immediately
   under this preference. */
@media (prefers-reduced-motion: reduce) {
  .age-tile, .age-card, .ages-pane,
  .ages-on .ages-stack.reveal.in .age-tile { transition: none; }
  .ages-on .ages-stack.reveal:not(.in) .age-tile { --fan-x: 0%; }
}


/* =====================================================================
   HERO v4, "THE PANEL".  Ported from 21st.dev PrismaHero (12200),
   chosen by Connor from rendered previews 2026-07-31.
   The signature moves, kept: an inset rounded panel rather than an
   edge-to-edge band, an oversized wordmark held at the bottom left ON
   the media, the promise and CTA in a narrow column bottom right, a
   film-grain pass over a two-stop grade, and a staggered word reveal.
   ===================================================================== */
.hero.hero-panel {
  display: block;
  min-height: 0;
  padding: clamp(8px, 1.1vw, 16px);
  padding-top: calc(var(--nav-h) + clamp(8px, 1.1vw, 16px));
  background: var(--client-band-dark);
  margin-top: calc(-1 * var(--nav-h));
}
.hero-panel .panel {
  position: relative;
  height: calc(100svh - var(--nav-h) - clamp(16px, 2.2vw, 32px));
  min-height: 520px;
  max-height: 940px;
  border-radius: clamp(16px, 2vw, 32px);
  overflow: hidden;
  isolation: isolate;
  background: #140918;
}
.hero-panel .panel-media { position: absolute; inset: 0; margin: 0; z-index: 0; }
.hero-panel .panel-media img {
  /* 50% 50% pairs with the recentred derivatives (optimize.py SEQ,
     Connor 2026-08-08): the dancers are centred in the files, so the
     panel shows the centre band. The old 34% showed the top band of
     crops that were themselves biased up, which is what read as
     "dancers too low". */
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; display: block;
}
/* Film grain. A tiny inline SVG turbulence, tiled: no extra request, and it
   is what stops a flat webp reading as a stock photo at this scale. */
.hero-panel .panel-noise {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  opacity: 0.42; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
}
/* Two-stop grade, client ink not #000, peak under the 0.90 ceiling. */
.hero-panel .panel-grade {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(20,9,24,0.46) 0%, rgba(20,9,24,0.04) 26%, rgba(20,9,24,0) 42%),
    linear-gradient(0deg, rgba(20,9,24,0.88) 0%, rgba(20,9,24,0.74) 20%, rgba(20,9,24,0.34) 42%, rgba(20,9,24,0) 62%);
}

.hero-panel .panel-content {
  position: absolute; inset: auto 0 0 0; z-index: 3;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(14px, 2vw, 28px);
  padding: clamp(18px, 3vw, 44px);
  align-items: end;
}
.hero-panel .panel-h1 { margin: 0; min-width: 0; }
.hero-panel .panel-mark { display: block; }
.hero-panel .panel-mark img {
  display: block;
  width: min(58vw, 780px);
  height: auto;
  filter: drop-shadow(0 6px 30px rgba(20,9,24,0.6));
}
.hero-panel .panel-line {
  display: block;
  margin-top: clamp(6px, 1vh, 14px);
  font-family: var(--client-display);
  font-size: clamp(1.05rem, 1.9vw, 1.75rem);
  font-weight: 600;
  letter-spacing: 0.005em;
  color: rgba(255,255,255,0.94);
  text-shadow: 0 1px 14px rgba(20,9,24,0.7);
}
.hero-panel .panel-line span { display: inline-block; margin-right: 0.28em; }

.hero-panel .panel-aside {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: clamp(14px, 2vh, 22px);
}
.hero-panel .panel-sub {
  margin: 0;
  max-width: 46ch;
  font-family: var(--client-body);
  font-size: clamp(0.86rem, 1.05vw, 1rem);
  line-height: 1.45;
  color: rgba(255,255,255,0.82);
  text-shadow: 0 1px 12px rgba(20,9,24,0.7);
}
/* CTA: pill with a circular arrow that grows the gap on hover, as in the
   source. Fill is the client's on-dark action colour, label is plum ink. */
.hero-panel .panel-cta {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 6px 6px 6px 22px;
  border-radius: 999px;
  background: #C474D3;
  color: #241329;
  font-family: var(--client-body); font-weight: 600; font-size: 1rem;
  text-decoration: none;
  min-height: 52px;
  transition: gap 220ms ease, background 220ms ease;
}
.hero-panel .panel-cta:hover { gap: 18px; background: #D291DE; }
.hero-panel .panel-cta-dot {
  display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: 50%;
  background: #241329; color: #fff;
  transition: transform 220ms ease;
}
.hero-panel .panel-cta:hover .panel-cta-dot { transform: scale(1.08); }
.hero-panel .panel-cta-dot svg { width: 18px; height: 18px; }
.hero-panel .panel-cta:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }

@media (min-width: 900px) {
  .hero-panel .panel-content {
    grid-template-columns: minmax(0, 1.55fr) minmax(280px, 0.9fr);
    gap: clamp(24px, 4vw, 72px);
  }
  .hero-panel .panel-aside { padding-bottom: clamp(8px, 1.6vh, 22px); }
}
@media (max-width: 899px) {
  .hero-panel .panel-mark img { width: min(84vw, 460px); }
  .hero-panel .panel-cta { width: 100%; justify-content: space-between; }
}

/* Staggered word reveal, the framer-motion effect done with CSS only:
   y 20px -> 0, opacity 0 -> 1, same 0.6s and the same easing curve. */
.reveal-w { opacity: 0; transform: translateY(20px); }
.hero-panel.is-ready .reveal-w {
  opacity: 1; transform: none;
  transition: opacity 600ms cubic-bezier(0.16,1,0.3,1) var(--d, 0ms),
              transform 600ms cubic-bezier(0.16,1,0.3,1) var(--d, 0ms);
}
@media (prefers-reduced-motion: reduce) {
  .reveal-w { opacity: 1; transform: none; transition: none; }
}

/* The nav becomes the floating pill the reference leads with, but only
   while it sits over the panel. On scroll it returns to the house bar. */
.nav.nav-over:not(.scrolled) .nav-inner {
  width: auto;
  margin-inline: auto;
  background: rgba(16, 8, 20, 0.92);
  border-radius: 0 0 clamp(14px, 1.6vw, 22px) clamp(14px, 1.6vw, 22px);
  padding-inline: clamp(16px, 2.4vw, 34px);
  backdrop-filter: blur(6px);
}

/* The promise column sits bottom right, which on this frame is lit tulle.
   Standard 01 s5 anchors the scrim to the type side, so the grade gains a
   right-hand anchor rather than the whole panel being darkened. */
.hero-panel .panel-grade {
  background:
    linear-gradient(180deg, rgba(20,9,24,0.46) 0%, rgba(20,9,24,0.04) 26%, rgba(20,9,24,0) 42%),
    linear-gradient(0deg, rgba(20,9,24,0.90) 0%, rgba(20,9,24,0.80) 22%, rgba(20,9,24,0.40) 44%, rgba(20,9,24,0) 64%),
    radial-gradient(120% 78% at 88% 100%, rgba(20,9,24,0.86) 0%, rgba(20,9,24,0.58) 38%, rgba(20,9,24,0) 72%);
}
/* Compact centred pill, as the reference leads with. The house bar returns
   the moment the nav leaves the panel. */
.nav.nav-over:not(.scrolled) .nav-inner {
  width: fit-content;
  max-width: calc(100% - 32px);
  gap: clamp(14px, 2vw, 30px);
}
.nav.nav-over:not(.scrolled) .nav-phone { display: none; }
.nav.nav-over:not(.scrolled) .nav-cta {
  background: #C474D3; color: #241329; border-color: transparent;
}

/* Left anchor too: the wordmark is held bottom left, and on this frame that
   is lit tulle. Measured 3.92:1 with only the bottom band; both corners now
   carry an anchor and the centre of the photograph stays untouched. */
.hero-panel .panel-grade {
  background:
    linear-gradient(180deg, rgba(20,9,24,0.46) 0%, rgba(20,9,24,0.04) 26%, rgba(20,9,24,0) 42%),
    linear-gradient(0deg, rgba(20,9,24,0.90) 0%, rgba(20,9,24,0.82) 24%, rgba(20,9,24,0.44) 46%, rgba(20,9,24,0) 66%),
    radial-gradient(120% 78% at 88% 100%, rgba(20,9,24,0.86) 0%, rgba(20,9,24,0.58) 38%, rgba(20,9,24,0) 72%),
    radial-gradient(105% 72% at 14% 100%, rgba(20,9,24,0.88) 0%, rgba(20,9,24,0.62) 40%, rgba(20,9,24,0) 74%);
}

/* Reveal, rewritten as a CSS animation with no JS dependency.
   The earlier version held every word at opacity 0 until main.js added a
   class, so any JS failure left the promise line, the copy and the CTA
   permanently invisible. An animation with fill-mode both runs on load and
   cannot strand the content. */
.hero-panel .reveal-w {
  opacity: 0;
  animation: heroPullUp 600ms cubic-bezier(0.16, 1, 0.3, 1) var(--d, 0ms) both;
}
@keyframes heroPullUp {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-panel .reveal-w { opacity: 1; transform: none; animation: none; }
}


/* =====================================================================
   HERO v5.  Connor 2026-07-31: logo at the TOP, promise line and the
   Start Here CTA at the BOTTOM, no arrow anywhere, and the body
   paragraph that sat above the CTA removed.
   ===================================================================== */
.hero-panel .panel-top {
  position: absolute; inset: clamp(20px, 4vh, 54px) 0 auto 0; z-index: 3;
  display: flex; justify-content: center;
  padding-inline: clamp(18px, 3vw, 44px);
}
.hero-panel .panel-top .panel-h1 { margin: 0; }
.hero-panel .panel-top .panel-mark img {
  width: min(46vw, 620px);
  filter: drop-shadow(0 6px 30px rgba(20,9,24,0.62));
}
.hero-panel .panel-foot {
  position: absolute; inset: auto 0 0 0; z-index: 3;
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(16px, 2.4vh, 28px);
  /* Connor 2026-08-08: promise line + CTA sit slightly lower. Bottom
     inset eased off the shared 3.4vw clamp; the bottom grade holds
     0.90 to 0.80 through its first 16pct, so the lowered line still
     sits on the dense end of the scrim. */
  padding: clamp(20px, 3.4vw, 52px) clamp(18px, 3vw, 44px) clamp(12px, 2vw, 30px);
  text-align: center;
}
.hero-panel .panel-foot .panel-line {
  margin: 0;
  font-family: var(--client-display);
  font-size: clamp(1.5rem, 3.4vw, 3rem);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.012em;
  color: #fff;
  text-shadow: 0 2px 18px rgba(20,9,24,0.72);
}
.hero-panel .panel-foot .panel-line span { display: inline-block; margin-right: 0.26em; }
/* CTA with no arrow: a clean pill, nothing inside it but the label. */
.hero-panel .panel-cta {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 0;
  padding: 0 clamp(30px, 3.2vw, 44px);
  min-height: 56px;
  border-radius: 999px;
  background: #C474D3; color: #241329;
  font-family: var(--client-body); font-weight: 600; font-size: 1.02rem;
  text-decoration: none;
  transition: background 220ms ease, transform 220ms ease;
}
.hero-panel .panel-cta:hover { gap: 0; background: #D291DE; transform: translateY(-2px); }

/* The grade re-anchors: type is now centred top and centred bottom, so the
   two corner radials are replaced by straight top and bottom bands. */
.hero-panel .panel-grade {
  background:
    linear-gradient(180deg, rgba(20,9,24,0.86) 0%, rgba(20,9,24,0.62) 12%, rgba(20,9,24,0.16) 26%, rgba(20,9,24,0) 40%),
    linear-gradient(0deg,  rgba(20,9,24,0.90) 0%, rgba(20,9,24,0.80) 16%, rgba(20,9,24,0.44) 32%, rgba(20,9,24,0) 52%);
}

@media (max-width: 768px) {
  .hero-panel .panel-top .panel-mark img { width: min(78vw, 420px); }
  .hero-panel .panel-cta { width: 100%; }
}

/* Arrows are gone site-wide on this page; the hover nudge went with them. */
.text-link .arw, .svc-link .arw { display: none; }

/* Getting Started: the two closing Q&As were a narrow measure inside a wide
   container, which left a column of dead space down the right. They now run
   as a two-up so the band is filled, and the season heading holds one line. */
@media (min-width: 900px) {
  .process .qa-pair {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(28px, 4vw, 64px);
    margin-top: clamp(28px, 4vh, 52px);
  }
  .process .qa-pair > div { min-width: 0; }
  .process .qa-pair h3 { margin-top: 0; white-space: nowrap; }
  .process .qa-pair .answer { max-width: none; }
}

/* Rotating frames inside the panel. Stacked in one grid cell, opacity only. */
.hero-panel .panel-media.hero-seq { display: grid; }
.hero-panel .hero-seq .seq-frame {
  grid-area: 1 / 1;
  display: block; min-width: 0;
  opacity: 0;
  transition: opacity 700ms ease-in-out;
  will-change: opacity;
}
.hero-panel .hero-seq .seq-frame.is-on { opacity: 1; }
.hero-panel .hero-seq .seq-frame img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; display: block;
}
@media (prefers-reduced-motion: reduce) {
  .hero-panel .hero-seq .seq-frame { transition: none; }
  .hero-panel .hero-seq .seq-frame:not(.is-on) { display: none; }
}

/* Logo lifted, and trimmed so the swash V clears the top edge cleanly.
   Connor 2026-07-31. */
.hero-panel .panel-top { inset: clamp(8px, 1.4vh, 20px) 0 auto 0; }
.hero-panel .panel-top .panel-mark img { width: min(41vw, 545px); }
@media (max-width: 768px) {
  .hero-panel .panel-top { inset: 8px 0 auto 0; }
  .hero-panel .panel-top .panel-mark img { width: min(72vw, 380px); }
}

/* Raising the logo moved it below the old top band, which fell off by 26pct
   and left it at 1.90:1 over lit backdrop. The band now reaches the logo's
   full height. The upper third of every frame is backdrop, not faces, so
   this costs nothing the photograph needs. */
.hero-panel .panel-grade {
  background:
    linear-gradient(180deg,
      rgba(20,9,24,0.90) 0%, rgba(20,9,24,0.84) 14%, rgba(20,9,24,0.70) 26%,
      rgba(20,9,24,0.44) 36%, rgba(20,9,24,0.14) 46%, rgba(20,9,24,0) 56%),
    linear-gradient(0deg,
      rgba(20,9,24,0.90) 0%, rgba(20,9,24,0.80) 16%, rgba(20,9,24,0.44) 32%,
      rgba(20,9,24,0) 52%);
}

/* Logo: smaller and with real air above it (Connor 2026-08-01: the V read
   as cut off and the mark covered faces on several frames). Width comes
   down 545 -> 440 and the top inset grows so the swash never kisses the
   panel's clipped corner radius. */
.hero-panel .panel-top { inset: clamp(22px, 3.4vh, 44px) 0 auto 0; }
.hero-panel .panel-top .panel-mark img { width: min(31vw, 440px); }
@media (max-width: 768px) {
  .hero-panel .panel-top { inset: 16px 0 auto 0; }
  .hero-panel .panel-top .panel-mark img { width: min(62vw, 330px); }
}

/* Stats under "What results can I expect?": lines gone, number centred
   directly over its caption (Connor 2026-08-01). */
.stats { display: flex; gap: clamp(20px, 3vw, 44px); }
.stats > div {
  border-left: 0 !important;
  padding-left: 0 !important;
  flex: 1 1 0;
  text-align: center;
}
.stats .stat { display: block; text-align: center; }
.stats dd { margin: 6px 0 0; text-align: center; }

/* Field cards: the two slots whose photography exists but is unreachable
   (yoga originals over the Drive ceiling; hip hop resumes this season). A
   committed brand field with the logo's own 'e' so the row reads designed,
   never empty. Swap for the real frame the moment it lands. */
.svc-card .svc-img.svc-field {
  display: grid; place-items: center; position: relative;
  /* .svc-card .svc-img sets a pale tint background at higher specificity,
     which washed the field card out to pink. Matched and overridden here. */
  background: radial-gradient(120% 100% at 30% 20%, #533D6B 0%, #241329 70%);
}
.svc-card .svc-img.svc-field-teal { background: radial-gradient(120% 100% at 30% 20%, #1e817f 0%, #0d3a39 75%); }
.svc-field-glyph {
  font-family: var(--client-display); font-style: italic; font-weight: 600;
  font-size: clamp(6rem, 11vw, 9.5rem); line-height: 1;
  color: rgba(255,255,255,0.16); user-select: none;
}
.svc-field-tag {
  position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%);
  width: max-content;
  font-family: var(--client-body); font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(255,255,255,0.85);
}


/* =====================================================================
   MERGE FROM THE PREVIOUS BUILD (2026-08-04, Connor): the why strip and
   the FAQ accordion, rebuilt in this build's design system.
   ===================================================================== */
.why-grid {
  list-style: none;
  margin: clamp(18px, 2.6vh, 30px) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: clamp(14px, 1.8vw, 24px);
}
.why-grid li {
  background: var(--client-card);
  border-radius: var(--radius);
  padding: clamp(16px, 2vw, 24px);
  font-size: 0.95rem;
  line-height: 1.5;
  box-shadow: 0 6px 22px rgba(14, 10, 18, 0.06);
}
.why-grid strong {
  display: block;
  font-family: var(--client-display);
  font-size: 1.05rem;
  margin-bottom: 6px;
  color: var(--client-ink);
}

/* FAQ: native details/summary, zero JS. The marker is a plus that turns
   into a close glyph on open; no chevrons, no arrows, per the house rule. */
.faq .container-read { max-width: min(760px, calc(100% - 48px)); margin-inline: auto; }
.faq-list { margin-top: clamp(20px, 3vh, 34px); }
.faq-list details {
  background: var(--client-card);
  border-radius: var(--radius);
  margin-bottom: 12px;
  box-shadow: 0 4px 18px rgba(14, 10, 18, 0.05);
  overflow: hidden;
}
.faq-list summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 20px;
  font-family: var(--client-display);
  font-weight: 600;
  font-size: 1.02rem;
  color: var(--client-ink);
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary::after {
  content: "+";
  flex: 0 0 auto;
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--client-primary-soft);
  color: var(--client-primary-ink);
  font-family: var(--client-body);
  font-size: 1.15rem;
  line-height: 1;
  transition: transform 200ms ease;
}
.faq-list details[open] summary::after { transform: rotate(45deg); }
.faq-list details > p {
  margin: 0;
  padding: 0 20px 18px;
  font-size: 0.95rem;
  line-height: 1.55;
  color: color-mix(in srgb, var(--client-ink) 80%, transparent);
}
.faq-list summary:focus-visible { outline: 3px solid var(--client-primary-ink); outline-offset: -3px; }

.foot-mission {
  max-width: 44ch;
  margin-top: 12px;
  font-size: 0.86rem;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.72);
}

/* Five reasons, five columns at desktop: auto-fit was wrapping 4+1 with an
   orphan card. Below 1100px it falls back to the auto grid. */
@media (min-width: 1100px) {
  .why-grid { grid-template-columns: repeat(5, 1fr); }
  .why-grid li { font-size: 0.88rem; }
  .why-grid strong { font-size: 0.98rem; }
}


/* =====================================================================
   COH MOTION PORT (2026-08-04). Word-mask title reveals + hero settle.
   Mechanics lifted verbatim from our COH Church build: the outer word
   span clips vertically only ("visible clip") and carries padding that a
   negative margin cancels, so ascenders and descenders have headroom
   inside the clip and nothing shears off mid-glyph.
   ===================================================================== */
.wr .wr-word {
  display: inline-block;
  vertical-align: bottom;
  --wr-clear: 0.34em;
  --wr-top: 0.12em;
  padding: var(--wr-top) 0 var(--wr-clear);
  margin: calc(-1 * var(--wr-top)) 0 calc(-1 * var(--wr-clear));
  overflow: visible clip;
}
.wr .wr-word > span {
  display: inline-block;
  transform: translateY(calc(100% + var(--wr-clear)));
  transition: transform 0.85s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: var(--wd, 0s);
  will-change: transform;
}
.wr.wr-in .wr-word > span { transform: translateY(0); }
@media (hover: none), (pointer: coarse) {
  .wr .wr-word > span { transition-duration: 0.55s; }
}
@media (prefers-reduced-motion: reduce) {
  .wr .wr-word > span { transition: none; transform: none; }
}

/* Hero settle: REMOVED 2026-08-18 on Connor's direction. The panel used to
   open at scale(1.06) and ease to rest, and each live frame then pushed
   from 1 to 1.06 over its dwell (see the seq-frame rule further down,
   also removed). Both were doing the same thing to the same pixels: with
   the two stacked, the fold never showed the photograph at its true crop,
   and on the portrait sources it read as "too zoomed in". The frames now
   render at exactly their cover size and never move. */

/* Block reveals, retimed to COH's feel: same 24px rise, but the softer
   0.9s ease-out instead of the tighter house duration, and half delays on
   coarse pointers, which is what keeps mobile from feeling laggy. */
.reveal { transition-duration: 0.9s; transition-timing-function: ease-out; }
.reveal-media { transition-duration: 1s; }
@media (hover: none), (pointer: coarse) {
  .reveal { transition-duration: 0.45s; transition-delay: calc(var(--d, 0s) * 0.5); }
}

/* Lenis's own required base styles (from its docs): */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }

/* Nav locked to its light form on the homepage too (Connor 2026-08-04:
   no dark-to-light transition; logo top left from the first frame).
   nav-over is gone from the markup, which retires the dark pill state,
   the hidden logo and the hidden phone. The hero no longer tucks under
   the bar, so its negative pull and nav-height padding go too. */
.hero.hero-panel {
  margin-top: 0;
  padding-top: clamp(8px, 1.1vw, 16px);
}
.hero-panel .panel-top { inset: clamp(22px, 3.4vh, 44px) 0 auto 0; }

/* The why paragraph (Connor 2026-08-04): the five reason cards became one
   flowing paragraph in the results row; the key phrases carry the brand
   highlight so they read at a scan. primary-ink on the cream band is
   8.97:1, and the tint sits behind bold text, so nothing drops below Q7. */
.why-par { line-height: 1.65; }
.why-par .hl {
  font-weight: 600;
  color: var(--client-primary-ink);
  background: linear-gradient(transparent 62%, var(--client-primary-soft) 62%);
  padding: 0 2px;
  border-radius: 2px;
}

/* Resources hub cards (2026-08-04): four routed guides. Whole card is the
   link; image top, title, one line. */
.hub-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: clamp(16px, 2vw, 28px);
  margin-top: clamp(22px, 3vh, 36px);
}
.hub-card {
  display: block;
  background: var(--client-card);
  border-radius: var(--radius);
  padding: clamp(14px, 1.6vw, 20px);
  text-decoration: none;
  color: var(--client-ink);
  box-shadow: 0 6px 22px rgba(14, 10, 18, 0.06);
  transition: transform 220ms ease, box-shadow 220ms ease;
}
.hub-card:hover { transform: translateY(-4px); box-shadow: 0 14px 34px rgba(14, 10, 18, 0.12); }
.hub-card:focus-visible { outline: 3px solid var(--client-primary-ink); outline-offset: 3px; }
.hub-card .svc-img { aspect-ratio: 4 / 5; border-radius: calc(var(--radius) - 6px); overflow: hidden; margin-bottom: 12px; }
.hub-card .svc-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hub-card h3 { margin: 0 0 6px; font-size: 1.08rem; }
.hub-card p { margin: 0; font-size: 0.9rem; line-height: 1.5; color: color-mix(in srgb, var(--client-ink) 75%, transparent); }

/* =====================================================================
   VIBRANCY PASS, Connor 2026-08-08: "lean away from the dark purples,
   lean into the lighter colours of the brand guide." Three moves:
   1. The hero's outer frame leaves deep plum for the paper: the page
      now OPENS light, and the photo panel floats on it.
   2. The age-group band went soft lilac (see the ages-section block).
   3. Proof keeps its documented role as the page's emotional peak and
      keeps its depth, but changes HUE: deep plum becomes the brand's
      teal (--client-teal-band, derivation and measured floors at the
      token). The purple radial accent becomes a glow of the raw brand
      secondary, eased to 0.32 so body text inside it holds its floor
      (verified on rendered pixels).
   This is a directed departure from the field-dark note above; the
   page no longer opens and closes on the brand's dark. The footer
   stays band-dark. Recorded in BRAND.md.
   ===================================================================== */
.hero.hero-panel { background: var(--paper); }
.section.proof.field-dark {
  /* Glow eased to 0.26 and its core lifted to the corner: at 0.32/8pct
     the white-84 body text nearest the core measured 7.0:1 dead on the
     floor; at 0.26/0pct the gutter beside the answer text measures
     clear (verified on rendered pixels, see BRAND.md). */
  background:
    radial-gradient(120% 90% at 85% 0%, rgba(30, 129, 127, 0.26) 0%, rgba(30, 129, 127, 0) 62%),
    var(--client-teal-band);
}
.field-dark .eyebrow { color: var(--client-terminal); }
.field-dark .stat-num { color: #9FD6BC; }

/* =====================================================================
   ABOUT PAGE HEAD + FACULTY GRID, Connor 2026-08-08.
   The About hero image is retired: a compact type-only head opens the
   page (breadcrumb + the twenty-years H1) and the story section is the
   first thing after it. The h1 span roles are reused from the hero
   system; their on-image whites re-map to the light page here. The
   team section now carries the faculty themselves: photo tiles where
   confirmed photos exist, initials tiles where they do not (GAPS 3).
   ===================================================================== */
.about-head { padding-block: clamp(26px, 4vh, 46px) 0; }
.about-head .crumbs { margin-bottom: clamp(18px, 2.6vh, 30px); }
.about-head h1 { max-width: none; margin: 0 auto; text-align: center; text-wrap: balance; }
.about-head .h1-lead { color: var(--accent); }
.about-head .h1-hero { color: var(--client-primary-ink); }
.about-head .h1-tail { color: color-mix(in srgb, var(--ink) 82%, transparent); }

.fac-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: clamp(14px, 2vw, 24px);
}
.fac-cell { margin: 0; }
.fac-tile {
  display: block;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--client-primary-soft);
}
.fac-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fac-tile-empty { display: grid; place-items: center; }
.fac-init {
  font-family: var(--display);
  font-weight: 700;
  font-size: 2.2rem;
  letter-spacing: 0.04em;
  color: var(--client-primary-ink);
}
.fac-cell figcaption strong {
  display: block;
  margin-top: 10px;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.02rem;
  color: var(--ink);
}
.fac-cell figcaption span {
  display: block;
  margin-top: 2px;
  font-size: var(--t-caption);
  line-height: 1.4;
  color: color-mix(in srgb, var(--ink) 62%, transparent);
}

/* =====================================================================
   FULL-BLEED HERO, Connor 2026-08-08: the paper frame and the rounded
   inset are gone. The hero is the full first picture, edge to edge,
   from the bottom of the light nav bar to the fold. The panel keeps
   its internal anatomy (media stack, grade, noise, top wordmark,
   bottom line + copy + CTA); only the framing changed. The hero copy
   grew a standing paragraph (panel-sub) under the line, per the
   2026-08-08 homepage copy doc.
   ===================================================================== */
.hero.hero-panel { padding: 0; margin-top: 0; }
.hero-panel .panel {
  border-radius: 0;
  height: calc(100svh - var(--nav-h));
  max-height: none;
}
.hero-panel .panel-foot .panel-sub {
  margin: 0;
  max-width: 64ch;
  font-family: var(--client-body);
  font-size: clamp(0.88rem, 1.05vw, 1.02rem);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.92);
  text-shadow: 0 1px 12px rgba(20, 9, 24, 0.7);
}
@media (max-width: 768px) {
  .hero-panel .panel-foot .panel-sub { font-size: 0.86rem; }
}

/* =====================================================================
   ENCORE-FRAMEWORK RESTRUCTURE, Connor 2026-08-08: the site's pages
   and framework mirror the Encore Performers build (craft benchmark;
   palette, type and concept stay Elevate's). Dropdown navigation with
   About and Programs groups, grouped mobile menu, a homepage photo
   gallery band, and four program sub-pages. Directed departure from
   Policy 3.1 s1 ("six links, no dropdowns"); recorded in BRAND.md.
   ===================================================================== */
.nav-drop { position: relative; display: inline-flex; align-items: center; }
.nav-drop-btn {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: var(--tap);
  padding: 0; border: 0; background: none; cursor: pointer;
  font-family: var(--body); font-size: var(--t-small); font-weight: 600;
  color: color-mix(in srgb, var(--ink) 70%, transparent);
}
.nav-drop-btn:hover, .nav-drop:focus-within .nav-drop-btn { color: var(--ink); }
.nav-drop-btn svg { width: 12px; height: 12px; transition: transform var(--dur-ui) var(--ease); }
.nav-drop:focus-within .nav-drop-btn svg,
.nav-drop.open .nav-drop-btn svg { transform: rotate(180deg); }
.nav-drop-menu {
  position: absolute; top: calc(100% + 10px); left: 50%;
  transform: translate(-50%, 6px);
  min-width: 220px; padding: 10px;
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: 0 18px 44px color-mix(in srgb, var(--ink) 16%, transparent);
  opacity: 0; visibility: hidden; z-index: 60;
  transition: opacity .2s var(--ease), transform .2s var(--ease), visibility 0s linear .2s;
}
.nav-drop-menu::before { content: ""; position: absolute; left: 0; right: 0; top: -12px; height: 12px; }
/* Opening on :hover unconditionally is why tapping "Programs" landed the
   user on All Dance Classes (Connor, 2026-08-19): on a touchscreen or a tap
   on a trackpad the first tap both triggers the hover state AND passes
   through to whatever the revealed menu puts under the finger, which is the
   first item. Hover is now gated to pointers that can actually hover; touch
   and click use the button, which only toggles and never navigates. */
.nav-drop:focus-within .nav-drop-menu,
.nav-drop.open .nav-drop-menu {
  opacity: 1; visibility: visible; transform: translate(-50%, 0);
  transition: opacity .2s var(--ease), transform .2s var(--ease), visibility 0s;
}
@media (hover: hover) and (pointer: fine) {
  .nav-drop:hover .nav-drop-menu {
    opacity: 1; visibility: visible; transform: translate(-50%, 0);
    transition: opacity .2s var(--ease), transform .2s var(--ease), visibility 0s;
  }
}
.nav-drop-menu a {
  display: block; padding: 9px 14px; border-radius: 8px;
  border-bottom: 0; min-height: 0;
  font-size: 0.92rem; font-weight: 600; text-decoration: none;
  color: var(--ink);
}
.nav-drop-menu a:hover { background: var(--client-primary-soft); color: var(--client-primary-ink); }
.nav-drop-menu a[aria-current="page"] { color: var(--client-primary-ink); }

/* Nine top-level items: the bar hands off to the hamburger earlier. */
@media (max-width: 1279px) { .nav .nav-links, .nav .nav-phone { display: none; } .nav .menu-btn { display: inline-flex; } }
@media (min-width: 1280px) and (max-width: 1439px) { .nav .nav-phone { display: none; } }

.menu-group {
  margin: 18px 0 2px; font-size: 0.68rem; font-weight: 600;
  letter-spacing: 0.22em; text-transform: uppercase;
  color: color-mix(in srgb, var(--ink) 48%, transparent);
}

/* Homepage gallery band (Encore beat 2): pure photography, no copy. */
.gallery-band { padding-block: clamp(26px, 4vh, 54px) clamp(34px, 5vh, 70px); }
.gallery-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: clamp(10px, 1.2vw, 18px); }
.gallery-grid figure {
  margin: 0; border-radius: var(--radius); overflow: hidden;
  aspect-ratio: 4 / 5; background: var(--client-primary-soft);
}
.gallery-grid figure:nth-child(even) { transform: translateY(clamp(10px, 1.6vw, 26px)); }
.gallery-grid img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (max-width: 820px) {
  .gallery-grid { grid-template-columns: repeat(3, 1fr); }
  .gallery-grid figure:nth-child(even) { transform: translateY(10px); }
}

.ages-all { text-align: center; margin: clamp(22px, 3vh, 34px) auto 0; }

/* =====================================================================
   2026-08-08, three refinements (Connor):
   1. KEN BURNS on the hero frames: each frame opens at its widened
      crop and eases to 1.06 across the 3s dwell plus the 700ms fade,
      so it is still creeping in as the crossfade takes it. A
      TRANSITION, not an animation: when is-on leaves, the outgoing
      frame drifts back so slowly (about 2 percent of the travel
      during the fade) that there is no snap, and it has fully reset
      long before its next turn in the loop.
   2. The seam between the age-group fan and the default copy
      tightens; the old 34px bottom padding overshot once the captions
      settled.
   3. Nav condensed to six items (Resources takes in tuition, dress
      code, FAQs): the hamburger hand-off returns to 1100px.
   ===================================================================== */
/* The per-frame Ken Burns push (scale 1 -> 1.06 over 3.7s) is REMOVED,
   2026-08-18, Connor: base image only, no zoom. Nothing replaces it; the
   frames cross-fade and hold still. Do not reintroduce a transform here
   without also checking the panel-media settle above, which was the other
   half of the same effect. */

.ages-stack { padding-bottom: 14px; }

@media (min-width: 1100px) and (max-width: 1279px) {
  .nav .nav-links { display: flex; }
  .nav .menu-btn { display: none; }
  .nav .nav-phone { display: none; }
}
@media (max-width: 1099px) { .nav .nav-links, .nav .nav-phone { display: none; } .nav .menu-btn { display: inline-flex; } }

/* Resources jump chips: the chip tag as a link. */
.resource-jumps { justify-content: center; margin: clamp(16px, 2.4vh, 26px) 0 0; padding: 0; list-style: none; }
.resource-jumps .chip { text-decoration: none; transition: background var(--dur-ui) var(--ease), color var(--dur-ui) var(--ease); }
.resource-jumps a.chip:hover { background: var(--client-primary-soft); color: var(--client-primary-ink); }

/* =====================================================================
   STORYBRAND HOMEPAGE PASS, Connor 2026-08-11 (wireframe supplied):
   stakes bullets, value-prop bullets, and the explanatory paragraph.
   One direct CTA repeated per section, per Miller.
   ===================================================================== */
.stakes-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(12px, 1.8vh, 20px) clamp(24px, 3vw, 56px);
  margin: 0; padding: 0; list-style: none;
  max-width: 62rem;
}
.stakes-list li, .value-list li {
  position: relative;
  padding-left: 22px;
  font-size: var(--t-lede);
  line-height: 1.5;
  color: color-mix(in srgb, var(--ink) 74%, transparent);
}
.stakes-list li::before, .value-list li::before {
  content: "";
  position: absolute; left: 0; top: 0.55em;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--client-primary);
  opacity: 0.55;
}
.value-list { margin: 0 0 6px; padding: 0; list-style: none; display: grid; gap: clamp(10px, 1.6vh, 18px); }
@media (max-width: 700px) { .stakes-list { grid-template-columns: 1fr; } }
.explain-par { max-width: 68ch; margin-inline: auto; text-align: center; font-size: var(--t-lede); line-height: 1.6; color: color-mix(in srgb, var(--ink) 74%, transparent); }
.explain-cta { text-align: center; }
#place-to-shine .sec-head.center h2 { margin-inline: auto; }

/* =====================================================================
   REVIEW HIGHLIGHTS, Connor 2026-08-11: the conversion phrase in each
   testimonial carries a purple marker band (vanilla port of the
   21st.dev HighlightText component). The source's skewed inline-block
   band cannot wrap, and these phrases run long inside 300px cards, so
   the band is a background with box-decoration-break: clone: it wraps
   line by line like a real marker. White on the brand primary #792b87
   measures 8.37:1 (documented pair), so the words stay legible.
   The quotes themselves are EXCERPTS of the approved testimonials:
   verbatim substrings only, an ellipsis where a sentence is skipped,
   never a reworded word. Originals in BRAND.md and the retired files.
   ===================================================================== */
mark.hl-mark {
  /* Connor 2026-08-11: lightened to the section CTA's exact on-dark
     pair, #C474D3 fill with the plum ink label (5.65:1, documented). */
  background: linear-gradient(100deg,
    transparent 0%, #C474D3 1.4%,
    #C474D3 98.6%, transparent 100%);
  color: #241329;
  padding: 0.06em 0.32em;
  margin-inline: -0.06em;
  border-radius: 0.28em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.field-dark .review blockquote mark.hl-mark { color: #241329; }

/* Stakes centred (Connor 2026-08-11): copy centred above, the eight
   bullets spread at equal distance across the band, CTA centred under
   them. Scoped past the z-right anchor rules. */
#stakes .sec-head { margin-inline: auto; text-align: center; max-width: 46rem; }
#stakes .stakes-list {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  max-width: none;
  gap: clamp(16px, 2.4vh, 24px) clamp(18px, 2.2vw, 40px);
}
.cta-center { text-align: center; }
@media (max-width: 1020px) { #stakes .stakes-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { #stakes .stakes-list { grid-template-columns: 1fr; } }

/* The centred stakes head, completed: the global type measures (h2 at
   18ch, p at 68ch) anchor their BOXES left inside a centred parent,
   the same trap the ages pane hit on 8/8. Centre the boxes, keep the
   measures; the anchor system's explicit left eyebrow gets overridden. */
#stakes .sec-head .eyebrow { text-align: center; margin-inline: auto; }
#stakes .sec-head h2, #stakes .sec-head .lede { margin-inline: auto; }
.cta-center { max-width: none; }

/* Mission / Vision two-up on the About page (Encore about framework,
   Connor 2026-08-11): two white statement cards on the cream band. */
.mv-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 48px);
  margin-top: clamp(10px, 2vh, 24px);
}
.mv-card {
  background: var(--card);
  border-radius: var(--radius);
  padding: clamp(20px, 2.6vw, 36px);
  box-shadow: 0 10px 30px color-mix(in srgb, var(--ink) 6%, transparent);
}
.mv-card .statement { margin: 0 0 .5em; max-width: none; }
.mv-card .caption-ill { margin: 0; }
@media (max-width: 820px) { .mv-grid { grid-template-columns: 1fr; } }

/* =====================================================================
   FACULTY FEATURED PROFILE (About page), Connor 2026-08-11: vanilla
   port of the 21st.dev ProfileCard. Square media with the overlapping
   white card; panes stack in one grid cell and swap with the source's
   entrance (fade + 20px slide). Thumbs switch the featured person.
   No-JS: all six profiles stack readable in flow.
   ===================================================================== */
.mv-grid.mv-one { grid-template-columns: 1fr; max-width: 640px; margin-inline: auto; }

.profile-stage { margin-top: clamp(18px, 3vh, 36px); }
.profile-panes { display: grid; }
.profile-pane {
  grid-area: 1 / 1;
  display: flex;
  align-items: center;
  opacity: 0;
  visibility: hidden;
  transform: translateX(20px);
  transition: opacity .45s var(--ease), transform .45s var(--ease), visibility 0s linear .45s;
}
.profile-pane.is-on {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity .5s var(--ease) .05s, transform .5s var(--ease) .05s, visibility 0s;
}
.profile-media {
  width: min(42%, 470px);
  aspect-ratio: 1 / 1;
  flex-shrink: 0;
  border-radius: calc(var(--radius) + 8px);
  overflow: hidden;
  background: var(--client-primary-soft);
}
.profile-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.profile-media-empty { display: grid; place-items: center; }
.profile-media-empty .fac-init { font-size: clamp(3rem, 6vw, 5rem); }
.profile-body {
  position: relative;
  z-index: 1;
  flex: 1;
  /* Connor 2026-08-11: overlap eased so the instructor photo reads
     clearly; the card keeps just a sliver of the source's overlap. */
  margin-left: clamp(-22px, -1.5vw, -14px);
  background: var(--card);
  border-radius: calc(var(--radius) + 8px);
  box-shadow: 0 24px 60px color-mix(in srgb, var(--ink) 14%, transparent);
  padding: clamp(22px, 3vw, 44px);
}
.profile-body h3 { margin: 0 0 .25em; max-width: none; }
.profile-role {
  margin: 0 0 1em;
  font-size: var(--t-caption);
  font-weight: 600;
  color: color-mix(in srgb, var(--ink) 62%, transparent);
}
.profile-body p:last-child { margin-bottom: 0; }

.profile-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(12px, 1.6vw, 22px);
  margin-top: clamp(20px, 3vh, 34px);
  justify-content: center;
}
.profile-thumbs button {
  display: grid;
  gap: 7px;
  justify-items: center;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--body);
}
.pt-img {
  display: block;
  width: clamp(60px, 6vw, 82px);
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  overflow: hidden;
  background: var(--client-primary-soft);
  outline: 2px solid transparent;
  outline-offset: 3px;
  transition: outline-color var(--dur-ui) var(--ease), transform var(--dur-ui) var(--ease);
}
.pt-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pt-empty { display: grid; place-items: center; font-family: var(--display); font-weight: 700; color: var(--client-primary-ink); }
.profile-thumbs button:hover .pt-img { transform: translateY(-2px); }
.profile-thumbs button[aria-pressed="true"] .pt-img { outline-color: var(--accent); }
.pt-name { font-size: var(--t-caption); font-weight: 600; color: color-mix(in srgb, var(--ink) 70%, transparent); }
.profile-thumbs button[aria-pressed="true"] .pt-name { color: var(--client-primary-ink); }

/* No JS: every profile in flow, thumbs hidden. */
.profile-stage:not(.profiles-on) .profile-panes { display: block; }
.profile-stage:not(.profiles-on) .profile-pane {
  opacity: 1; visibility: visible; transform: none; margin-bottom: 34px;
}
.profile-stage:not(.profiles-on) .profile-thumbs { display: none; }

@media (max-width: 820px) {
  .profile-pane { flex-direction: column; align-items: stretch; }
  .profile-media { width: min(100%, 400px); margin-inline: auto; }
  .profile-body { margin-left: 0; margin-top: -18px; width: min(94%, 460px); margin-inline: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .profile-pane, .profile-pane.is-on { transition: none; }
}

/* =====================================================================
   TESTIMONIAL CARDS, Connor 2026-08-11: solid white cards with a real
   shadow on the teal band (the glass treatment retired), ink text,
   typographic quotation marks on every quote, key phrases BOLD (the
   purple highlight band retired), and placeholder avatars instead of
   initials. The avatars are brand-tinted silhouette graphics, clearly
   placeholders: the no-stock-face / no-AI-face testimonial rule still
   holds, and real parent photos drop in when Sarah supplies them. -->
   ===================================================================== */
.field-dark .review {
  background: var(--card);
  border: 0;
  backdrop-filter: none;
  box-shadow: 0 18px 44px rgba(4, 26, 25, 0.45);
}
.field-dark .review.featured {
  background: var(--card);
  border: 0;
  box-shadow: 0 26px 60px rgba(4, 26, 25, 0.55);
}
.field-dark .review blockquote { color: var(--ink); }
.field-dark .review blockquote strong { font-weight: 700; color: var(--ink); }
.field-dark .review figcaption { color: color-mix(in srgb, var(--ink) 62%, transparent); }
.review blockquote::before { content: "\201C"; }
.review blockquote::after { content: "\201D"; }
.review-avatar {
  width: 40px; height: 40px;
  border-radius: 50%;
  display: block;
  flex-shrink: 0;
}
/* the retired purple band: markup now uses <strong>; rule kept inert
   in case an excerpt ever wants the marker look back */


/* =====================================================================
   THE 2026-08-13 LAYOUT RESTORE (Connor, Policy Owner)
   ---------------------------------------------------------------------
   Connor asked for the v1 one-pager's BELOW-FOLD flow back, keeping the
   current hero and the age-group fan. Three of that build's devices come
   back here, rebuilt in this brand rather than ported: the story block,
   the arc gallery, and the numbered why-cards.

   The v1 build is this division's stored anti-target
   (benchmarks/elevate-v1-REJECTED-fold-1440.png). Its failure was the
   FOLD (a serif column beside a contained photo on a pale page) and its
   flatness, not its section rhythm. The fold is untouched by this
   change, and every device below carries a depth treatment the original
   did not have (offset frame, real shadow, boundary overlap, scale
   break) so the restore cannot drift back into Q4 flatness.
   ===================================================================== */

/* --- 0. BUG FIX, .reveal-media never landed -------------------------
   PRE-EXISTING, SITE WIDE, found while measuring the story block on
   2026-08-13. Not introduced by this change.

   .reveal.in sets `transform: none` (line ~1355). .reveal-media.from-left
   and .from-right set their entrance offset (line ~1360). The two have
   IDENTICAL specificity (0,2,0), so the later rule wins and the offset
   survives .in. Every media panel on the site was therefore parked 20px
   to the side and 24px down from its own grid position, permanently, and
   the "enters from the side it belongs to" move never actually ran: the
   panel was just displaced and static.

   Verified with getComputedStyle after a real scroll: the matrix stayed
   matrix(1,0,0,1,-20,24) with .in applied, at 390 and at 1440.

   Fixed at 0,3,0 so it does not depend on source order the way the rule
   it corrects did. Under prefers-reduced-motion the existing !important
   block still wins, which is correct.

   NOTE: this nudges every split image on every page to its true grid
   position. That is a real visual change, deliberately made, and it is
   flagged in BRAND.md. */
.reveal-media.from-left.in,
.reveal-media.from-right.in { transform: none; }


/* --- 1. THE STORY BLOCK, homepage Problem ---------------------------
   Media left, narrative right, on a Z-RIGHT section. The offset frame
   behind the photo is the depth device (Standard 02 section 2,
   layering): a tinted rectangle pushed up and left so the photo reads
   as sitting in front of the page rather than punched into it. */
.story {
  display: grid;
  grid-template-columns: minmax(0, 44%) 1fr;
  gap: var(--gap-split);
  align-items: center;
}
.story-media { position: relative; margin: 0; }

/* The offset frame is a solid box-shadow on the PHOTO, not a pseudo
   element on the figure. The figure also holds the caption, so a
   figure-level frame stretched down behind the words and the caption
   ended up half on mint and half off it. A shadow is bound to the
   photo's own border box, follows its radius exactly, and is never
   clipped by its overflow. */
.story-frame {
  position: relative;
  z-index: 1;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--client-primary-soft);
  box-shadow:
    -18px -18px 0 0 var(--client-terminal),
    0 22px 48px rgba(14, 10, 18, 0.16);
}
.story-frame img { width: 100%; height: 100%; object-fit: cover; display: block; }

.story-kicker {
  position: relative;
  z-index: 1;
  margin: 26px 0 0;
  font-family: var(--display);
  font-style: italic;
  font-size: var(--t-lede);
  line-height: 1.45;
  color: var(--client-ramp-2);   /* teal ink, 6.85:1 on paper, documented */
  text-align: center;
}
.story-copy > :last-child { margin-bottom: 0; }
.story-lede { font-size: var(--t-lede); line-height: 1.55; }
.story-copy p { color: color-mix(in srgb, var(--ink) 70%, transparent); }
/* The closer is the one line the section is built to land. Ink at full
   strength against the 70pct body around it: a weight break, not a
   colour break, so it cannot compete with the CTA. */
.story-closer {
  color: var(--ink) !important;
  font-weight: 600;
}

@media (max-width: 900px) {
  .story { grid-template-columns: 1fr; }
  /* The frame offset would push the photo off the left gutter at narrow
     widths, so it comes in to 12px and the figure gets the room back. */
  .story-frame { margin-left: 12px; box-shadow: -12px -12px 0 0 var(--client-terminal), 0 16px 32px rgba(14, 10, 18, 0.14); }
  .story-kicker { margin-top: 20px; text-align: left; }
}


/* --- 2. THE ARC GALLERY, leads the Promise section ------------------
   Five portrait frames on a shallow arc, youngest to oldest left to
   right. It is the LEAD element of its section and crosses the boundary
   above it (design law C3 OVERLAP, via .overlaps-up on the wrapper).

   The arc is why this is not a second copy of the gallery band: that
   strip is a flat even grid near the top of the page, this one curves,
   rotates, scales its centre frame up and casts depth-graded shadows. */
.arc {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: clamp(10px, 1.3vw, 20px);
  align-items: start;
  /* translateY does not grow the row's box, so the two outer frames hang
     56px past it and land on the eyebrow underneath. The clearance has
     to carry that drop explicitly, not just a visual gap. */
  margin-bottom: calc(56px + clamp(28px, 3.5vw, 48px));
}
.arc figure {
  margin: 0;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--client-primary-soft);
  transform: translateY(var(--arc-y, 0)) rotate(var(--arc-r, 0deg)) scale(var(--arc-s, 1));
  box-shadow: 0 var(--arc-sh, 14px) calc(var(--arc-sh, 14px) * 2.4) rgba(14, 10, 18, 0.16);
}
.arc img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Outer frames sit lowest and lean outward; the centre frame is the one
   scale break in the row (Standard 02 section 2, scale contrast). */
.arc figure:nth-child(1) { --arc-y: 56px; --arc-r: -5deg;   --arc-s: .94; --arc-sh: 10px; }
.arc figure:nth-child(2) { --arc-y: 20px; --arc-r: -2.5deg; --arc-s: .98; --arc-sh: 14px; }
.arc figure:nth-child(3) { --arc-y: 0;    --arc-r: 0deg;    --arc-s: 1.06; --arc-sh: 22px; z-index: 2; }
.arc figure:nth-child(4) { --arc-y: 20px; --arc-r: 2.5deg;  --arc-s: .98; --arc-sh: 14px; }
.arc figure:nth-child(5) { --arc-y: 56px; --arc-r: 5deg;    --arc-s: .94; --arc-sh: 10px; }

/* Below 900 the arc would be five slivers. It becomes a 3-up strip and
   drops the two oldest frames rather than shrinking every frame past
   legibility. The dropped frames are decorative duplicates of imagery
   already on the programme pages, so nothing is lost. */
@media (max-width: 900px) {
  .arc { grid-template-columns: repeat(3, 1fr); margin-bottom: clamp(28px, 5vw, 44px); }
  .arc figure { transform: none; box-shadow: 0 10px 24px rgba(14, 10, 18, 0.14); }
  .arc figure:nth-child(4),
  .arc figure:nth-child(5) { display: none; }
}
@media (max-width: 560px) {
  .arc { grid-template-columns: repeat(2, 1fr); }
  .arc figure:nth-child(3) { display: none; }
}


/* --- 3. THE WHY CARDS, five reasons --------------------------------
   Three across, then two centred underneath, which is the v1
   arrangement. Six tracks make the centring structural instead of a
   hand-placed margin.

   Tints are the three fills already measured in BRAND.md; no new hex is
   introduced here, so no new contrast pair needs measuring. The numeral
   is muted ink at display scale, matching .step::before: nothing
   decorative wears the action colour (Standard 02 section 3.2). */
.why-cards {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--gap-card);
}
.why-card {
  grid-column: span 2;
  padding: var(--pad-card);
  border-radius: var(--radius);
  border: var(--hairline);
}
.why-card:nth-child(4) { grid-column: 2 / span 2; }
.why-card:nth-child(5) { grid-column: 4 / span 2; }

.why-card:nth-child(1),
.why-card:nth-child(4) { background: var(--client-primary-soft); }  /* ink 15.07:1 */
.why-card:nth-child(2),
.why-card:nth-child(5) { background: var(--client-terminal); }      /* mint tint */
.why-card:nth-child(3) { background: var(--paper-tint); }           /* ink 70pct 7.23:1 */

.why-num {
  display: block;
  font-family: var(--display);
  font-size: clamp(1.9rem, 2.6vw, 2.4rem);
  font-weight: 700;
  line-height: 1;
  margin-bottom: .5rem;
  color: color-mix(in srgb, var(--ink) 34%, transparent);
}
.why-card h3 { margin-bottom: .4em; }
.why-card p {
  margin: 0;
  font-size: var(--t-small);
  color: color-mix(in srgb, var(--ink) 70%, transparent);
}

@media (max-width: 980px) {
  .why-cards { grid-template-columns: repeat(4, 1fr); }
  .why-card,
  .why-card:nth-child(4),
  .why-card:nth-child(5) { grid-column: span 2; }
}
@media (max-width: 640px) {
  .why-cards { grid-template-columns: 1fr; }
  .why-card,
  .why-card:nth-child(4),
  .why-card:nth-child(5) { grid-column: auto; }
}


/* --- 4. THE RECREATIONAL NOTE, closes the Classes section -----------
   Carries the two facts the cut Value list was holding (no auditions,
   every dancer performs) so removing that section loses nothing. */
.rec-note {
  max-width: 62ch;
  margin: clamp(24px, 3vw, 36px) auto 0;
  padding: 16px 26px;
  border-radius: 999px;
  background: var(--card);
  border: var(--hairline);
  text-align: center;
  font-size: var(--t-small);
  color: color-mix(in srgb, var(--ink) 70%, transparent);
}
.rec-note strong { color: var(--ink); font-weight: 600; }

@media (max-width: 560px) {
  .rec-note { border-radius: var(--radius); padding: 16px 20px; }
}


/* --- 5. PROCESS AS THE TERMINAL SECTION -----------------------------
   The Final CTA band is removed on Connor's direction (2026-08-13), so
   Process is now the page's last content section and carries the
   terminal zone. It needs the closing weight the band used to provide:
   a little more bottom room and a CTA at terminal size. */
.section.process[data-anchor="terminal"] { padding-bottom: calc(var(--pad-section) + 16px); }
.section.process[data-anchor="terminal"] .cta-btn {
  min-height: 58px;
  font-size: 1.1rem;
  padding-inline: 40px;
}

/* =====================================================================
   SPRING SHOWCASE: the stage mosaic (showcase.html). Five 2026 stage
   photos, one per age group, added 2026-08-18 on Connor's direction.

   It sits on the dark band on purpose. Every source is a lit stage
   against a black backdrop, so on the paper tint the frames read as
   holes punched in the page; on the deep plum they read as the house
   lights going down, and the stage lighting inside each photo becomes
   the only light in the section.

   The two rows do NOT share an aspect. Forcing one would either crop
   the pointe portrait to a square or cut dancers off the ends of the
   wide line shots, and on a stage photo a dancer clipped at the frame
   edge is the first thing the eye finds. So each figure keeps its own
   ratio and the split row aligns to the top instead of stretching.
   ===================================================================== */
.stage-mosaic { display: grid; gap: clamp(12px, 1.4vw, 22px); }
.stage-mosaic .sm-pair {
  display: grid; gap: clamp(12px, 1.4vw, 22px);
  grid-template-columns: 1fr 1fr;
}
.stage-mosaic .sm-split {
  display: grid; gap: clamp(12px, 1.4vw, 22px);
  grid-template-columns: 5fr 7fr;
  align-items: start;
}
.stage-shot {
  position: relative; margin: 0; overflow: hidden;
  border-radius: var(--radius);
  background: #150C1A;
}
.stage-shot > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.stage-shot.sh-wide { aspect-ratio: 3 / 2; }
.stage-shot.sh-tall { aspect-ratio: 3 / 4; }
.stage-shot.sh-full { aspect-ratio: 16 / 9; }
/* Caption scrim. Bottom-weighted only: the stage lighting in these
   frames comes from above, so darkening the top would fight the photo. */
.stage-shot::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 58%;
  background: linear-gradient(0deg, rgba(11,6,14,0.86) 0%, rgba(11,6,14,0.45) 42%, rgba(11,6,14,0) 100%);
  pointer-events: none;
}
.stage-shot figcaption {
  position: absolute; inset: auto 0 0 0; z-index: 2;
  padding: clamp(14px, 1.8vw, 24px);
  display: grid; gap: 4px;
}
.stage-shot .sc-age {
  font-family: var(--body); font-weight: 600;
  font-size: var(--t-eyebrow); line-height: 1; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--client-terminal);
}
.stage-shot .sc-note {
  font-family: var(--display); font-weight: 600;
  font-size: clamp(15px, 1.05vw, 18px); line-height: 1.32;
  color: #fff;
}
@media (max-width: 860px) {
  .stage-mosaic .sm-pair,
  .stage-mosaic .sm-split { grid-template-columns: 1fr; }
  .stage-shot.sh-tall { aspect-ratio: 4 / 5; }
  .stage-shot.sh-full { aspect-ratio: 3 / 2; }
}

/* band-dark carries a section for the first time on showcase.html, and the
   header stack does not inherit its colour: .sec-head h2 and .answer both
   set their own ink, so on the plum band they rendered near-black on
   near-black. Contrast measured against --client-band-dark #241329:
   white 17.50:1, white 84pct 12.51:1 (clears the 7:1 body floor),
   terminal #D8EEE2 14.37:1. */
.section.band-dark .sec-head h2 { color: #fff; }
.section.band-dark .sec-head .answer { color: rgba(255,255,255,0.84); }
.section.band-dark .sec-head .eyebrow { color: var(--client-terminal); }

/* The split row: the pointe portrait is taller than the teen line shot by
   design (each frame keeps its own ratio rather than being cropped square),
   which leaves dead band beside it. A note fills that column instead of a
   stretched crop that would cut the end dancers off the line. */
.stage-mosaic .sm-split { grid-template-rows: auto 1fr; }
.stage-mosaic .sm-split > .sh-tall { grid-column: 1; grid-row: 1 / span 2; }
.stage-mosaic .sm-split > .sh-wide { grid-column: 2; grid-row: 1; }
.stage-mosaic .sm-note {
  grid-column: 2; grid-row: 2;
  display: grid; gap: 10px; align-content: center;
  padding: clamp(18px, 2.2vw, 30px);
  border: 1px solid rgba(255,255,255,0.16);
  border-radius: var(--radius);
}
.stage-mosaic .sm-note p { margin: 0; color: rgba(255,255,255,0.84); }
.stage-mosaic .sm-note a { color: var(--client-terminal); text-underline-offset: 3px; }
@media (max-width: 860px) {
  .stage-mosaic .sm-split > .sh-tall,
  .stage-mosaic .sm-split > .sh-wide,
  .stage-mosaic .sm-note { grid-column: auto; grid-row: auto; }
}

/* A page hero with no image. showcase.html, 2026-08-18: Connor cut the
   opening photograph so the page starts on the question instead. The
   two-column grid would otherwise leave the copy stranded in a 56pct
   column beside a hole, so the text takes the full width and is held to
   a readable measure. */
.page-hero.page-hero-text { grid-template-columns: minmax(0, 1fr); }
.page-hero.page-hero-text > div { max-width: 62ch; }

/* 2026-08-19, Connor's layout audit of the inner pages.
   1. THE SEAM. A page-hero's 70px bottom padding plus the next section's
      full --pad-section top stacked into a ~180px dead band between the
      hero and the breadcrumb on every inner page (schedule was the named
      offender). The pair is collapsed at the seam.
   2. THE LETTERBOX. .page-hero-media forces 16/11 but schedule-band is
      21:9, so the frame showed a solid pink bed under the photo, more
      dead space directly under the hero image. Cover-fit crops to fill.
   Z-alternation on data-dense inner sections was also retired page by
   page (data-anchor flipped to z-left) so headers sit flush over their
   full-width content instead of floating beside emptiness. */
.page-hero + .section { padding-top: clamp(24px, 2.5vw, 44px); }
.page-hero-media img { width: 100%; height: 100%; object-fit: cover; }

/* The showcase fee strip sits as the right cell of a 2-col row; stacked
   vertically it fills the cell instead of wrapping into a shallow line. */
.info-strip.stack { flex-direction: column; gap: 14px; height: 100%; justify-content: center; }
.grid-2.align-start { align-items: start; }

/* showcase.html opens straight into its first section rather than through a
   page-hero, so it needs the nav clearance a .page-hero used to provide.
   Padding-top only: the section's own bottom padding still hands off. */
.section.page-open { padding-top: clamp(104px, 84px + 4vw, 150px); }
.section.page-open .sec-head h1 { margin-bottom: .35em; }

/* ---- Free-trial form, SOP E additions ------------------------------------
   Ported with the form wiring so the static build keeps the lead capture that
   was already set up. Two classes the markup now needs. */

/* Layer 1a. The honeypot must be invisible to people and present to bots, so
   it is removed from the layout and from the a11y tree (aria-hidden and
   tabindex="-1" are on the markup) WITHOUT display:none — a field that is
   never rendered is one some crawlers skip, and then it catches nothing.
   Same clip pattern as .visually-hidden above. */
.hp-field {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap; border: 0;
}

/* Server-side messages. Hidden until there is something to say, so a screen
   reader is not handed an empty live region on load. */
.form-status {
  margin-top: .75rem;
  font-size: .95rem;
  line-height: 1.45;
}
.form-status[hidden] { display: none; }

/* Measured against --client-paper #FAF8FB = 7.74:1, which clears the 7:1
   body floor this file holds itself to. Re-measure if the paper changes. */
.form-status.is-error { color: #9B1C15; }

/* Turnstile renders a fixed 300x65 iframe; give it room so the submit button
   does not jump when the widget resolves. */
.form-lead .cf-turnstile { margin: 1rem 0 .25rem; min-height: 65px; }

/* Go Studio Pro schedule + registration embed. Copied verbatim from the Next
   build's stylesheet so the widget sizes identically on both. */
/* margin-bottom matches the top: the embed had only a top margin, so the
   "Monday" heading sat hard against the widget's bottom border (the later
   day headings carry .mt-sub and were fine, Monday is the one that does
   not). Spacing the BOX rather than adding a class to that one heading
   keeps it correct whatever follows the embed. Client note 2026-08-28. */
.schedule-embed { margin-top: var(--gap-split); margin-bottom: var(--gap-split); border: var(--hairline); border-radius: var(--radius); background: var(--card); overflow: hidden; }
.schedule-embed iframe { border: 0; width: 100%; height: clamp(560px, 88vh, 1100px); display: block; }

/* Footer credit link. Copied from the Next build's stylesheet so the link
   reads as a link in the dark footer band without shouting. */
.foot-credit { min-height: 0; font-size: inherit; color: #ffffffd1; text-decoration: none; display: inline;
  border-bottom: 1px solid #ffffff57; transition: color var(--dur-ui) var(--ease), border-color var(--dur-ui) var(--ease); }
.foot-credit:hover, .footer a.foot-credit:focus-visible { color: #fff; border-bottom-color: #ffffffb3; text-decoration: none; }
