:root {
  /* ---- MARCH FORTH ----
     Chris's brand board is three colours: #0D0D0F black, #1B2A3D navy,
     #E8E3D9 sand. Two of them are dark, and they sit only 1.34:1 apart, so
     they cannot carry the five-band descent on their own. The ladder below
     extends ABOVE the navy instead of compressing below it: his navy lands on
     rung 3 and his black is the floor. */
  --sand: #e8e3d9;               /* the ground */
  --sand-2: #dfd9cd;             /* alternating light band */
  --navy: #1b2a3d;               /* Chris's navy — the brand colour */
  --brand-black: #0d0d0f;        /* Chris's black */

  --porcelain: var(--sand);      /* legacy aliases, kept so nothing dangles */
  --porcelain-2: var(--sand-2);
  --signal: var(--navy);
  --signal-dark: #101b28;        /* pressed / hover */
  --paper: var(--sand);
  --paper-bright: #fdf8ec;       /* WARM — physical props only (card, polaroid, scrap, tape) */

  /* light tints, built in the navy's own hue (214deg) rather than mixed toward
     sand — mixing produced grey mud that failed on the lighter bands */
  --ice: #dee5ed;
  --pale: #c9d5e3;
  --sky: #aabdd5;
  --sky-strong: #7d9abf;
  --ocean: #1a5da8;              /* links: 5.17 on sand */
  --blue: var(--navy);           /* alias used by .cred and the h2 colour */
  /* the offer 'breather' band. --sky (#aabdd5) put rust-ink at 3.83 and .micro
     at 4.26; --ice (#dee5ed) fixed those but landed within 0.006 luminance of
     the sand ground, so the band disappeared into its neighbours. This clears
     both (4.74 / 5.28) and stays visibly cooler than sand. */
  --breather: #c3d1e4;
  /* hero ripple bands, re-derived for the navy. Both must stay >=3:1 on the
     bright wave behind the accent word — verified by pixel sampling, not math,
     because the backdrop is a photograph. */
  --accent-word: #17539a;        /* the ONE bright note on the page: "mental" in the hero.
                                    Navy read as a lighter black next to the black lines;
                                    this pops by saturation, not lightness, so it still
                                    holds >=3:1 on the sky and foam behind it (pixel-sampled). */
  /* The word's BASE is what carries legibility: ink-mask sampled >=3.4:1 at
     six breakpoints. The bands are motion: a lift and a dip that cross the
     word every 8.5s / 13s. Decided 2026-09-08 (Anthony): the lift is allowed
     to sit around 2.5:1 for the moment it passes — the capped version was
     invisible. Both bands are off under reduced motion. The sampler reports
     the composited band colour (base + crest at the mask alpha). */
  --ripple-crest: #3170b3;
  --ripple-trough: #0e2a4d;
  --teal: #1f3d63;
  --cta: var(--navy);
  --cta-dark: var(--signal-dark);
  --rust: #b5502f;               /* the one warm accent: hand-drawn MARKS */
  --rust-ink: #8f3d22;           /* rust TEXT — 5.74 on sand. Warmer ground
                                    suits it better than the old cool porcelain */
  --ink: #232a33;                /* body — 11.32 on sand; softer than pure black */
  --ink-soft: #47505c;

  /* type */
  --poster: 'Anton', 'Arial Narrow', sans-serif;
  --label: 'Aileron', system-ui, sans-serif;   /* was Oswald */
  --body: 'Aileron', system-ui, sans-serif;    /* was Source Sans 3 */
  --hand: 'Rock Salt', cursive;
  /* hero display — Anton, the same poster face as .support-line.
     SIL OFL: free for commercial use, no permission needed. */
  --hero: var(--poster);

  --maxw: 1160px;
  --gutter: 30px;

  /* spacing scale — one rhythm everywhere, all on a 4pt grid.
     Three vertical tiers: section > block > element. */
  --s-section: clamp(56px, 6vw, 84px);     /* section top/bottom, no tear */
  --s-tear-pad: clamp(84px, 8.6vw, 112px); /* section top when a tear overlaps it */
  --s-grid-row: 40px;                      /* row gap, every two-column section body */
  --s-grid-col: 56px;                      /* column gap, ditto */
  --s-block: 32px;                         /* between blocks inside a column */
  --s-head: 36px;                          /* heading -> its content */
  --s-el: 20px;                            /* between adjacent elements */
  --w-label: 160px;                        /* definition-list label column */

  /* type scale — 8 steps. Was 19 distinct px values with five sitting within
     2px of each other twice over, which is drift, not hierarchy. Use these;
     do not introduce a raw px font-size. */
  --fs-micro: 8px;      /* barcode / sticker micro-caps */
  --fs-tiny: 11px;      /* smallest uppercase labels */
  --fs-label: 12px;
  --fs-label-lg: 13px;
  --fs-sm: 15px;
  --fs-base: 17px;      /* body default */
  --fs-md: 19px;
  --fs-lg: 21px;

  --measure: 44ch;
  --measure-wide: 58ch;

  /* depth ladder — the page descends from surface to seafloor */
  /* The descent, re-derived in CIE L* so each band steps down by an equal
     perceptual amount (~5.9 L*) rather than an equal hex amount. Starting from
     the deeper brand blue costs headroom — the old ladder spanned L* 44->3,
     this one spans 26->3 — so the rungs are spaced evenly to keep the descent
     legible across the whole page instead of bottoming out early. */
  --depth-1: #2e4560;
  --depth-2: #25364b;
  --depth-3: #1d2937;
  --depth-3b: #161c24;
  --depth-4: #0d0d10;

  /* blue-black ink for the poster type at the surface */
  --ink-deep: #0d0d0f;   /* Chris's black, for poster type */
}


/* Aileron — Sora Sagano, CC0 1.0 Universal (public domain). Licence text ships
   at assets/fonts/Aileron-LICENSE-CC0.txt. Not on Google Fonts, so it is
   self-hosted: subset to Latin + the punctuation this copy uses, 111KB of OTF
   down to 45KB of WOFF2 across four weights. */
@font-face {
  font-family: 'Aileron'; font-style: normal; font-weight: 300; font-display: swap;
  src: url("../assets/fonts/aileron-300.woff2") format('woff2');
}
@font-face {
  font-family: 'Aileron'; font-style: normal; font-weight: 400; font-display: swap;
  src: url("../assets/fonts/aileron-400.woff2") format('woff2');
}
@font-face {
  font-family: 'Aileron'; font-style: normal; font-weight: 600; font-display: swap;
  src: url("../assets/fonts/aileron-600.woff2") format('woff2');
}
@font-face {
  font-family: 'Aileron'; font-style: normal; font-weight: 700; font-display: swap;
  src: url("../assets/fonts/aileron-700.woff2") format('woff2');
}

* { box-sizing: border-box; }
/* clip on <body> alone doesn't reach the viewport here: the ticker's
   rotate+scale(1.02) adds ~17px of scrollable overflow. Clip both. */
html, body { margin: 0; padding: 0; overflow-x: clip; }
/* iOS rubber-band was revealing the page canvas past the end of the footer:
   the canvas takes its colour from <body> (porcelain), so bouncing past a
   near-black footer flashed a screenful of white.
   Two layers, because this could not be tested on real iOS here:
   1. suppress the bounce, which fixes both ends of the page;
   2. if a browser ignores that, the canvas is the seafloor rather than
      porcelain, so the bottom still matches the footer.
   <body> keeps its porcelain and paints over the whole content box, so the
   page itself is pixel-identical either way — only the region outside it
   changes (verified by diffing captures with animations frozen). */
html {
  overscroll-behavior-y: none;
  background: var(--depth-4);
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
  overflow-x: clip;
}
/* derived from the token so it cannot drift out of sync again — this was
   still rgba(11,110,224) (the old --ocean) after the palette change */
a { color: var(--ocean); text-decoration-color: color-mix(in srgb, var(--ocean) 45%, transparent);
    text-underline-offset: 3px; }
a:hover { color: var(--blue); text-decoration-color: var(--blue); }
/* --ocean measures 1.60-2.98:1 on the five depth bands and 4.46 on sky, so it
   is only safe on porcelain. Set a passing colour per band so a new inline
   link cannot ship unreadable. NOTE .offer is the LIGHT sky band, not a dark
   one — it needs a darker link, not a lighter one (--sky-strong would be
   1.45:1 there). */
.on-blue a:not(.btn), .outcomes a:not(.btn),
.book a:not(.btn), .footer a:not(.btn) { color: var(--sky-strong); }
.on-blue a:not(.btn):hover, .outcomes a:not(.btn):hover,
.book a:not(.btn):hover, .footer a:not(.btn):hover { color: var(--pale); }
::selection { background: var(--sky); }
/* height:auto is load-bearing: the width/height attributes on every <img>
   reserve aspect-ratio space for CLS, but without this the browser honours
   the literal pixel height and the polaroids stretch. */
img { max-width: 100%; height: auto; display: block; }

.container { max-width: var(--maxw); margin: 0 auto; padding-inline: var(--gutter); position: relative; }

