/* ==========================================================================
   showpiece.css — /showpiece-style starter
   Tokens (references/showpiece-style.md §9.2) and the core components the SP
   patterns use (references/patterns.md). Plain CSS, no external URLs.

   Rules this file keeps (from /web-design-and-build):
   - Content is visible with no JavaScript: every start state is scoped to
     .js-gsap (added by site-motion.js only when GSAP AND ScrollTrigger
     loaded) AND to prefers-reduced-motion: no-preference AND to the motion
     switch being on.
   - First-screen layout never waits for .js-gsap (it lands after first
     paint). A scene that can be in the first screen (SP-SPLIT-WIPE hero
     variant) keys its layout to media queries + .js, which motion-pref.js
     sets in <head> before first paint (patterns.md rule 3).
   - Transform and opacity only; static clip-path shapes only (wipes are
     transform masks, never a per-frame clip-path tween); no layout animation.
   - Reduced motion and [data-motion="off"] on <html> get the same still page.
   - Oversized type is clipped per section (overflow-x: clip), never on body.

   Breakpoints: ONE desktop breakpoint, BP (--sp-bp). Phone ≤ 767,
   tablet 768 to BP-1, desktop ≥ BP (pins, scrubs, cursor effects).
   Default BP = 1024. Media queries can't read custom properties, so every
   rule that depends on BP sits in §20 "Desktop (BP)" at the end of this
   file, under two adjacent queries: that is the one place to change.
   Blue Digital house site (gsap-options.md §2: desktop ≥ 981, tablet
   768–980, phone ≤ 767; the house file wins): use the house override
   block in §20 and set BP = 981 in site-motion.js.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens — replace the placeholder values with the client's (§9.3).
   Placeholders are a neutral grey ramp with a mid-blue accent so nothing
   reads as any reference brand.
   -------------------------------------------------------------------------- */
:root{
  /* colour roles (§3), bMember (Checkpoint 2 v2 token table). Demo brands override these in bmember.css */
  --sp-ink:        #0B1730;   /* darkest text on light */
  --sp-paper:      #F6F7FB;   /* lightest ground, text on dark */
  --sp-surface:    #EBEFF7;   /* alternate light band, cards */
  --sp-stage:      #002B64;   /* dark bands: hero, exploded platform, footer (bMember navy) */
  --sp-accent:     #E31825;   /* the ONE accent: buttons, accent word, active (white on it 4.75:1) */
  --sp-accent-2:   #B5121D;   /* accent hover / pressed */
  --sp-on-accent:  #FFFFFF;
  --sp-accent-text:#C41422;   /* accent as small text on paper (≥ 4.5:1) */
  --sp-accent-dark:#E31825;   /* brand red on the dark stage (client's choice, 2 Nov 2026): 2.89:1 on navy, so display-size text only */
  --sp-on-accent-dark:#FFFFFF; /* text on that red: 4.75:1 */
  --sp-ink-muted:  #4A5263;   /* secondary text on paper (7.3:1) */
  --sp-paper-muted:#B9C6E0;   /* secondary text on the stage (8.0:1) */
  --sp-line:       rgb(11 23 48 / .14);
  --sp-line-on-dark: rgb(246 247 251 / .2);
  --sp-overlay:    rgb(0 0 0 / .45);
  --sp-glass:      rgb(246 247 251 / .62);

  /* current band (switched by .sp-band--dark) */
  --sp-bg: var(--sp-paper);
  --sp-fg: var(--sp-ink);
  --sp-focus: var(--sp-fg);

  /* type families (§2.4, pairing A): grotesk display + serif italic accent; Manrope text (Inter avoided:
     raycast and linear, the anchor references, both use it) */
  --sp-font-display: "Poppins", system-ui, sans-serif;
  --sp-font-accent:  "Fraunces", Georgia, serif;
  --sp-font-text:    "Manrope", system-ui, sans-serif;
  --sp-font-mono:    "Manrope", system-ui, sans-serif;  /* labels in the text face: ≤ 3 families */
  --sp-display-case: none;
  --sp-display-weight: 700;

  /* fluid type scale (§2.1): floor @390, ceiling @1920, rem in the middle term so zoom scales it */
  --sp-fs-mega:       clamp(7rem, 2.92rem + 16.73vw, 23rem);   /* 112 → 368px, once per page */
  --sp-fs-display-xl: clamp(4.5rem, 1.82rem + 10.98vw, 15rem); /* 72 → 240px */
  --sp-fs-display:    clamp(3rem, 1.53rem + 6.01vw, 8.75rem);  /* 48 → 140px */
  --sp-fs-h2:         clamp(2.25rem, 1.49rem + 3.14vw, 5.25rem);/* 36 → 84px */
  --sp-fs-h3:         clamp(1.5rem, 1.18rem + 1.31vw, 2.75rem);/* 24 → 44px */
  --sp-fs-lede:       clamp(1.25rem, 1.12rem + .52vw, 1.75rem);/* 20 → 28px */
  --sp-fs-body:       clamp(1rem, .95rem + .2vw, 1.1875rem);   /* 16 → 19px, floor 16 */
  --sp-fs-label:      clamp(.75rem, .72rem + .13vw, .875rem);  /* 12 → 14px, floor 12 */

  /* grid and spacing (§4) */
  --sp-margin:  clamp(1rem, .5rem + 3.3vw, 4.5rem);
  --sp-gap:     clamp(.75rem, .5rem + 1vw, 2rem);
  --sp-max:     1440px;
  --sp-measure: 68ch;
  --sp-narrow:  40rem;
  --sp-pad-s:   clamp(3rem, 2rem + 4vw, 6rem);
  --sp-pad-m:   clamp(4rem, 2.5rem + 6vw, 9rem);
  --sp-pad-l:   clamp(6rem, 3rem + 10vw, 15rem);
  --sp-bp:      1024px;  /* desktop breakpoint, for reference only: media queries can't read it.
                            Change it in §20 (CSS) and BP (site-motion.js). House sites: 981px */

  /* fixed header and pins (patterns.md rule 5) */
  --sp-header-h:   64px;   /* height of the fixed header bar */
  --sp-header-gap: 12px;   /* its inset from the top of the viewport (20px ≥ BP, §20) */
  --sp-pin-top: calc(var(--sp-header-h) + var(--sp-header-gap)); /* band the fixed header covers: pins
                            start below it and pinned screens are 100svh minus it. 0px if the header isn't fixed */

  /* radius (§4.4): ONE family. Soft shown; square = 0/2/4px, large = 24–60px */
  --sp-r-sm:   8px;
  --sp-r-md:   16px;
  --sp-r-lg:   clamp(20px, 2.4vw, 32px);
  --sp-r-pill: 999px;
  --sp-notch:  clamp(14px, 1.6vw, 28px);  /* corner cut size for SP-NOTCH */

  /* motion tokens (gsap-motion-system.md §1–§2), shared with site-motion.js T */
  --sp-ease-out:        cubic-bezier(.215,.61,.355,1);  /* out: power3.out */
  --sp-ease-in-out:     cubic-bezier(.645,.045,.355,1); /* inOut: power3.inOut */
  --sp-ease-out-strong: cubic-bezier(.16,1,.3,1);       /* outStrong: expo.out */
  --sp-ease-ui:         cubic-bezier(.25,.46,.45,.94);  /* ui: power2.out */
  --sp-ease-pop:        cubic-bezier(.34,1.56,.64,1);   /* pop: back.out(1.6), buttons/badges only */
  --sp-dur-xs: .2s; --sp-dur-sm: .4s; --sp-dur-md: .6s; --sp-dur-lg: .9s; --sp-dur-xl: 1.2s;
  /* Optional brand curve (one CustomEase replacing outStrong), four numbers, e.g. .65,.05,0,1.
     site-motion.js registers it; set --sp-ease-out-strong to cubic-bezier(<same numbers>) too. */
  --sp-ease-brand: ;
}

/* Dark band: swaps roles, adds no colours (§3.3) */
/* --sp-focus is re-declared on every band: a var() set on :root resolves there (to ink) and would
   otherwise draw ink focus rings on dark bands */
.sp-band{ background:var(--sp-bg); color:var(--sp-fg); --sp-focus: var(--sp-fg); }
.sp-band--dark{
  --sp-bg: var(--sp-stage); --sp-fg: var(--sp-paper); --sp-accent-text: var(--sp-accent-dark);
  --sp-ink-muted: var(--sp-paper-muted); --sp-line: var(--sp-line-on-dark);
  --sp-glass: rgb(0 20 50 / .55);
}
.sp-band--surface{ --sp-bg: var(--sp-surface); }

/* --------------------------------------------------------------------------
   2. Base and type
   -------------------------------------------------------------------------- */
*,*::before,*::after{ box-sizing:border-box; }   /* padding stays inside 100svh pinned screens */
html{ -webkit-text-size-adjust:100%; text-size-adjust:100%; }
body{
  margin:0; background:var(--sp-bg); color:var(--sp-fg);
  font:400 var(--sp-fs-body)/1.55 var(--sp-font-text);
  -webkit-font-smoothing:antialiased;
}
img,video,svg,canvas{ display:block; max-width:100%; }
img,video{ height:auto; }

.sp-display-xl,.sp-display,.sp-mega{
  font-family:var(--sp-font-display); font-weight:var(--sp-display-weight);
  text-transform:var(--sp-display-case); text-wrap:balance; margin:0;
}
.sp-mega      { font-size:var(--sp-fs-mega);       line-height:.84; letter-spacing:-.04em; }
.sp-display-xl{ font-size:var(--sp-fs-display-xl); line-height:.86; letter-spacing:-.03em; }
.sp-display   { font-size:var(--sp-fs-display);    line-height:.92; letter-spacing:-.02em; }
.sp-h2{ font:var(--sp-display-weight) var(--sp-fs-h2)/1 var(--sp-font-display); letter-spacing:-.02em; text-wrap:balance; margin:0 0 .4em; }
.sp-h3{ font:var(--sp-display-weight) var(--sp-fs-h3)/1.08 var(--sp-font-display); letter-spacing:-.01em; margin:0 0 .4em; }
.sp-lede{ font-size:var(--sp-fs-lede); line-height:1.3; max-width:var(--sp-measure); }
.sp-label{
  font:500 var(--sp-fs-label)/1.25 var(--sp-font-mono);
  letter-spacing:.08em; text-transform:uppercase; color:var(--sp-ink-muted);
}
/* Uppercase display opens up slightly (oryzo +.018em); set on the element */
.sp-upper{ text-transform:uppercase; letter-spacing:-.005em; }
.sp-dim{ color:var(--sp-ink-muted); }              /* SP-INK dimmed tail: still ≥ 4.5:1 */
.sp-measure{ max-width:var(--sp-measure); }