/* ---- paper grain: felt, not seen ---- */
.grain {
  position: fixed; inset: 0; z-index: 40; pointer-events: none;
  background-image: url("../assets/tex-fiber.webp");
  background-size: 240px auto; background-repeat: repeat;
  mix-blend-mode: multiply; opacity: .10;
}

/* ---- type scale — big slab poster ---- */
h1, h2, h3 { font-family: var(--poster); font-weight: 400; letter-spacing: .012em; margin: 0; }
h1 { font-size: clamp(44px, 9.5vw, 132px); line-height: .94; color: var(--blue); }
/* two-tier hierarchy: default h2 speaks, .h2-display shouts */
h2 { font-size: clamp(32px, 3.6vw, 50px); line-height: 1.04; color: var(--blue); text-transform: uppercase; margin-bottom: var(--s-head); }
.h2-display { font-size: clamp(44px, 5.4vw, 76px); line-height: .98; }
/* word masks for the split-headline reveal */
.w { display: inline-block; overflow: hidden; vertical-align: bottom; padding-bottom: .08em; margin-bottom: -.08em; }
.w-i { display: inline-block; will-change: transform; }

.label {
  font-family: var(--label); font-weight: 500; font-size: var(--fs-label-lg);
  letter-spacing: .16em; text-transform: uppercase; color: var(--teal);
  margin: 0 0 14px;
}
.hand { font-family: var(--hand); font-weight: 400; font-size: var(--fs-base); line-height: 1.65; color: var(--teal); margin: 0; }
.micro { font-size: var(--fs-base); color: var(--ink-soft); margin: 12px 0 0; }

/* ---- buttons ---- */
.btn {
  display: inline-block; font-family: var(--label); font-weight: 500;
  font-size: var(--fs-sm); letter-spacing: .09em; text-transform: uppercase;
  color: var(--porcelain); background: var(--cta);
  border-radius: 2px; padding: 13px 24px; text-decoration: none;
  border: 1.5px solid var(--cta-dark);
  box-shadow: 0 3px 0 var(--cta-dark);
  transition: transform .15s ease, box-shadow .15s ease, background .15s ease;
}
.btn:hover { background: var(--cta-dark); color: var(--porcelain); transform: translateY(-2px); box-shadow: 0 5px 0 var(--cta-dark); }
.btn:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--cta-dark); }
.btn-lg { font-size: var(--fs-base); padding: 17px 30px; }
.link-cta {
  font-family: var(--label); font-weight: 500; font-size: var(--fs-sm);
  letter-spacing: .09em; text-transform: uppercase; color: var(--blue);
  text-decoration: none; border-bottom: 2px solid var(--sky-strong); padding-bottom: 3px;
}
.link-cta:hover { border-color: var(--blue); }
.cta-row { display: flex; align-items: center; gap: var(--s-el); flex-wrap: wrap; margin-top: var(--s-block); }

/* ---- nav ---- */
.nav {
  position: sticky; top: 0; z-index: 30;
  background: rgba(248,247,244,.94); backdrop-filter: blur(6px);
  border-bottom: 1.5px solid var(--ink);
}
.nav-inner {
  max-width: var(--maxw); margin: 0 auto; padding: 13px var(--gutter);
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
}
.wordmark {
  text-decoration: none; white-space: nowrap;
  display: inline-block; padding-block: 10px;  /* 24px lockup -> 44px hit area */
}
/* the marks are the brand SVGs (brand/), sized by height so width follows the viewBox */
.wordmark img { display: block; height: 24px; width: auto; }

/* inline links sit on a 24-33px line box; extend their hit area to 44px so
   they are tappable without inflating the text they sit in */
.link-cta, .footer-links a, .micro a, .footer-legal a { position: relative; }
.link-cta::before, .micro a::before {
  content: ""; position: absolute; left: -6px; right: -6px; top: 50%;
  height: 44px; transform: translateY(-50%);
}

/* ---- section guide: the one-pager's table of contents ---- */
.nav-guide { flex: 1 1 auto; min-width: 0; }
.nav-guide ul {
  list-style: none; margin: 0; padding: 0;
  display: flex; align-items: center; gap: clamp(14px, 1.7vw, 26px);
  justify-content: center;
}
.nav-guide a {
  position: relative; display: block; white-space: nowrap; text-decoration: none;
  font-family: var(--label); font-weight: 500; font-size: var(--fs-label);
  letter-spacing: .13em; text-transform: uppercase; color: var(--teal);
  padding: 6px 0 8px;
}
.nav-guide a:hover { color: var(--blue); }
/* 44px minimum hit area without changing the visual rhythm of the bar */
.nav-guide a::before { content: ""; position: absolute; inset: -6px -10px; }
/* The ::before extension is clipped on the vertical axis once .nav-guide
   becomes a scroll container at <=860px (overflow-x:auto forces overflow-y
   to auto), which dropped the real hit area to 31px. Give the link its own
   height so the target does not depend on an ancestor's overflow. */
.nav-guide a { display: inline-flex; align-items: center; min-height: 44px; }
.wordmark { display: inline-flex; align-items: center; min-height: 44px; }
/* the marker underline again, at nav scale — drawn, not a border */
.nav-guide a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 2px; height: 3px;
  background: var(--rust);
  -webkit-mask-image: url("../assets/nav-mark.svg"); mask-image: url("../assets/nav-mark.svg");
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  transform: scaleX(0); transform-origin: left center;
  transition: transform .22s ease-out;
}
.nav-guide a[aria-current="true"] { color: var(--rust); }
.nav-guide a[aria-current="true"]::after { transform: scaleX(1); }
.nav-cta { flex: 0 0 auto; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--ink-deep); color: var(--paper-bright);
  font-family: var(--label); font-size: var(--fs-label-lg); letter-spacing: .14em;
  text-transform: uppercase; padding: 12px 18px; text-decoration: none;
}
.skip-link:focus { left: 8px; top: 8px; }

@media (max-width: 1080px) {
  .nav-guide ul { gap: 14px; }
  .nav-guide a { font-size: var(--fs-tiny); letter-spacing: .1em; }
}
@media (max-width: 860px) {
  /* the guide drops to its own row and scrolls sideways rather than cramming */
  .nav-inner { flex-wrap: wrap; row-gap: 4px; padding-bottom: 0; }
  .wordmark { flex: 1 1 auto; }
  .nav-guide { flex: 1 0 100%; order: 3; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    margin-inline: calc(var(--gutter) * -1); padding-inline: var(--gutter);
    -webkit-mask-image: linear-gradient(to right, transparent, #000 16px, #000 calc(100% - 16px), transparent);
    mask-image: linear-gradient(to right, transparent, #000 16px, #000 calc(100% - 16px), transparent);
  }
  .nav-guide::-webkit-scrollbar { display: none; }
  .nav-guide ul { justify-content: flex-start; gap: 18px; padding-bottom: 2px; }
}
@media (max-width: 600px) {
  /* The section guide is a desktop table of contents. On a phone the page is
     linear and the bar is fixed: six links (two rows) held 155px of an 812px
     screen on every scroll. Wordmark + Book a call only. */
  .nav-guide { display: none; }
  .nav-inner { flex-wrap: nowrap; }
}

/* focus target for the skip link — never show a ring for it */
#main:focus { outline: none; }

/* focus + hover precision */
/* The ring has to clear 3:1 against BOTH the band behind it and the fill of
   the control it wraps. A single --ocean ring failed six of eight contexts:
   1.60 on outcomes, 1.95 on about, 2.32 on testimonials, 2.66 on book,
   2.98 on footer, and 1.60 against every CTA's own fill. On book and footer
   it failed against both neighbours at once, so it was invisible.
   --sky-strong clears all of them (book 8.16, footer 9.14, vs fill 4.91). */
a:focus-visible, button:focus-visible {
  outline: 2px solid var(--focus-ring, var(--ocean));
  outline-offset: 3px; border-radius: 2px;
}
.on-blue, .outcomes, .why, .book, .footer, .btn { --focus-ring: var(--sky-strong); }
/* the nav sits on porcelain, where the darker ring reads better */
.nav { --focus-ring: var(--ocean); }
.nav .btn { --focus-ring: var(--sky-strong); }
.link-cta { position: relative; border-bottom: 0; padding-bottom: 3px; }
.link-cta::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--sky-strong); transform-origin: left;
  transition: transform .15s ease, background .15s ease;
}
.link-cta:hover::after { transform: scaleY(1.6); background: var(--blue); }

/* ---- hero: giant type interleaved with the landscape ---- */
.hero { position: relative; background: var(--paper); overflow: clip; }