/* Sections that hold oversized type or tracks never scroll the page sideways */
.sp-section{ position:relative; padding-block:var(--sp-pad-m); overflow-x:clip; }
.sp-section--s{ padding-block:var(--sp-pad-s); }
.sp-section--l{ padding-block:var(--sp-pad-l); }

/* Grid and containers (§4.2) */
.sp-wrap{ width:min(100% - 2 * var(--sp-margin), var(--sp-max)); margin-inline:auto; }
.sp-grid{ display:grid; grid-template-columns:repeat(12,minmax(0,1fr)); column-gap:var(--sp-gap); row-gap:var(--sp-gap); }
@media (max-width:767px){ .sp-grid > *{ grid-column:1 / -1; } }

.sr-only{ position:absolute!important; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }

/* --------------------------------------------------------------------------
   3. Focus — every hover state also on :focus-visible
   -------------------------------------------------------------------------- */
:focus-visible{ outline:2px solid var(--sp-focus); outline-offset:3px; }
/* clip-path cuts outlines: clipped shapes draw focus with an inset box-shadow */
.sp-notch:focus-visible,
.sp-card--outline:focus-visible,
.sp-corner:focus-visible,
[data-clip]:focus-visible{
  outline:none;
  box-shadow:inset 0 0 0 2px var(--sp-focus), inset 0 0 0 4px var(--sp-bg);
}
.sp-card--outline:focus-within:not(:focus-visible){ box-shadow:inset 0 0 0 2px var(--sp-focus); }

/* --------------------------------------------------------------------------
   4. Buttons, pause control
   -------------------------------------------------------------------------- */
.sp-btn{
  display:inline-flex; align-items:center; gap:.6em; min-height:44px; padding:.8em 1.4em;
  font:600 var(--sp-fs-label)/1 var(--sp-font-text); letter-spacing:.04em; text-transform:uppercase;
  border-radius:var(--sp-r-pill); border:1px solid transparent; text-decoration:none; cursor:pointer;
  background:var(--sp-accent); color:var(--sp-on-accent);
  transition:background-color var(--sp-dur-xs) var(--sp-ease-ui), color var(--sp-dur-xs) var(--sp-ease-ui), transform var(--sp-dur-xs) var(--sp-ease-ui);
}
.sp-btn:hover,.sp-btn:focus-visible{ background:var(--sp-accent-2); }
.sp-btn--ghost{ background:transparent; color:var(--sp-fg); border-color:var(--sp-line); }
.sp-btn--ghost:hover,.sp-btn--ghost:focus-visible{ background:var(--sp-fg); color:var(--sp-bg); }

/* Pause/play for every loop or video > 5 s (patterns.md rule 1) */
.sp-pause{
  display:inline-grid; place-items:center; width:44px; height:44px; border-radius:50%;
  border:1px solid var(--sp-line); background:var(--sp-glass); color:var(--sp-fg); cursor:pointer;
}
.sp-pause[aria-pressed="true"]{ background:var(--sp-fg); color:var(--sp-bg); }

/* --------------------------------------------------------------------------
   5. Mixed headline — SP-TYPE-MIX (.sp-mix)
   -------------------------------------------------------------------------- */
.sp-mix{
  font-family:var(--sp-font-display); font-weight:var(--sp-display-weight);
  font-size:var(--sp-fs-display); line-height:.9; letter-spacing:-.01em;
  text-transform:var(--sp-display-case); text-wrap:balance; margin:0;
}
.sp-mix__accent{
  font-family:var(--sp-font-accent); font-style:normal; font-weight:400;
  font-size:1.04em;              /* optical match (landonorris 1.04, oryzo 1.02) */
  letter-spacing:-.023em; text-transform:none; color:var(--sp-accent-text);
}
.sp-mix__accent--italic{ font-style:italic; }

/* Manifesto lines — SP-STATEMENT-DRIFT (.sp-drift) */
.sp-drift{ overflow-x:clip; text-align:center; }
.sp-drift__line{ display:block; }   /* nowrap at desktop: §20 */

/* --------------------------------------------------------------------------
   6. Sticky stage — .sp-stage (track) > .sp-stage__sticky (one screen)
   Used by SP-HERO-STICKY-PORTRAIT, SP-SPLIT-WIPE, SP-REEL-GROW,
   SP-PERSP-UNFOLD. Scrubbed stages count as pins: 200vh default, 300vh max.
   With no JS, on phones and under reduced motion it is a normal section.
   Desktop layout: §20.
   -------------------------------------------------------------------------- */
.sp-stage{ position:relative; }
.sp-stage__sticky{ position:relative; overflow:clip; }

/* Split wipe — SP-SPLIT-WIPE (open and stacked by default) */
.sp-wipe__panels{ display:grid; gap:var(--sp-gap); }
.sp-wipe__panel{ position:relative; display:block; overflow:hidden; color:inherit; text-decoration:none; }
.sp-wipe__inner{ display:block; overflow:hidden; overflow:clip; height:100%; }   /* moves; its media counter-moves */
.sp-wipe__panel img,.sp-wipe__panel video{ width:100%; height:100%; object-fit:cover; aspect-ratio:4/5; }
.sp-wipe__title{ text-align:center; margin-block:0 var(--sp-gap); }
/* giant decorative line (aria-hidden) that slides across; only exists while a scrub can move it (§20) */
.sp-wipe__line{ display:none; }

/* Split wipe, hero variant — SP-SPLIT-WIPE (aventuradental). The H1 and the hero
   image (LCP) never move: the paper panel slides out and the giant line travels.
   Stacked by default (title, panel text, image). On desktop with motion allowed
   the split layout is painted from the first frame, keyed to .js (motion-pref.js),
   never to .js-gsap (§20); site-motion.js 'split-hero' only adds the scrub.
   <section class="sp-wipe-hero sp-stage" data-motion="split-hero" aria-labelledby="hero-title">
     <div class="sp-wipe-hero__stage sp-stage__sticky">
       <h1 class="sp-wipe-hero__title" id="hero-title">…</h1>
       <div class="sp-wipe-hero__panel">eyebrow, tagline, CTA</div>
       <picture class="sp-wipe-hero__media"><img … fetchpriority="high"></picture>
       <p class="sp-wipe__line" aria-hidden="true">Brand</p>
     </div>
   </section> */
.sp-wipe-hero{ overflow-x:clip; }
.sp-wipe-hero__stage{ display:grid; gap:var(--sp-gap); padding-top:calc(var(--sp-pin-top) + clamp(24px, 4vw, 56px)); }
.sp-wipe-hero__title{
  margin:0; padding-inline:var(--sp-margin); font:var(--sp-display-weight) var(--sp-fs-display)/.86 var(--sp-font-display);
  letter-spacing:-.03em; text-wrap:balance; text-transform:var(--sp-display-case);
}
.sp-wipe-hero__panel{ display:grid; gap:14px; justify-items:start; align-content:end; padding-inline:var(--sp-margin); }
.sp-wipe-hero__media img{ width:100%; aspect-ratio:4/5; object-fit:cover; }
@media (min-width:768px){ .sp-wipe-hero__media img{ aspect-ratio:16/9; } }

/* Reel / quote grows to full bleed — SP-REEL-GROW */
.sp-grow__media{ clip-path:inset(0 round var(--sp-r-lg)); }   /* static rounding: scale, never border-radius */
.sp-grow__media img,.sp-grow__media video{ width:100%; height:100%; object-fit:cover; aspect-ratio:16/9; }
.sp-grow__copy{ margin-top:var(--sp-gap); }

/* --------------------------------------------------------------------------
   7. Chapters — SP-CHAPTERS (stacked by default; one pinned screen on desktop, §20)
   The pinned element is .sp-ch itself. The pin starts below the fixed header
   (scroll-margin-top, read by site-motion.js), and the pinned screen is
   100svh minus that band, padding included (box-sizing reset, §2).
   Without ScrollTrigger there is no .js-gsap, so the chapters stay stacked.
   -------------------------------------------------------------------------- */
.sp-ch{ position:relative; scroll-margin-top:var(--sp-pin-top); }
.sp-ch__item{ display:grid; gap:var(--sp-gap); align-items:center; padding-block:var(--sp-pad-s); }
.sp-ch__media img{ width:100%; height:auto; border-radius:var(--sp-r-md); }
.sp-ch__bar{ display:block; height:2px; background:var(--sp-accent); transform-origin:left; }  /* progress-list variant: full when static */
@media (min-width:768px){ .sp-ch__item{ grid-template-columns:1fr 1fr; } }
/* local progress rail inside a chapter section (scrubbed by site-motion.js); shown only in §20 */
.sp-ch .sp-rail{ display:none; position:absolute; right:var(--sp-margin); top:50%; translate:0 -50%; width:2px; height:120px; background:var(--sp-line); }
.sp-ch .sp-rail__bar{ display:block; width:100%; height:100%; background:var(--sp-accent); transform-origin:top; }

/* --------------------------------------------------------------------------
   8. Pinned horizontal gallery — SP-PIN-HSCROLL (a list until desktop motion, §20)
   -------------------------------------------------------------------------- */
.sp-hscroll{ overflow-x:clip; scroll-margin-top:var(--sp-pin-top); }
.sp-hscroll__track{ display:grid; gap:var(--sp-gap); grid-template-columns:1fr; list-style:none; margin:0; padding:0; }
@media (min-width:600px){ .sp-hscroll__track{ grid-template-columns:1fr 1fr; } }
.sp-hscroll__item figcaption{ margin-top:.5em; }