/* one free-scrolling viewport — no holds, ever */
.stage-full {
  position: relative;
  /* --nav-h is published by js/main.js from the real measured bar. The old
     hardcoded 63px was never right: the nav measures 77px on desktop and
     100-101px once it wraps, so the hero never framed to the viewport. */
  /* min-height, not height: the column below is in flow now, so a longer
     sub-headline makes the stage taller instead of pushing the CTA out the
     bottom. Chris's copy runs ~8 lines at 38ch; the old 95-char line ran 3.
     Measured overflow before this: 16px at 1440x900, 63px at 1280x800 —
     the tagline was landing on the next section's sand at 1.00:1. */
  min-height: max(560px, calc(100svh - var(--nav-h, 77px)));
  background: url("../assets/horizon.webp") center 40% / cover no-repeat;
  overflow: hidden;
}
/* NOTE on the background-position scrub in js/main.js: under `cover` it can
   only move if the rendered background is taller than this box, and it usually
   is not. Measured vertical slack: 1440x900 0px, 1440x1080 0px, 1512x982 0px,
   1280x800 0px, 390x844 0px — but 1688x914 116px and 1920x1080 83px. So the
   parallax engages only on viewports wider than ~1.75x the stage height and is
   inert on every common laptop and phone. Making it work everywhere means
   moving the image onto its own over-sized layer and translating that instead,
   which re-frames the hero and needs the headline contrast sweep re-run. */
/* deterministic height for full-page QA captures (svh explodes in tall windows) */
body.static-mode .stage-full { min-height: 760px; }
/* One column holds the headline and the footer copy, so the space between them
   is a designed value instead of whatever is left over. They used to be
   anchored to opposite edges — headline to the top, footer to the bottom — so
   every extra pixel of viewport height fell into the middle as dead space:
   83px at 1280x800 but 278px (27% of the hero) at 1440x1080.

   No z-index here on purpose. .stage-col must NOT become a stacking context,
   or the headline could no longer sit behind the water while the CTA sits in
   front of it. Never add transform/opacity/filter/will-change to this rule. */
.stage-col {
  /* Top-anchored, NOT bottom-anchored. Bottom-anchoring closed the gap just as
     well, but it pushed the headline down the stage: into the dark wave (the
     crop was chosen specifically to keep it on sky) and into the polaroid,
     which collided at 768 and 900. Anchored at the top the headline stays on
     sky, and surplus stage height falls below the CTA as open water — which is
     photograph, not dead space. */
  /* max() so the headline always clears the fixed-height .stage-tear;
     a bare 6% let the tear eat 61% of the first line at 200% zoom */
  position: relative;
  padding-top: clamp(72px, 8.5vh, 120px); padding-bottom: clamp(24px, 4vh, 56px);
  /* split: column 1 = headline stack over the foot; column 2 = the polaroid,
     centred on the block beside it. The polaroid lives INSIDE this column now
     (it was absolute in .stage-full), so the two never overlap by construction. */
  /* ON THE SITE'S GRID. This column was the only thing on the page not
     bounded to --maxw: at 1690px it spanned 1572px, the headline started at
     x=68 while the nav wordmark and every section start at x=305, and ~490px
     of void opened between a height-capped headline and a right-hugging
     polaroid. Bounded and centred, the headline lands under the wordmark and
     the slack is ~48px at 1688. Track 2 is sized by the polaroid's own cap. */
  max-width: var(--maxw); margin-inline: auto;   /* border-box, like .container and .nav-inner */
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, min(600px, 62vh, 48%));
  grid-template-rows: auto auto;
  align-items: start; justify-items: start;
  gap: clamp(20px, 3.2vh, 44px) clamp(24px, 3vw, 48px);
  padding-inline: var(--gutter);
}
.giant-stack { position: relative; grid-column: 1; grid-row: 1; justify-self: stretch; }
.stage-foot { grid-column: 1; grid-row: 2; }

.giant {
  position: static; z-index: 2;
  margin: 0;
  font-family: var(--hero);
  /* SPLIT HERO (2026-09-09): three lines — SALES IS A / MENTAL / BATTLE. —
     beside a ~600px polaroid. Sized so the whole left column (three lines +
     scrap + buttons + note) fits an 823px stage at 900 tall: 3 x 131 + 28 +
     143 + 18 + 56 + 18 + 28 + padding = 796. Was min(11.2vw, 17.2vh) for two. */
  /* 130px cap: the column is bounded to --maxw now, so the left track is at
     most ~555px beside a 557px polaroid; "SALES IS A" is 3.95em wide, so
     anything past 130px would wrap mid-line on wide+tall screens (1920x1080
     reaches 150px on the vw term). */
  font-size: clamp(52px, min(8.9vw, 14vh), 130px); line-height: 1.04;
  letter-spacing: .01em; text-transform: uppercase; color: var(--ink-deep);
  font-weight: 400;
  /* Anton is already heavy, so the synthetic stroke is a hairline only.
     Solid fill: background-clip:text proved unreliable (hollow in Safari);
     the global grain supplies the texture. */
  -webkit-text-stroke: .006em var(--ink-deep);
}
.giant-under { color: var(--pale); -webkit-text-stroke-color: var(--pale); }
/* no overflow clip: the reveal is a mask-image wipe, and Anton's caps
   overshoot a line-height:1 box by ~25% — overflow:hidden beheaded them. */
.giant-mask { display: block; }
.giant-line {
  /* shrink-wrap the text: as a full-width block the mask travelled across
     1440px while the words only occupied ~850 of it, so the tear finished
     early and the last third of the animation did nothing. */
  display: inline-block; vertical-align: top;
  /* Torn-paper wipe. CSS holds the REVEALED state (mask-position 0%), so
     reduced-motion, no-JS and ?static=1 all render the headline in full;
     GSAP sets the hidden "from" at 100% and tears it open.
     The mask is opaque on its left and transparent on its right, so sliding
     the window from 100% -> 0% uncovers the line left-to-right.
     NOTE: CSS masks read the ALPHA channel — tear-wipe.png is RGBA for that
     reason; a black/white luminance PNG would mask nothing. */
  -webkit-mask-image: url("../assets/tear-wipe.png");
          mask-image: url("../assets/tear-wipe.png");
  /* 200%, not 300%: at 300% the tear cleared the box by the 25% mark and
     the last half of the travel was dead time. At 200% the visible window
     is half the mask, so the tear crosses over the whole animation. */
  -webkit-mask-size: 200% 100%;  mask-size: 200% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: 0% 0;    mask-position: 0% 0;
}
/* The pivot of the claim carries the brand blue, and water moves through it:
   two deep-blue bands sweep across the letterforms on different cycles, like
   light refracting through the surface.

   The bands go DARKER, never lighter. Measured on the lifted sky, every pale
   blue fails badly (--sky-strong is 1.38:1), so a bright glint would make the
   word vanish as it passed. Deeper blue raises contrast instead of destroying
   it: signal 3.52:1 at rest, ~5-8:1 under a band.

   Duplicate-layer technique, not background-clip:text — that rendered hollow
   in Safari back in v12 and is banned here. Same trick .giant-under uses. */