/* --------------------------------------------------------------------------
   9. Offset columns — SP-GRID-OFFSET (offset is static CSS; drift is JS)
   -------------------------------------------------------------------------- */
.sp-goff{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--sp-gap); align-items:start; }
.sp-goff__col{ display:grid; gap:var(--sp-gap); }
.sp-goff__col:nth-child(even){ padding-top:50%; }   /* % of the column width = half a square card; 4 columns ≥ BP (§20) */

/* --------------------------------------------------------------------------
   10. Notch and one-corner shapes — SP-NOTCH (one shape language per site)
   -------------------------------------------------------------------------- */
.sp-notch{       clip-path:polygon(0 0,100% 0,100% calc(100% - var(--sp-notch)),calc(100% - var(--sp-notch)) 100%,0 100%); }
.sp-notch--tl{   clip-path:polygon(var(--sp-notch) 0,100% 0,100% 100%,0 100%,0 var(--sp-notch)); }
.sp-notch--tr{   clip-path:polygon(0 0,calc(100% - var(--sp-notch)) 0,100% var(--sp-notch),100% 100%,0 100%); }
/* raised tab on a section or footer top edge (landonorris footer, terminal sections) */
.sp-notch--tab{
  --tab-x: var(--sp-margin); --tab-w: clamp(160px, 22vw, 360px); --tab-h: var(--sp-notch);
  clip-path:polygon(0 var(--tab-h), var(--tab-x) var(--tab-h), calc(var(--tab-x) + var(--tab-h)) 0,
    calc(var(--tab-x) + var(--tab-w) - var(--tab-h)) 0, calc(var(--tab-x) + var(--tab-w)) var(--tab-h),
    100% var(--tab-h), 100% 100%, 0 100%);
  padding-top:calc(var(--tab-h) + var(--sp-pad-s));
}
.sp-corner{    border-radius:0 0 0 20px; }   /* serpentine: one rounded corner */
.sp-corner--sm{ border-radius:0 0 0 10px; }

/* Outline card with a notched corner (landonorris grid) */
.sp-card--outline{
  position:relative; display:block; color:inherit; text-decoration:none;
  border:1px solid var(--sp-line); padding:clamp(1rem, 1.5vw, 1.5rem);
  clip-path:polygon(0 0,100% 0,100% calc(100% - var(--sp-notch)),calc(100% - var(--sp-notch)) 100%,0 100%);
}
.sp-card--outline::after{   /* draws the diagonal edge the clip removed */
  content:""; position:absolute; right:-1px; bottom:-1px; width:var(--sp-notch); height:var(--sp-notch);
  background:linear-gradient(to top left, transparent calc(50% - .75px), var(--sp-line) calc(50% - .75px), var(--sp-line) calc(50% + .75px), transparent calc(50% + .75px));
  pointer-events:none;
}
.sp-card__media{ overflow:hidden; aspect-ratio:1; }
.sp-card__media img{ width:100%; height:100%; object-fit:contain; transition:transform var(--sp-dur-sm) var(--sp-ease-out); }
.sp-card--outline:hover .sp-card__media img,
.sp-card--outline:focus-visible .sp-card__media img{ transform:scale(1.04); }
.sp-card__meta{ display:flex; justify-content:space-between; gap:1em; margin-top:.75em; }

/* --------------------------------------------------------------------------
   11. Glass header — SP-HDR-GLASS (solid fallback first)
   -------------------------------------------------------------------------- */
.sp-hdr-glass{
  position:fixed; z-index:40; top:max(var(--sp-header-gap), env(safe-area-inset-top)); left:var(--sp-header-gap); right:var(--sp-header-gap);
  min-height:var(--sp-header-h); display:flex; align-items:center; justify-content:space-between; gap:1rem;
  padding-inline:clamp(12px, 1.5vw, 24px); border-radius:var(--sp-r-sm);
  background:var(--sp-bg); color:var(--sp-fg);            /* solid fallback */
  box-shadow:inset 0 0 0 1px var(--sp-line);
  transition:transform var(--sp-dur-sm) var(--sp-ease-ui), background-color var(--sp-dur-sm) var(--sp-ease-ui);
}
@supports ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){
  .sp-hdr-glass{ background:var(--sp-glass); -webkit-backdrop-filter:blur(6px) saturate(1.2); backdrop-filter:blur(6px) saturate(1.2); }
}
@media (prefers-reduced-transparency:reduce){
  .sp-hdr-glass{ background:var(--sp-bg); -webkit-backdrop-filter:none; backdrop-filter:none; }
}
/* S2 collapse: class set by site.js / ScrollTrigger; height never changes (CLS) */
.sp-hdr-glass.is-hidden{ transform:translateY(calc(-100% - 24px)); }
.sp-hdr-glass:focus-within{ transform:none; }

/* --------------------------------------------------------------------------
   12. Page progress rail — SP-RAIL
   CSS scroll timeline where supported; site-motion.js 'rail' pattern scrubs
   it with ScrollTrigger where not. Hidden on phones, under reduced motion,
   with the motion switch off, and when neither engine is available.
   -------------------------------------------------------------------------- */
.sp-rail--page{
  display:none; position:fixed; z-index:30; left:clamp(8px, 1vw, 20px); top:50%; translate:0 -50%;
  width:6px; height:86px; border-radius:3px; background:var(--sp-line); overflow:hidden; pointer-events:none;
}
.sp-rail--page .sp-rail__bar{ display:block; width:100%; height:100%; background:var(--sp-accent); transform-origin:top; transform:scaleY(0); }
/* shown in §20 */
@keyframes sp-rail{ from{ transform:scaleY(0); } to{ transform:scaleY(1); } }

/* --------------------------------------------------------------------------
   13. Marquee — S10 (first copy readable, duplicate aria-hidden)
   <div class="sp-marquee"><div class="sp-marquee__track">
     <ul class="sp-marquee__group">…</ul><ul class="sp-marquee__group" aria-hidden="true">…</ul>
   </div><button class="sp-pause" aria-pressed="false">…</button></div>
   site.js adds .is-paused (button) and .is-offscreen (IntersectionObserver).
   -------------------------------------------------------------------------- */
.sp-marquee{ position:relative; overflow:hidden; }
.sp-marquee__track{ display:flex; width:max-content; }
.sp-marquee__group{ display:flex; gap:var(--sp-gap); padding-inline-end:var(--sp-gap); margin:0; list-style:none; flex:none; }
@media (prefers-reduced-motion:no-preference){
  :root:not([data-motion="off"]) .sp-marquee__track{ animation:sp-marquee var(--sp-marquee-dur, 40s) linear infinite; }
  .sp-marquee:hover .sp-marquee__track,
  .sp-marquee:focus-within .sp-marquee__track,
  .sp-marquee.is-paused .sp-marquee__track,
  .sp-marquee.is-offscreen .sp-marquee__track{ animation-play-state:paused; }
}
@keyframes sp-marquee{ to{ transform:translateX(-50%); } }
@media (prefers-reduced-motion:reduce){
  .sp-marquee__track{ width:auto; flex-wrap:wrap; }
  .sp-marquee__group[aria-hidden="true"]{ display:none; }
}
:root[data-motion="off"] .sp-marquee__track{ width:auto; flex-wrap:wrap; }
:root[data-motion="off"] .sp-marquee__group[aria-hidden="true"]{ display:none; }

/* --------------------------------------------------------------------------
   14. Footer curtain — SP-FOOTER-CURTAIN (CSS only)
   <main class="sp-main">…</main><footer class="sp-footer-curtain">…</footer>
   Only where the footer fits in one screen; otherwise it is a normal footer.
   -------------------------------------------------------------------------- */
.sp-main{ position:relative; z-index:1; background:var(--sp-bg); }
.sp-footer-curtain{ position:relative; z-index:0; }   /* sticky ≥ BP and ≥ 760px tall (§20) */
/* curved-edge variant: SVG ellipse at the bottom of the last section, flattened by site-motion.js */
.sp-curve{ position:relative; height:clamp(40px, 8vw, 140px); margin-bottom:calc(-1 * clamp(40px, 8vw, 140px)); z-index:2; pointer-events:none; }
.sp-curve__shape{ width:100%; height:100%; fill:var(--sp-bg); }

/* --------------------------------------------------------------------------
   15. Magnetic button — SP-MAGNETIC (wrapper is the static hit area)
   -------------------------------------------------------------------------- */
.sp-mag{ display:inline-block; padding:12px; }
.sp-mag__btn{
  position:relative; display:grid; place-items:center; overflow:hidden;
  width:clamp(120px, 10vw, 180px); aspect-ratio:1; border-radius:50%;
  background:var(--sp-ink); color:var(--sp-paper); text-decoration:none; border:0; cursor:pointer; font:inherit;
}
.sp-mag__btn::before{   /* circular fill rising from below (dennissnellenberg) */
  content:""; position:absolute; inset:-10%; border-radius:50%; background:var(--sp-accent);
  transform:translateY(100%); transition:transform var(--sp-dur-md) var(--sp-ease-in-out);
}
.sp-mag__btn:hover::before,.sp-mag__btn:focus-visible::before{ transform:translateY(0); }
.sp-mag__label{ position:relative; }

/* --------------------------------------------------------------------------
   16. Live number — SP-LIVE-NUMBER / stat numerals (no reflow while counting)
   -------------------------------------------------------------------------- */
.sp-calc__result,.sp-stat__num{ font-variant-numeric:tabular-nums; display:inline-block; min-width:var(--sp-num-ch, 6ch); }

/* Strokes — SP-STROKE-DRAW (fully drawn by default) */
.sp-stroke{ pointer-events:none; }
.sp-stroke path{ fill:none; stroke:var(--sp-accent); stroke-width:var(--sp-stroke-w, 3); stroke-linecap:round; stroke-linejoin:round; }