.hero-accent {
  font-style: normal;
  position: relative; display: inline-block;
  color: var(--accent-word);
  -webkit-text-stroke-color: var(--accent-word);
}
.hero-accent::before,
.hero-accent::after {
  content: attr(data-text);
  position: absolute; left: 0; top: 0;
  pointer-events: none; white-space: nowrap;
  /* soft-edged bands, masked the same way the headline tears open. Hard
     clip-path diagonals read as a generic shine sweep; a soft gradient mask
     reads as light bending through water. */
  -webkit-mask-size: 230% 100%; mask-size: 230% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.hero-accent::before {
  color: var(--ripple-crest); -webkit-text-stroke-color: var(--ripple-crest);
  -webkit-mask-image: linear-gradient(101deg,
    transparent 0 30%, rgba(0,0,0,.85) 37% 41%, transparent 49% 63%,
    rgba(0,0,0,.6) 68% 71%, transparent 79% 100%);
  mask-image: linear-gradient(101deg,
    transparent 0 30%, rgba(0,0,0,.85) 37% 41%, transparent 49% 63%,
    rgba(0,0,0,.6) 68% 71%, transparent 79% 100%);
  animation: hero-ripple-a 8.5s ease-in-out infinite;
}
.hero-accent::after {
  color: var(--ripple-trough); -webkit-text-stroke-color: var(--ripple-trough);
  -webkit-mask-image: linear-gradient(94deg,
    transparent 0 22%, rgba(0,0,0,.55) 30% 35%, transparent 44% 72%,
    rgba(0,0,0,.4) 77% 80%, transparent 88% 100%);
  mask-image: linear-gradient(94deg,
    transparent 0 22%, rgba(0,0,0,.55) 30% 35%, transparent 44% 72%,
    rgba(0,0,0,.4) 77% 80%, transparent 88% 100%);
  animation: hero-ripple-b 13s ease-in-out infinite;
}
/* the bands start and finish clear of the word, so at rest — and with motion
   off — it is simply solid Signal Blue */
@keyframes hero-ripple-a {
  0%, 14%   { -webkit-mask-position: 100% 0; mask-position: 100% 0; }
  76%, 100% { -webkit-mask-position: -30% 0; mask-position: -30% 0; }
}
@keyframes hero-ripple-b {
  0%, 30%   { -webkit-mask-position: 105% 0; mask-position: 105% 0; }
  88%, 100% { -webkit-mask-position: -35% 0; mask-position: -35% 0; }
}
/* the submerged twin stays one flat colour — it reads as ice, not ink */
.giant-under .hero-accent { color: inherit; -webkit-text-stroke-color: inherit; }
.giant-under .hero-accent::before,
.giant-under .hero-accent::after { display: none; }

/* ice twin of the headline — crossfades in as the water covers it */
.giant-under { position: absolute; inset: 0; color: var(--pale);
  opacity: 0; pointer-events: none; z-index: 2; }

/* the one contextual note — replaces the sticker chips */
.stage-note {
  font-size: var(--fs-base); color: var(--ink-deep); margin: 0; text-wrap: balance;
}
/* two clean lines at every width — the foot column is 524px on the site grid,
   so the note wraps on desktop too; the halves keep the dot off a line end */
.stage-note .note-half { display: block; }
.stage-note .note-sep { display: none;
}
/* the video caption: "(video coming soon)" always on its own line, so a
   narrower polaroid never breaks it mid-parenthesis */
.polaroid-hero .hand-sub { display: block; margin-top: 2px;
  text-shadow: 0 1px 8px rgba(248,247,244,.7), 0 0 20px rgba(248,247,244,.55);
}

/* surf line where the water meets the paper shore */
.stage-full::after {
  content: ""; position: absolute; left: -1%; right: -1%; bottom: -16px; height: 56px;
  background: url("../assets/foam-2.webp") center / 100% 100% no-repeat;
  z-index: 6; opacity: .9; pointer-events: none;
}

.lede { font-size: var(--fs-lg); line-height: 1.6; color: var(--ink-soft); max-width: var(--measure); margin: 0; font-weight: 300; }


.stage-tear { position: absolute; left: 0; right: 0; top: -1px; height: 82px; z-index: 7; }

/* real ocean — looping video with a photographic foam waterline */
/* The rising water is DEPTH, not a second photograph.

   This used to be ocean-still.jpg — a top-down close-up of sparkling water —
   sliding up over horizon.webp, which is a side-on breaking wave. Two
   incompatible camera angles at two different scales in the same frame: you
   look at a wave, then a texture shot from above slides over it. It read as a
   swatch pasted on a photo, the same failure as the drawn foam it replaced.

   Now it is a translucent wash in the page's own depth ladder, so the real
   wave stays visible through the water as it darkens, and the hero joins the
   surface -> seafloor descent the rest of the page already runs on. No second
   image, no perspective clash, and one fewer asset to download. */
.sea {
  /* waterline at 66%, not the old 74%: the hero foot copy sits just below it,
     and at 74% the link band was only dark enough for 3.81:1. Swept 70/66/62 —
     70 still failed (4.50), 66 clears it (5.37) without pulling the dark up
     into the polaroid. */
  /* starts at half the stage (just under the headline's baseline) and feathers
     over ~200px, so nothing has an edge. Stops re-derived so the foot copy keeps
     its density: link-cta and note are ink-mask sampled at >=4.5 (see README). */
  position: absolute; left: 0; right: 0; top: 50%; height: 140%; z-index: 4;
  will-change: transform;
  background: linear-gradient(to bottom,
    rgba(18, 63, 115, 0)    0%,
    rgba(18, 63, 115, .22)  8%,
    rgba(18, 63, 115, .54) 15%,
    rgba(16, 37, 67, .78)  24%,
    rgba(14, 30, 54, .88)  36%,
    rgba(13, 25, 44, .94)  70%,
    rgba(13, 25, 44, .97) 100%);
}

/* No drawn foam and no gradient "shimmer" here any more. The wave in
   assets/horizon.webp is a real photograph with its own spray, foam and
   sparkle; layering illustrated foam-1/foam-2 PNGs and an overlay-blend
   light sweep on top of it read as fake. The photography carries it. */

@keyframes drift { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* hero polaroid floats over the stage boundary */
.polaroid {
  margin: 0; padding: 16px 16px 12px;
  background: var(--paper-bright) url("../assets/tex-polaroid-clean.png");
  background-size: 200px auto;
  box-shadow: 0 30px 50px -22px rgba(34,52,60,.55), 0 3px 8px rgba(34,52,60,.2);
  transform: rotate(-1.4deg);
  position: relative; max-width: 520px;
}
.polaroid-hero {
  /* Below the headline's second line, not beside it. "MENTAL BATTLE." is
     ~27% wider than "THE PROBLEM" was, and at top:38% the polaroid's corner
     sat on the last letters — 126x144px of occlusion at 1440, 170x131 at
     1280, clear only at 1688. Vertical separation makes the horizontal
     overlap irrelevant and keeps the headline at full size; the width comes
     down a step so the frame still fits inside the stage. */
  /* Anchored to the stage FLOOR, not a top percentage: a top% can never both
     clear the headline and stay inside the stage across heights (52% still
     clipped 45x27px at 1440 and 90x25 at 1280 once the frame's rotation and
     the tape above it are counted). From the bottom, it cannot overflow the
     floor, and its top lands wherever the height leaves it. */
  /* SPLIT HERO: a grid cell, not an absolute corner. Spans both rows of the
     column and centres on the headline + foot block; the 62vh term keeps the
     frame inside the stage on short screens. The comments above describe the
     corner placement this replaced. */
  position: relative; z-index: 5;
  grid-column: 2; grid-row: 1 / 3; align-self: center; justify-self: end;
  /* the track carries the cap (600px / 62vh / 48%); the base .polaroid rule's
     max-width:520px silently held this at 530 until now */
  width: 100%; max-width: none; margin: 0;
  box-shadow: 0 34px 56px -22px rgba(10,20,35,.6), 0 4px 10px rgba(10,20,35,.25);
}

/* the clarifier — what this actually is, said in words */
/* ONE fluid rule for the scrap at every width; no breakpoints.
   Type ramps 15.5→20px with the viewport, the box is capped at 40ch so the
   copy sits at three lines everywhere, and the padding scales with the tear.
   Earlier versions set three hand-picked breakpoints and stretched the paper
   photo to whatever box the text made — a smeared 6:1 strip at 690px, a slab
   on phones. */
.stage-sub {
  --tear: clamp(30px, 2.6vw, 46px);            /* rendered size of the torn edge */
  font-size: clamp(15.5px, 0.55vw + 12.5px, 20px); font-weight: 400; line-height: 1.5;
  color: var(--ink); max-width: min(100%, 40ch); margin: 0;
  position: relative;
  padding: calc(var(--tear) * .6) calc(var(--tear) * .62) calc(var(--tear) * .66);
  transform: rotate(-.7deg);
  /* The paper lives on ::before, not here: the filter on the parent applies to
     the already-cut child, so the shadow follows the torn edge. */
  filter: drop-shadow(0 10px 16px rgba(10,20,35,.3));
}
/* The paper is a NINE-SLICE (border-image) of the photographed tear, so the
   torn edges render at their own scale whatever the box is — corners stay
   corners, edges repeat along their length, the middle is plain paper.
   Slices measured from the asset's alpha (900x490; tear depth 68/60/50/26px
   top/bottom/left/right, with margin): 90 60 85 80. The pseudo sits a little
   outside the text box so the tear bites land in the padding, not the copy. */
.stage-sub::before {
  content: ""; position: absolute; z-index: -1;
  inset: calc(var(--tear) * -.3) calc(var(--tear) * -.28);
  border-style: solid;
  border-width: var(--tear) calc(var(--tear) * .66) calc(var(--tear) * .93) calc(var(--tear) * .89);
  border-image-source: url("../assets/torn-paper.webp");
  border-image-slice: 90 60 85 80 fill;
  border-image-width: var(--tear) calc(var(--tear) * .66) calc(var(--tear) * .93) calc(var(--tear) * .89);
  border-image-repeat: round;
  background: var(--paper-bright);
  background-clip: padding-box;                /* legible before the image lands; never outside the paper */
  pointer-events: none;
}
.stage-foot {
  /* This copy sits below the waterline, over the depth wash. Measured backdrop
     luminance there is .06-.21, where ink-deep scores 1.8-2.9 — so the type is
     light here, matching every other deep band on the page. The torn scrap
     keeps dark type because it has its own cream paper behind it. */
  /* Local scrim, because the comment above is only true at some aspect
     ratios. The wash starts at a percentage of the stage, so whether it
     reaches the foot depends on the stage's HEIGHT, not its width — at
     900x1000 the foot sat on bright water and this light type measured
     1.01:1. A width media query cannot express that. The gradient below
     travels with the foot, so the ground is guaranteed. */
  position: relative; z-index: 6;
  display: flex; flex-direction: column; align-items: flex-start; gap: 18px;
  max-width: min(560px, 100%);
}
/* There used to be a navy scrim on .stage-foot::before, sized to the foot
   column with a 44px top ramp and no horizontal falloff. Once the copy got
   shorter its box showed: a hard edge under the headline and a vertical seam
   at ~half the stage. Deleted. The full-bleed .sea below is now the only
   ground behind the foot, retuned to start higher with a long feather. */
.stage-cta { margin-top: 0; }
.stage-note { color: var(--porcelain); text-shadow: none; }
.stage-cta .link-cta { color: var(--porcelain); }
.stage-cta .link-cta::after { background: var(--porcelain); }
.stage-cta .link-cta:hover { color: var(--sky); }
.stage-cta .link-cta:hover::after { background: var(--sky); }
.polaroid-media { position: relative; overflow: hidden; background: var(--pale); }
.polaroid .hand { font-size: var(--fs-sm); color: var(--teal); text-align: center; padding: 14px 6px 8px; }
.tape {
  position: absolute; width: 132px; height: 44px; top: -20px; left: 50%;
  transform: translateX(-50%) rotate(-2.4deg); z-index: 2;
  background: url("../assets/tex-tape.webp") center / contain no-repeat;
  filter: drop-shadow(0 3px 4px rgba(34,52,60,.25));
}
.play {
  position: absolute; left: 18px; bottom: 18px; width: 58px; height: 58px;
  border-radius: 50%; border: 0; cursor: pointer;
  background: rgba(253,248,236,.94); display: grid; place-items: center;
  box-shadow: 0 2px 10px rgba(34,52,60,.3);
}
.play svg { width: 26px; height: 26px; fill: var(--cta); margin-left: 3px; }

.sticker-barcode svg { width: 86px; height: 22px; }
/* ---- marquee tape ticker ---- */
.ticker {
  position: relative; z-index: 6; overflow: hidden;
  background: var(--ink-deep); border-block: 2px solid #000;
  transform: rotate(-1deg) scale(1.02); margin-block: -12px 0;
  box-shadow: 0 6px 18px rgba(34,52,60,.2);
}
.ticker-track {
  display: flex; width: max-content;
  animation: ticker 36s linear infinite;
  will-change: transform;
}
/* GSAP takes over the loop when JS is live (enables velocity response) */
.ticker-track.js-ticker { animation: none; }
.ticker-chunk {
  display: flex; align-items: center; white-space: nowrap;
  font-family: var(--label); font-weight: 600; font-size: var(--fs-lg);
  letter-spacing: .22em; text-transform: uppercase; color: var(--paper-bright);
  padding-block: 13px;
}
.ticker-chunk b { font-weight: 600; color: var(--sky-strong); }
.ticker-chunk .tick-sep { color: rgba(248,247,244,.45); margin-inline: 26px; font-weight: 400; }
@keyframes ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ---- torn dividers — every boundary a single real tear ---- */
.tear {
  position: absolute; left: 0; right: 0; top: -1px; height: 74px; z-index: 3;
  -webkit-mask-image: var(--tear-img); mask-image: var(--tear-img);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  pointer-events: none;
}
.tear-1 { --tear-img: url("../assets/torn-strip.png"); }
.tear-2 { --tear-img: url("../assets/torn-strip-2.png"); }
.tear-3 { --tear-img: url("../assets/torn-strip-3.png"); }
.tear-4 { --tear-img: url("../assets/torn-strip-4.png"); }
.tear--paper { background: var(--paper); }
.tear--blue { background: var(--blue); }
.tear--sky { background: var(--breather); }
.tear--pale { background: var(--pale); }
.tear--porcelain2 { background: var(--porcelain-2); }
.tear--depth3b { background: var(--depth-3b); }
.tear--depth1 { background: var(--depth-1); }
.tear--depth2 { background: var(--depth-2); }
.tear--depth3 { background: var(--depth-3); }

/* ---- credibility strip (immersion band #1) ---- */
/* PROVEN — rung 3 of the descent; the brand navy itself */
.cred { position: relative; background: var(--depth-3); padding-block: var(--s-tear-pad) var(--s-section); }
.cred-grid {
  display: grid; grid-template-columns: minmax(260px, .8fr) 1.2fr;
  gap: var(--s-grid-row) var(--s-grid-col); align-items: center;
}
.cred .label { color: var(--sky-strong); }
.cred-title { margin-bottom: 0; font-size: clamp(28px, 2.8vw, 38px); color: var(--pale); max-width: 18ch; font-weight: 400; letter-spacing: .02em; }
/* four patches now (was three): narrower each, so the row fits its 1.2fr
   column at 1440 without wrapping, and the tall shield-shaped one is height-
   capped so it does not tower over the tab-shaped Rangers patch beside it */
.patch-row {
  list-style: none; margin: 0; padding: 0;
  display: flex; gap: 18px; align-items: center; justify-content: flex-end; flex-wrap: nowrap;
}
.patch-row img {
  width: 138px; height: auto;
  filter: drop-shadow(0 10px 14px rgba(0,26,60,.5)); transition: transform .18s ease, filter .18s ease;
}
.patch-row li:nth-child(1) img { transform: rotate(-3deg); }
.patch-row li:nth-child(2) img { transform: rotate(2deg); }
.patch-row li:nth-child(3) img { transform: rotate(-1.6deg); }
.patch-row li:nth-child(4) img { transform: rotate(2.4deg); }
.patch-row li img { will-change: transform; }

/* ---- who it's for (back on paper, poster scale) ---- */
.whofor { position: relative; background: var(--breather); padding-block: var(--s-tear-pad) var(--s-section); }
.whofor-grid {
  display: grid; grid-template-columns: 1.25fr minmax(300px, .75fr);
  gap: var(--s-grid-row) var(--s-grid-col); align-items: center;
}
.whofor h2 { max-width: 20ch; font-size: clamp(40px, 4.6vw, 64px); text-wrap: balance; }
.whofor-body {
  font-size: clamp(20px, 1.8vw, 23px); line-height: 1.6; font-weight: 300;
  color: var(--ink-soft); max-width: var(--measure-wide); margin: 0;
}
.em-underlined { color: var(--blue); font-weight: 400; position: relative; }
.em-underlined::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -7px; height: 8px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 8' preserveAspectRatio='none'%3E%3Cpath d='M2 5 C 30 2, 60 7, 95 4 S 165 2, 198 5' fill='none' stroke='%23b5502f' stroke-width='2.4' stroke-linecap='round' opacity='.75'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
}
/* a real room, not a brand prop — photo print with one torn bottom edge */
.whofor-photo { margin: 0; justify-self: center; max-width: 460px; transform: rotate(-1.2deg); }
.photo-print {
  position: relative; background: var(--paper-bright);
  padding: 13px 13px 30px;
  box-shadow: 0 22px 40px -18px rgba(10,20,35,.5), 0 3px 10px rgba(10,20,35,.18);
}
/* the print's bottom edge is torn — a cream band masked by a real tear,
   flipped so the ragged edge bites up into the photo */
.photo-print::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 40px;
  background: var(--paper-bright);
  -webkit-mask-image: url("../assets/torn-strip-3.png");
  mask-image: url("../assets/torn-strip-3.png");
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  transform: scaleY(-1);
}
.photo-print img { width: 100%; height: auto; }
.whofor-photo figcaption { font-size: var(--fs-sm); color: var(--teal); text-align: center; padding-top: 12px; }

/* ---- method ---- */
.method { position: relative; background: var(--sand); padding-block: var(--s-tear-pad) var(--s-section); }
/* the head used to carry a hand note beside the headline ("One loop. Anyone,
   any time."); it floated at the far right and repeated the intro line under
   it, so it is gone. */
.method-head { margin-bottom: var(--s-head); }
.method-head h2.has-rule, .method-head h2 { max-width: 20ch; margin-bottom: 0; }
.hand-note { font-size: var(--fs-md); color: var(--rust-ink); text-wrap: balance; }
.step-name { font-size: clamp(32px, 3.6vw, 50px); color: var(--ink); font-weight: 400; text-transform: uppercase; letter-spacing: .02em; }
/* ---- method: the person + the conditions underneath ---- */
/* centred on the step's text block, not top-aligned: the drawing starts a
   fifth of the way down its own canvas, so top-aligning put the figure's head
   40px under the heading and the bands under the whole text block */
.step-viz { align-self: center; justify-self: end; width: 100%; margin-top: 0; }
.step-viz svg { width: 100%; height: auto; overflow: visible; }

/* CSS holds each step's FINAL state — reduced-motion and no-JS see the
   finished diagram; GSAP sets the "from" state at runtime. */
.viz-figure .vf {
  stroke: var(--ink-deep); stroke-width: 2.6;
  stroke-linecap: round; stroke-linejoin: round; fill: none;
}
.step-viz--1 .viz-figure { opacity: .5; transform: translateY(4px); transform-origin: 133px 118px; }
.step-viz--2 .viz-figure { opacity: .75; transform: translateY(2px); transform-origin: 133px 118px; }
.step-viz--3 .viz-figure { opacity: 1; }

.viz-band {
  stroke: var(--ink-deep); stroke-width: 2.2; fill: rgba(18,63,115,.14);
}
.viz-band--bad {
  stroke: var(--rust); stroke-width: 2.6; fill: none;
  stroke-dasharray: 13 11;
}
.viz-band--fixed { stroke: var(--signal); fill: rgba(18,63,115,.22); }

.viz-label {
  font-family: var(--label); font-size: var(--fs-tiny); font-weight: 500;
  letter-spacing: .13em; text-transform: uppercase; fill: var(--teal);
}
.viz-label--bad { fill: var(--rust); }

.viz-scan { stroke: var(--rust); stroke-width: 2; opacity: 0; }
.viz-check { stroke: var(--rust); stroke-width: 3.4; stroke-linecap: round; stroke-linejoin: round; fill: none; }
.viz-hold { stroke: var(--signal); stroke-width: 2.2; stroke-linecap: round; fill: none; opacity: .75; }