/* --------------------------------------------------------------------------
   17. GSAP start states — ONLY under .js-gsap, motion allowed, switch on.
   Everything else is set from site-motion.js with fromTo() inside matchMedia.
   Never put the H1 or the LCP image in here.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion:no-preference){
  :root.js-gsap:not([data-motion="off"]) [data-motion~="fade-up"]{ opacity:0; transform:translateY(24px); }
}

/* --------------------------------------------------------------------------
   18. View transitions — SP-TRANSITION (cross-document, no JS router)
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion:no-preference){
  @view-transition{ navigation:auto; }
  ::view-transition-old(root){ animation:none; }
  ::view-transition-new(root){ animation:sp-vt-wipe var(--sp-dur-md) var(--sp-ease-in-out) both; }
}
@keyframes sp-vt-wipe{ from{ clip-path:inset(100% 0 0 0); } to{ clip-path:inset(0); } }
/* curtain variant: add class="sp-vt-curtain" to <html> on the site */
.sp-vt-curtain::view-transition-new(root){ animation-name:sp-vt-curtain; }
@keyframes sp-vt-curtain{ from{ clip-path:ellipse(150% 0% at 50% 100%); } to{ clip-path:ellipse(150% 150% at 50% 100%); } }
/* the motion switch turns transitions off too */
:root[data-motion="off"]::view-transition-group(*),
:root[data-motion="off"]::view-transition-old(*),
:root[data-motion="off"]::view-transition-new(*){ animation:none !important; }

/* Smooth anchor scrolling only when motion is welcome */
@media (prefers-reduced-motion:no-preference){ :root:not([data-motion="off"]){ scroll-behavior:smooth; } }

/* --------------------------------------------------------------------------
   19. Reduced-motion kill switch (gsap-motion-system.md §5) and the same for
   the site motion switch (patterns.md rule 2).
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  *, *::before, *::after{
    animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important; scroll-behavior:auto !important;
  }
}
:root[data-motion="off"] *,
:root[data-motion="off"] *::before,
:root[data-motion="off"] *::after{
  animation-duration:.01ms !important; animation-iteration-count:1 !important;
  transition-duration:.01ms !important; scroll-behavior:auto !important;
}

/* --------------------------------------------------------------------------
   20. Desktop (BP) — the ONE place the desktop breakpoint lives in CSS.
   Both queries below say 1024px = BP in site-motion.js. Change both
   together, and BP with them.

   House override (Blue Digital house sites; /blue-digital-house-style
   gsap-options.md §2: desktop ≥ 981, tablet 768–980, phone ≤ 767; the house
   file wins if it ever differs): replace the two query heads with
     @media (min-width:981px){
     @media (min-width:981px) and (prefers-reduced-motion:no-preference){
   and set  const BP = 981;  at the top of site-motion.js. Nothing else in
   this file depends on BP (the 600px and 768px queries are layout only).

   Scene layouts are scoped to .js-gsap (site-motion.js, GSAP + ScrollTrigger
   present), EXCEPT first-screen layout, which is scoped to .js (motion-pref.js,
   set in <head> before first paint) so nothing shifts when GSAP arrives.
   -------------------------------------------------------------------------- */
@media (min-width:1024px){
  :root{ --sp-header-gap: 20px; }
  .sp-drift__line{ white-space:nowrap; }
  .sp-goff{ grid-template-columns:repeat(4,minmax(0,1fr)); }
  @media (min-height:760px){ .sp-footer-curtain{ position:sticky; bottom:0; } }
}