/* coaching-whiteboard flow */
/* the 780px cap dates from before the diagrams existed; it left the viz
   column stranded against a wall of empty space. Let the flow use the
   container and cap the copy column instead. */
.flow {
  list-style: none; margin: 0; padding: 0;
  /* declared HERE, not on .flow-step: .flow-arrow is a sibling, so a
     value scoped to .flow-step does not resolve in the arrow's calc()
     and the padding silently fell back to 0. */
  --flow-col1: 64px;
  --flow-gap: 36px;
}
.flow-step {
  display: grid;
  /* track 2 is max-content, not 1fr: .flow-body is capped at 46ch, so a
     flexible track parked the diagram 294px from the copy it annotates,
     leaving a canyon down the middle of the section. */
  /* Each step is its own grid, so the middle track must be FIXED, not
     max-content: step 3's shorter description narrowed its track by ~85px and
     its diagram slid left with it. One cap for all three, and the diagrams
     share an axis. */
  --flow-body: 46ch;
  /* two tracks now: the numeral lives INSIDE .flow-body so it aligns with the
     heading by construction (centring the old three-track row had parked the
     numeral 35px below the heading). */
  grid-template-columns: minmax(0, calc(var(--flow-col1) + var(--flow-gap) + var(--flow-body))) clamp(240px, 26vw, 340px);
  gap: var(--s-el) var(--flow-gap);
  align-items: center;
}
.flow-body {
  display: grid; grid-template-columns: var(--flow-col1) minmax(0, var(--flow-body));
  column-gap: var(--flow-gap); align-items: start;
}
.flow-body > :not(.flow-num) { grid-column: 2; }
.flow-num { position: relative; display: block; width: 58px; height: 52px; color: var(--rust-ink); margin-top: 4px; grid-column: 1; grid-row: 1 / span 3; }
.flow-num svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.flow-num .hand { position: absolute; inset: 0; display: grid; place-items: center; font-size: var(--fs-lg); color: var(--rust-ink); }
.step-desc { margin: 6px 0 0; font-size: var(--fs-lg); font-weight: 300; color: var(--ink-soft); }
.flow-note { font-size: var(--fs-sm); color: var(--teal); margin-top: 8px; opacity: .9; }
.flow-arrow { color: var(--rust); opacity: .75; }
.flow-arrow svg { width: 72px; height: 52px; display: block; margin-block: -10px; }
.flow-arrow-r { padding-left: calc(var(--flow-col1) + var(--flow-gap) + 5ch); }
.flow-arrow-l { padding-left: var(--flow-col1); }

/* the cohort, on an index card */
/* Chris liked this section and the bold type but not the paper it sat on, and
   said the blue with white type "looks really clean" — so the card comes off
   the taped notepad and onto a flat deep-blue panel. No rotation, no tape, no
   rule lines: it should read as the one straight, factual object on the page. */
/* narrower label column here than the About creds: these values are longer
   and were wrapping after three or four words */

/* handwritten section openers — straight on the grid; only physical objects rotate */
.hand-eyebrow { font-size: var(--fs-md); color: var(--rust-ink); margin-bottom: 14px; display: block; }
/* was --sky-strong: 3.7:1 on Signal Blue. --pale clears 4.8:1. */
.hand-eyebrow-sky { color: var(--pale); }
/* --sky is 3.7:1 on Signal Blue; --pale clears 4.8:1. This rule outranks
   .hand-eyebrow-sky on specificity, so it has to carry the accessible value. */
.on-blue .hand-eyebrow { color: var(--pale); }
.hand-sub { font-size: var(--fs-label); opacity: .75; display: inline-block; margin-left: 6px; }

.method-closer {
  font-size: clamp(22px, 2.3vw, 28px); font-weight: 300; color: var(--teal);
  max-width: 46ch; margin: var(--s-head) 0 0;
}

/* ---- outcomes (sky breather band) ---- */
/* WHAT CHANGES — rung 2 */
.outcomes { position: relative; background: var(--depth-2); padding-block: var(--s-tear-pad) var(--s-section); }
.outcome-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, minmax(280px, 1fr));
  gap: var(--s-grid-row) var(--s-grid-col);
}
.outcomes h2 { color: var(--pale); }
.outcome-grid li {
  font-size: var(--fs-lg); line-height: 1.5; font-weight: 300; color: var(--pale);
  padding-left: 22px;
}


/* ---- testimonials (immersion band #2) ---- */
.testimonials { position: relative; background: var(--depth-3); padding-block: var(--s-tear-pad) var(--s-section); }
.testimonials .label { color: var(--sky-strong); }
.quote-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--s-grid-row) var(--s-grid-col);
}
/* quotes vary in length, so the attribution is pushed to the bottom of each
   cell instead of sitting wherever the text happens to end — otherwise the
   NAME / ROLE lines stagger across the row. Matters more with real quotes. */
.quote-grid blockquote { margin: 0; height: 100%; display: flex; flex-direction: column; }
.quote-grid blockquote p { flex: 1 0 auto; }
.quote-grid blockquote footer { margin-top: auto; }
.quote-grid p {
  font-size: var(--fs-lg); line-height: 1.55; font-weight: 300; color: var(--pale);
  margin: 0 0 18px; text-indent: -.4em;
}
.quote-grid footer {
  font-family: var(--label); font-weight: 400; font-size: var(--fs-label);
  letter-spacing: .14em; text-transform: uppercase; color: var(--sky-strong);
  border-top: 1px solid rgba(194,212,255,.35); padding-top: 12px;
}

/* ---- book ---- */
/* CONTACT — rung 5, the floor. The footer is the same value: it is the
   seafloor continuing, so there is no tear between them. */
.book { position: relative; background: var(--depth-4); padding-block: var(--s-tear-pad) var(--s-section); }
.book .btn, .footer .btn { border-color: rgba(223,232,251,.5); box-shadow: 0 3px 0 rgba(223,232,251,.25); }
.book .btn:hover, .footer .btn:hover { box-shadow: 0 5px 0 rgba(223,232,251,.3); }
.book h2 { color: var(--ice); }
.book .micro { color: rgba(223,232,251,.75); }
.book .micro a { color: var(--sky-strong); }
.book-body { font-size: var(--fs-lg); line-height: 1.6; font-weight: 300; color: rgba(223,232,251,.9); max-width: var(--measure); margin: 0; }
.book .cta-row { align-items: baseline; }
.polaroid-book { max-width: 430px; padding-bottom: 14px; justify-self: center; }


/* ---- about: meet the man before the ask ---- */
/* ABOUT — rung 4 */
.about { position: relative; background: var(--depth-3b); padding-block: var(--s-tear-pad) var(--s-section); }
.about-grid {
  display: grid; grid-template-columns: 1.2fr .8fr;
  gap: var(--s-grid-row) var(--s-grid-col); align-items: center;
}
.about h2 { color: var(--porcelain); }
.about-body {
  font-size: clamp(18px, 1.6vw, 22px); line-height: 1.62; font-weight: 300;
  color: rgba(232,239,252,.92); max-width: var(--measure-wide); margin: 0 0 20px;
}
.creds { list-style: none; margin: var(--s-block) 0 0; padding: 0; border-top: 1px solid rgba(194,212,255,.28); }
.creds li {
  display: grid; grid-template-columns: var(--w-label) 1fr; gap: 16px;
  padding: 16px 0; border-bottom: 1px solid rgba(194,212,255,.18);
}
.cred-k {
  font-family: var(--label); font-weight: 500; font-size: var(--fs-label);
  letter-spacing: .15em; text-transform: uppercase;
  color: var(--sky);   /* --sky-strong was 3.7:1 on depth-2; --sky clears 5.4:1 */
  padding-top: 3px;
}
.cred-v { font-size: var(--fs-base); color: var(--pale); }
.polaroid-about { max-width: 430px; justify-self: center; transform: rotate(1.6deg); }
.tape-about { left: auto; right: 26px; transform: rotate(5deg); }

/* book closes tight now that the photo lives in About */
.book-single { max-width: var(--maxw); }
.book-single > div { max-width: 720px; }
.book-single .cta-row { margin-top: var(--s-block); }

/* ---- footer: the seafloor, with one last poster moment ---- */
.footer { position: relative; background: var(--brand-black); color: rgba(223,232,251,.8);
  padding-top: var(--s-section); }
.footer-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--s-grid-row) var(--s-grid-col); padding-block: var(--s-section) var(--s-grid-row);
}
.wordmark-footer { margin: 0 0 16px; padding: 0; min-height: 0; }
/* .footer scopes it so the nav's phone rule (.wordmark img { height: 18px })
   cannot shrink the footer mark to a 26px stamp — same class, later rule, won */
/* the horizontal lockup: MF monogram over the wordmark (brand/README: the
   secondary lockup). Taller than the stacked wordmark was, so the monogram
   stays well above its 24px minimum. */
.footer .wordmark-footer img { height: 104px; }
.tagline { font-size: var(--fs-base); max-width: 40ch; margin: 0; }
/* stacked links can't use an overflowing hit area — a 44px pseudo would
   overlap the neighbour and steal its tap. Real height + a real 8px gap. */
.footer-links { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; justify-self: end; }
.footer-links a {
  color: var(--sky); text-decoration: none; border-bottom: 1px solid rgba(194,212,255,.4);
  padding: 10px 0 12px; font-family: var(--label); font-weight: 400; font-size: var(--fs-label-lg);
  letter-spacing: .1em; text-transform: uppercase;
}
.footer-links a:hover { color: var(--paper-bright); border-color: var(--paper-bright); }
.footer-legal {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  border-top: 1px solid rgba(232,239,252,.18); padding-block: 18px 26px;
  font-family: var(--label); font-weight: 400; font-size: var(--fs-label);
  letter-spacing: .18em; text-transform: uppercase; color: rgba(232,239,252,.6);
}
/* ---- dive below the surface — ambient water behind the whole page ---- */
.dive {
  position: fixed; inset: 0; z-index: 39; pointer-events: none;
  mix-blend-mode: multiply; overflow: hidden;
}
body.static-mode .dive { display: none; }
.dive-swells { position: absolute; left: 0; right: 0; height: 42vh; will-change: transform; }
.dive-primary { top: 58%; }
.dive-echo { top: 96%; }
.dfoam { position: absolute; left: -3%; width: 106%; height: auto; opacity: .34; }
.dive-primary .dfoam-a { top: 0; animation: dive-sway 14s ease-in-out infinite alternate; }
.dive-primary .dfoam-b { top: 9vh; opacity: .26; animation: dive-sway 19s ease-in-out infinite alternate-reverse; }
.dive-echo .dfoam-a { top: 0; opacity: .22; animation: dive-sway 17s ease-in-out infinite alternate; }
@keyframes dive-sway { from { transform: translateX(-2%); } to { transform: translateX(2%); } }
.dive-tint {
  position: absolute; inset: 0; opacity: 0;
  background: linear-gradient(to bottom, rgba(18,63,115,.06), rgba(3,11,24,.32));
  will-change: opacity;
}

/* ---- hand-drawn marker underline beneath each section heading ----
   Same pen language as .flow-num / .flow-arrow, extended site-wide.
   Inherits the heading's own ink so it works on light and deep bands alike;
   the light paper sections take the rust accent instead. */
.head-rule {
  /* width is set per-heading from the last line's measured width in
     js/main.js; 320px was a fixed guess that overhung by 196px on one
     heading and fell 237px short on another. */
  /* Taken out of flow: as a child it added its own height to every heading
     box, which broke .method-head's flex-end alignment by 26px and taxed
     the vertical rhythm of every section. */
  position: absolute; top: 100%; left: 0; margin-top: 8px;
  display: block; width: var(--rule-w, min(100%, 320px)); height: 13px;
  margin-top: 14px; overflow: visible;
  color: var(--rule-ink, currentColor);
}
.head-rule path { fill: none; stroke: currentColor; stroke-width: 3.2; stroke-linecap: round; opacity: .85; }
.method, .whofor { --rule-ink: var(--rust); }
h2.has-rule { position: relative; margin-bottom: var(--s-block); }

/* ---- outcomes: the left rule is a drawn pen stroke, not a border ---- */
.outcome-grid li { position: relative; }
.outcome-rule {
  position: absolute; left: 0; top: 0; width: 7px; height: 100%;
  overflow: visible; pointer-events: none;
}
.outcome-rule path {
  fill: none; stroke: var(--sky-strong); stroke-width: 3;
  stroke-linecap: round; vector-effect: non-scaling-stroke;
}

/* ---- motion base states (GSAP animates from these) ---- */
/* no will-change here: it promoted 27 elements to permanent compositor
   layers for animations that run once. GSAP sets it per-tween itself. */
.gs-reveal { }

/* ---- reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
  /* must match the specificity of .dive-primary .dfoam-a etc (0-2-0);
     a bare .dfoam reset (0-1-0) silently lost and the loops kept running */
  .dive-primary .dfoam-a, .dive-primary .dfoam-b, .dive-echo .dfoam-a,
  .dfoam { animation: none; }
  .ticker-track { animation: none; }
  .hero-accent::before, .hero-accent::after { display: none; }
  .sea { transform: none; }
  .dive { display: none; }
  html { scroll-behavior: auto; }
}

/* ---- responsive ---- */
@media (max-width: 900px) {
  .stage-full { min-height: 480px; }
  .stage-col { gap: clamp(18px, 2.6vh, 32px); }
  /* tablet landscape (861-900): the stage is not full height here, so the
     bottom-anchored polaroid sits beside the foot. Measured at 900px: a 340px
     polaroid started at x=524 while the foot ran to 556 — the note went under
     the tape. Narrower both sides: 48px clear at 900, more above it. */
  .stage-col { grid-template-columns: minmax(0, 1fr) minmax(240px, min(600px, 60vh, 44%)); }
  .cred-grid { grid-template-columns: 1fr; gap: var(--s-block); }
  .about-grid { grid-template-columns: 1fr; gap: var(--s-grid-row); }
  .flow { max-width: 780px; }
  .flow-step { grid-template-columns: minmax(0, 1fr); }
  .step-viz { grid-column: 1; justify-self: start; max-width: 280px; margin-top: var(--s-el); margin-left: calc(var(--flow-col1) + var(--flow-gap)); }
  .polaroid-about { justify-self: start; max-width: 330px; }
  .creds li { grid-template-columns: 140px 1fr; }
  .patch-row { justify-content: flex-start; }
  .whofor-grid { grid-template-columns: 1fr; gap: var(--s-grid-row); }
  .whofor-object { justify-items: start; }
  .patch-pp { width: 240px; }
  .whofor-photo { max-width: 100%; justify-self: start; }
  .outcome-grid { grid-template-columns: 1fr; gap: var(--s-block); }
  .flow-arrow svg { width: 64px; }
  .footer-links { justify-self: start; }
  .ticker-chunk { font-size: var(--fs-base); }
}
@media (max-width: 860px) {
  /* linearize the stage: normal-flow stacking over the artwork — no absolute collisions */
  .stage-full { height: auto; min-height: 100svh; max-height: none; padding: 17% 6% 130px; }
  body.static-mode .stage-full { height: auto; }
  /* Anton is condensed, so the two-line break fits at a larger size here than
     any previous hero face; measured against the longest line ('People aren't'). */
  .stage-col { position: static; padding: 0; gap: 0; display: flex; flex-direction: column; align-items: flex-start; max-width: none; margin-inline: 0; }
  .giant { z-index: 5; font-size: min(15.5vw, 118px); }
  .giant-under { display: none; } /* ice crossfade is a desktop nicety; mobile keeps the fade-out */
  .stage-foot { z-index: 5; max-width: 100%; margin-top: 22px; gap: 14px; }
  .polaroid-hero { position: relative; z-index: 5; right: auto; top: auto; width: 82%; margin: 48px auto 0; }
  .stage-cta { margin: 0; gap: 16px; }
  /* The hero photo behind this link is bright water (luminance .50-.86
     measured), so the pale link failed at 1.09:1 and had been patched with a
     translucent grey slab that read as a broken button. Dark ink on the same
     water measures 9.55:1, needs no box, and matches the headline. */
  /* the underline is drawn by ::after, not border-bottom (which is set to 0
     at :259), so the rule has to target the pseudo-element */
  .stage-cta .link-cta { color: var(--ink-deep); }
  .stage-cta .link-cta::after { background: var(--ink-deep); }
  /* the mobile hero linearises, so the foot ends up ABOVE the waterline over
     bright water — it keeps dark type, unlike the desktop foot which sits in
     the depth wash. Both halves of the foot must agree. */
  .stage-note { margin: 0; font-size: var(--fs-label-lg); color: var(--ink-deep); }

  .flow-step { --flow-col1: 52px; --flow-gap: 16px; }
  .patch-row { flex-wrap: nowrap; gap: 12px; justify-content: flex-start; }
  .patch-row img { width: auto; height: 64px; }
  .creds li { grid-template-columns: 1fr; gap: 4px; padding: 12px 0; }
  .polaroid-about { max-width: 100%; }
  .polaroid { transform: rotate(-.8deg); }

  .nav-inner { padding: 11px 18px; }
  .wordmark img { height: 18px; }
  .footer .wordmark-footer img { height: 84px; }
  .nav .btn { white-space: nowrap; font-size: var(--fs-label-lg); padding: 10px 14px;
    display: inline-flex; align-items: center; min-height: 44px; }
  .cta-row { gap: 18px; }
  .footer-legal { flex-direction: column; align-items: flex-start; }
  .tear { height: 48px; }
  .stage-tear { height: 54px; }
}

/* ---- the method diagram on phones ----
   It used to be display:none below 560px, which hid the one place the
   abstract claim ("find the condition underneath") becomes a picture — from
   exactly the audience Chris texts the link to. It is shown instead, at a
   size that costs little scroll, below the copy rather than beside it. */
@media (max-width: 900px) {
  .step-viz {
    display: block; grid-column: 1; margin-left: calc(var(--flow-col1) + var(--flow-gap)); justify-self: start;
    max-width: 260px; margin-top: var(--s-el);
  }
}
@media (max-width: 560px) {
  .step-viz { max-width: 210px; margin-top: 14px; }
}