@media (min-width:1024px) and (prefers-reduced-motion:no-preference){

  /* --- First screen: SP-SPLIT-WIPE hero variant, keyed to .js (never .js-gsap) --- */
  :root.js:not([data-motion="off"]) .sp-wipe-hero__stage{
    display:block; height:100vh; height:100svh; min-height:620px; padding:0;   /* border-box: fits the screen */
  }
  :root.js:not([data-motion="off"]) .sp-wipe-hero__media{ position:absolute; inset:0; }
  :root.js:not([data-motion="off"]) .sp-wipe-hero__media img{ height:100%; aspect-ratio:auto; }
  :root.js:not([data-motion="off"]) .sp-wipe-hero__panel{
    position:absolute; inset:0 50% 0 0; z-index:1; background:var(--sp-bg);
    padding:calc(var(--sp-pin-top) + 40px) var(--sp-margin) clamp(32px, 5vh, 64px);
  }
  /* The H1 spans both halves and never moves. Difference blend: check contrast over the panel AND the
     image (darken the image file itself if needed), or drop the blend and set one colour that passes on both. */
  :root.js:not([data-motion="off"]) .sp-wipe-hero__title{
    position:absolute; z-index:2; inset:calc(var(--sp-pin-top) + 6vh) 0 auto 0;
    font-size:var(--sp-fs-display-xl); color:var(--sp-paper); mix-blend-mode:difference; pointer-events:none;
  }

  /* --- Below the first screen: scenes, only while GSAP + ScrollTrigger run (.js-gsap) --- */
  /* sticky stages (counted as pins) */
  :root.js-gsap:not([data-motion="off"]) .sp-stage{ height:200vh; }
  :root.js-gsap:not([data-motion="off"]) .sp-stage--300{ height:300vh; }   /* maximum: 200% of travel */
  :root.js-gsap:not([data-motion="off"]) .sp-stage__sticky{ position:sticky; top:0; height:100vh; height:100svh; }

  /* SP-SPLIT-WIPE two-panel variant */
  :root.js-gsap:not([data-motion="off"]) .sp-wipe .sp-stage__sticky{ display:grid; }
  :root.js-gsap:not([data-motion="off"]) .sp-wipe__panels{ grid-area:1/1; grid-template-columns:1fr 1fr; gap:0; height:100%; }
  :root.js-gsap:not([data-motion="off"]) .sp-wipe__panel img,
  :root.js-gsap:not([data-motion="off"]) .sp-wipe__panel video{ aspect-ratio:auto; }
  :root.js-gsap:not([data-motion="off"]) .sp-wipe__title{ grid-area:1/1; align-self:center; z-index:1; pointer-events:none; }
  /* giant line: centred in the two-panel variant, starts beyond the right edge in the hero */
  :root.js-gsap:not([data-motion="off"]) .sp-wipe__line{
    display:block; position:absolute; z-index:1; top:50%; left:50%; translate:-50% -50%; margin:0;
    white-space:nowrap; font:var(--sp-display-weight) var(--sp-fs-mega)/.84 var(--sp-font-display);
    letter-spacing:-.04em; text-transform:var(--sp-display-case); color:var(--sp-paper); pointer-events:none;
  }
  :root.js-gsap:not([data-motion="off"]) .sp-wipe-hero .sp-wipe__line{ top:52%; left:100%; translate:0 0; }

  /* SP-REEL-GROW */
  :root.js-gsap:not([data-motion="off"]) .sp-grow .sp-stage__sticky{ display:grid; place-items:center; }
  :root.js-gsap:not([data-motion="off"]) .sp-grow__media{ grid-area:1/1; width:100%; height:100%; }
  :root.js-gsap:not([data-motion="off"]) .sp-grow__media img,
  :root.js-gsap:not([data-motion="off"]) .sp-grow__media video{ aspect-ratio:auto; }
  :root.js-gsap:not([data-motion="off"]) .sp-grow__copy{ position:relative; grid-area:1/1; z-index:1; margin:0; max-width:70%; text-align:center; color:var(--sp-paper); }
  :root.js-gsap:not([data-motion="off"]) .sp-grow__copy::before{ content:""; position:absolute; inset:-30% -20%; z-index:-1; background:radial-gradient(closest-side, var(--sp-overlay), transparent); }

  /* SP-CHAPTERS: one screen below the fixed header, padding inside it */
  :root.js-gsap:not([data-motion="off"]) .sp-ch{
    height:calc(100vh - var(--sp-pin-top)); height:calc(100svh - var(--sp-pin-top));
    overflow:clip; display:grid; align-content:center; padding-block:clamp(16px, 3vh, 40px);
  }
  :root.js-gsap:not([data-motion="off"]) .sp-ch__items{ display:grid; min-height:0; }
  :root.js-gsap:not([data-motion="off"]) .sp-ch__item{ grid-area:1/1; padding:0; min-height:0; }
  :root.js-gsap:not([data-motion="off"]) .sp-ch__media img{ width:auto; max-width:100%; max-height:60svh; }
  :root.js-gsap:not([data-motion="off"]) .sp-ch .sp-rail{ display:block; }

  /* SP-PIN-HSCROLL: one screen below the fixed header */
  :root.js-gsap:not([data-motion="off"]) .sp-hscroll{
    height:calc(100vh - var(--sp-pin-top)); height:calc(100svh - var(--sp-pin-top)); display:flex; align-items:center;
  }
  :root.js-gsap:not([data-motion="off"]) .sp-hscroll__track{
    display:flex; flex-wrap:nowrap; align-items:flex-start; width:max-content;
    gap:clamp(2rem, 4vw, 6rem); padding-inline:var(--sp-margin);
  }
  /* scattered prints: set --w (width) and --y (drop) per item in the stylesheet, not in style="" */
  :root.js-gsap:not([data-motion="off"]) .sp-hscroll__item{ width:var(--w, 22vw); margin-top:var(--y, 0); flex:none; }
  :root.js-gsap:not([data-motion="off"]) .sp-hscroll__item:nth-child(3n+2){ --w:16vw; --y:12vh; }
  :root.js-gsap:not([data-motion="off"]) .sp-hscroll__item:nth-child(3n){ --w:26vw; --y:4vh; }

  /* SP-RAIL (page): CSS scroll timeline where supported, ScrollTrigger fallback otherwise */
  :root.js-gsap:not([data-motion="off"]) .sp-rail--page{ display:block; }
  @supports (animation-timeline:scroll()){
    :root:not([data-motion="off"]) .sp-rail--page{ display:block; }
    :root:not([data-motion="off"]) .sp-rail--page .sp-rail__bar{
      animation:sp-rail linear both; animation-timeline:scroll(root);
    }
  }
}