/* the play button is a real tab stop with no handler until the video exists */
.play[disabled] { cursor: default; opacity: .85; }
.play[disabled]:hover { transform: none; }

/* ============================================================
   Chris's copy round — three new sections on the existing system
   ============================================================ */

/* THE PROBLEM — first paper section after the hero */
.problem { position: relative; background: var(--sand); padding-block: var(--s-section); }
.problem h2 { max-width: 20ch; }
/* the two-column drawn-rule list, on a light ground: the rule goes rust */
.point-grid {
  list-style: none; margin: var(--s-block) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, minmax(280px, 1fr));
  gap: var(--s-grid-row) var(--s-grid-col);
}
.point-grid li {
  position: relative; padding-left: 22px;
  font-size: var(--fs-md); line-height: 1.55; font-weight: 300; color: var(--ink-soft);
}
.point-grid li b, .outcome-grid li b { font-weight: 600; color: var(--ink); }
.outcome-grid li b { color: var(--ice); }
.point-grid .outcome-rule path { stroke: var(--rust); }
.section-closer {
  margin: var(--s-block) 0 0; max-width: 52ch;
  font-size: var(--fs-md); line-height: 1.55; color: var(--ink);
}
.on-blue .section-closer { color: var(--pale); }

/* WHY IT STAYS BROKEN — rung 1, short and punchy, the old cred band's shape */
.why { position: relative; background: var(--depth-1); padding-block: var(--s-section); }
.why-grid {
  display: grid; grid-template-columns: minmax(260px, .9fr) 1.1fr;
  gap: var(--s-grid-row) var(--s-grid-col); align-items: start;
}
.why-body { margin: 0; font-size: var(--fs-md); line-height: 1.6; color: var(--pale); max-width: 46ch; }

/* THE SYSTEM and WAYS TO BRING IT IN share one card component, so they read
   as siblings. The card is the navy panel Chris said he liked from the old
   cohort section, carried forward. */
.system { position: relative; background: var(--sand-2); padding-block: var(--s-tear-pad) var(--s-section); }
.system h2 { max-width: 26ch; text-wrap: balance; }
.ways   { position: relative; background: var(--sand-2); padding-block: var(--s-tear-pad) var(--s-section); }
.ways h2 { max-width: 16ch; }
/* THE SYSTEM head: headline + lede beside the Performance OS wheel. The wheel
   is Chris's infographic rebuilt as native SVG in the site's own type and
   palette (seven domains on the navy ladder, psychological safety as the ring).
   The infographic's bottom loop (Align / Calibrate / Sustain) is NOT here: it
   conflicts with the Diagnose / Intervene / Verify loop in his copy. */
.system-head {
  display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr);
  gap: var(--s-grid-row) var(--s-grid-col); align-items: center;
}
.os-wheel { margin: 0; justify-self: end; width: min(100%, 560px); }
/* the whole wheel tilts a few degrees toward the pointer (JS sets --tx/--ty) */
.wheel-tilt { perspective: 900px; }
.wheel {
  display: block; width: 100%; height: auto; overflow: visible;
  transform: rotateX(var(--tx, 0deg)) rotateY(var(--ty, 0deg));
  transition: transform .35s cubic-bezier(.2,.7,.2,1);
  will-change: transform;
}
.wheel-wedges { stroke: var(--sand-2); stroke-width: 3; stroke-linejoin: round; }
.wheel-wedge { cursor: pointer; outline: none; }
.wheel-wedge text { stroke: none; pointer-events: none; }
/* the lift lives on an inner group so the reveal's scale (outer group) and the
   hover/active translate never write the same transform */
.wedge-lift { transform: translate(0, 0); transition: transform .18s cubic-bezier(.2,.7,.2,1); will-change: transform; }
.wheel-wedge.is-active .wedge-lift,
.wheel-wedge:hover .wedge-lift { transform: translate(var(--lx), var(--ly)); }
.wheel-wedge:hover .wedge-lift { transition-duration: .18s; }
.wedge-glow { fill: var(--navy); opacity: 0; transition: opacity .18s ease-out; stroke: none; }
.wheel-wedge--dark .wedge-glow { fill: var(--sand); }
.wheel-wedge.is-active .wedge-glow, .wheel-wedge:hover .wedge-glow { opacity: .1; }
.wheel-wedge--dark.is-active .wedge-glow, .wheel-wedge--dark:hover .wedge-glow { opacity: .14; }
.wheel-wedge.is-active .wedge-fill { stroke: var(--sand); stroke-width: 4; }
.wheel-wedge:focus-visible .wedge-fill { stroke: var(--rust); stroke-width: 5; }
.wheel-label { font-family: var(--label); font-weight: 700; font-size: 16.5px; letter-spacing: 0; fill: var(--ink); text-transform: uppercase; }
.wheel-wedge--dark .wheel-label { fill: var(--pale); }
.wheel-label--short { display: none; font-size: 20px; }
/* the two longest words ("sustainability", "confidence") clear the radial
   cut lines at a hair less size; the other five stay full size */
.wheel-label--long.wheel-label--tight { font-size: 15px; }
.wheel-label--short.wheel-label--tight { font-size: 18.5px; }
.wheel-ringlabel { font-family: var(--label); font-weight: 600; font-size: 15px; letter-spacing: .3em; fill: var(--sand); text-transform: uppercase; }
.wheel-ringlabel--soft { font-weight: 400; letter-spacing: .22em; font-size: 13px; fill: var(--sky); }
.wheel-core-name { font-family: var(--poster); font-size: 34px; letter-spacing: .02em; fill: var(--sand); }
.wheel-core-sub { font-family: var(--label); font-weight: 600; font-size: 11px; letter-spacing: .3em; fill: var(--sky); text-transform: uppercase; }
/* idle life: the ring's words drift round, slowly — a property the reveal never touches */
.wheel-ring-text { transform-origin: 300px 300px; animation: wheel-drift 110s linear infinite; }
@keyframes wheel-drift { to { transform: rotate(360deg); } }
/* the caption: whichever domain is active. Fixed height so the swap never shifts layout. */
.wheel-caption { text-align: center; margin: 14px auto 0; min-height: 96px; }
.wheel-caption-eyebrow { margin: 0 0 2px; color: var(--rust-ink); font-size: var(--fs-md); }
.wheel-caption-swap { transition: opacity .18s ease-in; }
.wheel-caption-swap.is-out { opacity: 0; }
.wheel-caption-name { margin: 0; font-family: var(--poster); text-transform: uppercase; font-size: clamp(24px, 2.2vw, 30px); color: var(--navy); line-height: 1.05; }
.wheel-caption-sub { margin: 4px 0 0; font-size: var(--fs-md); color: var(--ink-soft); }
@media (prefers-reduced-motion: reduce) {
  .wheel-ring-text { animation: none; }
  .wheel, .wedge-lift, .wedge-glow, .wheel-caption-swap { transition: none; }
}
@media (max-width: 900px) {
  .system-head { grid-template-columns: 1fr; }
  .os-wheel { justify-self: center; width: min(100%, 420px); }
}
@media (max-width: 600px) {
  /* full names cannot reach 12px inside a seven-wedge ring at phone width:
     one word per wedge, the caption carries the full name and its line */
  .os-wheel { width: calc(100% + 2 * var(--gutter)); max-width: 420px; margin-inline: calc(-1 * var(--gutter)); }
  .wheel-label--long { display: none; }
  .wheel-label--short { display: block; }
  .wheel-tilt { perspective: none; }
  .wheel { transform: none; }
}
.os-grid {
  list-style: none; margin: var(--s-block) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-el);
}
.os-grid--four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.os-card {
  position: relative; background: var(--depth-3); color: var(--pale);
  padding: 28px 26px 30px;
  box-shadow: 0 22px 38px -20px rgba(6,16,30,.5);
  /* a hairline of the sand shows at the top edge, like the tape on the props */
  border-top: 3px solid rgba(232,227,217,.55);
}
.os-label { font-size: var(--fs-sm); color: var(--sky); margin: 0 0 10px; }
.os-title {
  font-family: var(--poster); font-weight: 400; text-transform: uppercase;
  font-size: clamp(22px, 2vw, 28px); line-height: 1.05; letter-spacing: .015em;
  color: var(--ice); margin: 0 0 12px;
}
.os-body { margin: 0; font-size: var(--fs-base); line-height: 1.55; color: var(--pale); font-weight: 300; }
.ways-foot {
  margin-top: var(--s-block);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-el) var(--s-block);
}
.ways-foot .hand-note { margin: 0; }

/* PROVEN — the credentials list under the patches */
.cred-body { margin: 14px 0 0; font-size: var(--fs-md); line-height: 1.6; color: var(--pale); max-width: 40ch; }
.creds--proven { margin-top: var(--s-block); max-width: 720px; }
.method-intro { margin-bottom: var(--s-block); }

@media (max-width: 1080px) {
  .os-grid, .os-grid--four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  .why-grid { grid-template-columns: 1fr; }
  .point-grid { grid-template-columns: 1fr; gap: var(--s-block); }
}
@media (max-width: 560px) {
  .os-grid, .os-grid--four { grid-template-columns: 1fr; }
}
