/* =========================================================
   IVEY LIFTS — 1:1 Coaching
   Palette: near-black canvas, graphite surfaces, gray-white type,
            ivy green accent, and a three-plane foliage ramp.

   Forked from the FushiFit build. Structure is the same. Two things
   changed and both matter:

     1. The accent moved from Ty's sampled 132deg green to a cooler
        143deg ivy green, and the type went from cream to a neutral
        gray-white, because the brief was black, gray and ivy green.

     2. There is a FOLIAGE ramp (--ivy-t0..t5, plus --ivy-vein and
        --ivy-stem) that is entirely separate from the accent ramp. The
        ivy is art, not UI: it never carries text, so it lives below the
        4.5:1 floor on purpose. See the note on that block for why it is
        still lifted brighter than the photograph it was drawn from.

   Green is far too light to carry the page type on top of it, so every
   accent-coloured FILL takes --accent-ink (near-black) instead of
   --text. Grep for --accent-ink before adding a new filled element.
   ========================================================= */

:root{
  /* --- canvas --- */
  --bg:         #0a0b0a;   /* page */
  --bg-alt:     #101210;   /* alternating sections */
  --surface:    #161816;   /* cards */
  --surface-2:  #1c1f1d;   /* raised cards / inputs */
  --line:       rgba(241,243,241,.10);
  --line-2:     rgba(241,243,241,.18);

  /* --- type ---
     Neutral gray-white, not the cream this was forked from. The brief
     named black, gray and ivy green, and cream on an ivy page reads
     as a second warm accent fighting the green. */
  --text:       #f1f3f1;   /* headings + body   (17.7:1 on bg) */
  --muted:      #a4aaa5;   /* body copy, gray   (8.3:1 on bg) */
  --muted-2:    #757b77;   /* meta, labels      (4.6:1 on bg) */

  /* Ivy green, hue 143. Cooler and bluer than the forest green on the
     FushiFit build (132) so the two sites don't read as the same
     template, and closer to what an actual ivy leaf does under a cool
     light. Chosen rather than sampled: there is no footage of Ivey yet.
     When his clips land, re-sample the hue off them the way Ty's was
     done, keep the hue only, and rebuild this ramp around it.

     Every translucent accent in this file goes through the two -rgb
     tokens, so a retheme is this block and nothing else. Keep each one
     in sync with its hex twin below. */
  --accent-rgb:    58,141,90;
  --accent-hi-rgb: 74,181,115;

  /* Darkened a step on request. 4.8:1 is close to the floor here and the
     thing that sets it is not the page, it is the BUTTON: --accent-btn
     carries near-black --accent-ink, and one more step down puts that pair
     under 4.5:1. Going darker than this means moving the button label to
     --text and rebalancing the whole set, not just nudging this value. */
  --accent:        #3a8d5a;  /* headings, rules, icons   (4.8:1 on bg) */
  --accent-hi:     #4ab573;  /* hover, small labels      (7.7:1 on bg) */
  --accent-btn:    #3a8d5a;  /* button fill              (4.7:1 w/ ink) */
  --accent-btn-hi: #419f65;
  --accent-ink:    #07120b;  /* type + icons sitting ON an accent fill */
  --accent-deep:   #123620;
  --accent-dim:    rgba(var(--accent-rgb),.10);
  --accent-line:   rgba(var(--accent-rgb),.30);

  /* --- foliage ---
     A six step ramp, not three flat planes. Built against a photograph of
     real ivy, where the giveaway is that a near-black leaf and a pale sage
     one sit on the same runner. Three tones read as three cut-out layers;
     six with per-leaf jitter reads as foliage.

     The whole ramp was darkened a step on request. It is NOT a uniform
     shift: the light end came down further than the dark end, so the ramp
     reads deeper without the bottom falling out of it. t0 is the floor and
     sits at about 1.6:1 against the page. Below roughly 1.4:1 these stop
     being visible at all on a bright screen in a bright room, which is how
     these get reviewed, so that is the limit rather than a preference.

     These are ART, not UI. They never carry text, so they sit below the
     4.5:1 floor on purpose. */
  --ivy-t0:   #173b23;   /* 1.6:1 on bg, deepest shadow leaves */
  --ivy-t1:   #1b462b;
  --ivy-t2:   #225335;
  --ivy-t3:   #2e6644;
  --ivy-t4:   #3e7a57;
  --ivy-t5:   #518a6b;   /* 4.9:1 on bg, the ones catching light */

  /* Pale palmate veins. The single most identifiable thing about an ivy
     leaf, and the reason these read as ivy rather than as green shapes.
     One colour across the whole ramp, the way the photograph has it: strong
     against the dark leaves, nearly lost on the pale ones. */
  --ivy-vein: #b9d4bd;

  /* Woody stem. Ivy runners are brown bark, not green, and painting them
     green was the other thing that made the first pass read as clip art.
     Desaturated far enough to sit inside a black and gray page. */
  --ivy-stem: #544838;

  /* Shine stops. The band that travels through the accent gradients.
     Both stops are lighter than --accent, never darker, so the contrast
     floor on small accent text holds through the whole animation. */
  --accent-mid:    #81cf9f;
  --accent-peak:   #c9edd7;

  /* Validation red. The accent can't do this job on this build: a form
     error rendered in green reads as a tick, not a problem. */
  --warn:          #ff6b6b;  /* form errors             (7.1:1 on bg) */

  /* --- fonts --- */
  --display: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --sans:    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

  --nav-h: 79px;

  --wrap: 1180px;
  --r:    12px;

  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 12px 32px rgba(0,0,0,.35);
  --ease: cubic-bezier(.22,.61,.36,1);
}

*,*::before,*::after{ box-sizing:border-box; }

html{
  scroll-behavior:smooth;
  scroll-padding-top:96px;
  -webkit-text-size-adjust:100%;
  background:var(--bg);
}

body{
  margin:0;
  background:var(--bg);
  color:var(--text);
  font-family:var(--sans);
  font-size:17px;
  line-height:1.7;
  font-weight:350;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  overflow-x:hidden;
}

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

h1,h2,h3,h4{
  font-family:var(--display);
  font-weight:700;
  line-height:1.05;
  letter-spacing:-.02em;
  margin:0;
  color:var(--text);
  font-variation-settings:"wdth" 112;
}

p{ margin:0 0 1.15em; }
p:last-child{ margin-bottom:0; }

a{ color:inherit; text-decoration:none; }

::selection{ background:var(--accent-btn); color:var(--accent-ink); }

.wrap{ width:100%; max-width:var(--wrap); margin:0 auto; padding:0 28px; }

.accent{ color:var(--accent); }

.skip-link{
  position:absolute; left:-9999px; top:0; z-index:200;
  background:var(--accent-btn); color:var(--accent-ink); padding:12px 20px; font-weight:600;
  border-radius:0 0 8px 0;
}
.skip-link:focus{ left:0; }

/* =============== SHARED BITS =============== */

.eyebrow{
  font-family:var(--sans);
  font-size:12px;
  font-weight:600;
  letter-spacing:.18em;
  text-transform:uppercase;
  color:var(--accent);
  margin:0 0 18px;
  display:flex;
  align-items:center;
  gap:12px;
}
.eyebrow::before{
  content:"";
  width:28px; height:1px;
  background:var(--accent-line);
  flex:none;
}
.eyebrow--center{ justify-content:center; }
.eyebrow--center::after{
  content:"";
  width:28px; height:1px;
  background:var(--accent-line);
  flex:none;
}

.btn{
  display:inline-flex;
  align-items:center;
  gap:10px;
  font-family:var(--sans);
  font-size:15px;
  font-weight:600;
  letter-spacing:.01em;
  padding:15px 28px;
  border-radius:8px;
  border:1px solid transparent;
  cursor:pointer;
  transition:background .25s var(--ease), border-color .25s var(--ease),
             transform .25s var(--ease), color .25s var(--ease);
  text-align:center;
  line-height:1.2;
}
.btn:focus-visible{ outline:2px solid var(--accent-hi); outline-offset:3px; }

.btn--primary{
  background:var(--accent-btn);
  color:var(--accent-ink);
}
.btn--primary:hover{ background:var(--accent-btn-hi); transform:translateY(-1px); }

.btn--ghost{
  background:transparent;
  color:var(--text);
  border-color:var(--line-2);
}
.btn--ghost:hover{ border-color:var(--accent); color:var(--accent-hi); }

.btn--lg{ padding:18px 34px; font-size:16px; }
.btn--block{ width:100%; justify-content:center; }
.btn[disabled]{ opacity:.4; cursor:not-allowed; transform:none; }

.section{ padding:clamp(72px,10vw,128px) 0; }
.section--alt{ background:var(--bg-alt); }

.section__head{ max-width:760px; margin:0 0 56px; }
.section__head--center{ margin-left:auto; margin-right:auto; text-align:center; }
.section__title{ font-size:clamp(32px,4.6vw,52px); text-transform:uppercase; }
.section__lede{
  color:var(--muted);
  font-size:clamp(17px,1.7vw,19px);
  margin-top:20px;
  max-width:62ch;
}
.section__head--center .section__lede{ margin-left:auto; margin-right:auto; }

/* reveal-on-scroll */
.reveal{ opacity:0; transform:translateY(22px); transition:opacity .7s var(--ease), transform .7s var(--ease); }
.reveal.is-in{ opacity:1; transform:none; }
@media (prefers-reduced-motion:reduce){
  .reveal{ opacity:1; transform:none; transition:none; }
  html{ scroll-behavior:auto; }
}

/* =============== INTRO =============== */
/* Self-terminating: the CSS animation hides the overlay on its own at 3.15s,
   so a blocked or broken script can never leave a visitor staring at it. */

.intro{
  position:fixed; inset:0; z-index:999;
  display:grid; place-items:center;
  padding:24px;
  overflow:hidden;
  background:
    radial-gradient(70% 55% at 50% 46%, rgba(var(--accent-rgb),.13), transparent 62%),
    var(--bg);
  animation:introAuto 3.15s linear forwards;
}
.intro.gone{ display:none; }
@keyframes introAuto{
  0%,80%{ opacity:1; }
  100%{ opacity:0; visibility:hidden; pointer-events:none; }
}

.intro__inner{ text-align:center; }

.intro__name{
  font-family:var(--display);
  font-weight:800;
  text-transform:uppercase;
  letter-spacing:-.025em;
  line-height:1;
  font-size:clamp(36px,8.6vw,104px);
  white-space:nowrap;
  font-variation-settings:"wdth" 108;
}
.intro__l{
  display:inline-block;
  opacity:0;
  will-change:transform,opacity;
  transform:translateX(var(--dx));
  animation:slideAcross .85s cubic-bezier(.16,.84,.34,1) var(--d) forwards;
}
.intro__sp{ display:inline-block; width:.3em; }
.intro__l--b{ color:var(--accent); }
@keyframes slideAcross{ to{ opacity:1; transform:translateX(0); } }

.intro__rule{
  display:block;
  height:3px;
  margin:22px auto 0;
  border-radius:2px;
  background:var(--accent);
  transform:scaleX(0);
  animation:drawRule .65s cubic-bezier(.16,.84,.34,1) 1s forwards;
}
@keyframes drawRule{ to{ transform:scaleX(1); } }

.intro__role{
  margin:18px 0 0;
  font-family:var(--sans);
  font-size:clamp(10px,1.3vw,13px);
  font-weight:600;
  letter-spacing:.34em;
  text-transform:uppercase;
  color:var(--muted);
  opacity:0;
  animation:riseIn .7s var(--ease) 1.35s forwards;
}
@keyframes riseIn{
  from{ opacity:0; transform:translateY(10px); }
  to{ opacity:1; transform:none; }
}

@media (prefers-reduced-motion:reduce){
  .intro{ display:none; }
}

/* =============== NAV =============== */

.nav{
  position:fixed; inset:0 0 auto 0; z-index:100;
  background:transparent;
  transition:background .3s var(--ease), border-color .3s var(--ease), backdrop-filter .3s var(--ease);
  border-bottom:1px solid transparent;
}
.nav.is-stuck{
  background:rgba(10,11,10,.88);
  backdrop-filter:saturate(140%) blur(14px);
  -webkit-backdrop-filter:saturate(140%) blur(14px);
  border-bottom-color:var(--line);
}
.nav__inner{
  display:flex; align-items:center; justify-content:space-between;
  gap:24px; height:var(--nav-h);
}
.brand{
  font-family:var(--display);
  font-weight:700;
  font-size:17.5px;
  letter-spacing:.16em;
  text-transform:uppercase;
  font-variation-settings:"wdth" 100;
  display:flex; align-items:baseline; gap:8px;
}
.brand em{ font-style:normal; color:var(--accent); }

.nav__links{ display:flex; align-items:center; gap:34px; }
.nav__links a{
  font-size:14px; font-weight:500; letter-spacing:.1em; text-transform:uppercase;
  color:var(--muted);
  transition:color .2s var(--ease);
}
.nav__links a:hover{ color:var(--text); }
.nav .btn{ padding:12px 22px; font-size:14px; letter-spacing:.06em; }
/* `.nav__links a` is (0,1,1) and beats `.btn--primary` (0,1,0), so the nav's
   muted link colour was winning and painting the primary CTA's label gray on
   its own fill. That measured 1.8:1 against the accent. Re-assert the ink at
   matching specificity. */
.nav__links a.btn--primary{ color:var(--accent-ink); }

.nav__toggle{
  display:none; background:none; border:0; color:var(--text);
  padding:8px; cursor:pointer;
}

@media (max-width:900px){
  .nav__links{
    position:fixed; inset:72px 0 auto 0;
    flex-direction:column; align-items:stretch; gap:0;
    background:rgba(10,11,10,.98);
    backdrop-filter:blur(14px);
    border-bottom:1px solid var(--line);
    padding:12px 28px 26px;
    transform:translateY(-12px);
    opacity:0; pointer-events:none;
    transition:opacity .25s var(--ease), transform .25s var(--ease);
  }
  .nav__links.is-open{ opacity:1; transform:none; pointer-events:auto; }
  .nav__links a{ padding:15px 0; border-bottom:1px solid var(--line); font-size:14px; }
  .nav__links .btn{ margin-top:18px; justify-content:center; }
  .nav__toggle{ display:block; }
}

/* =============== SECTION RAIL ===============
   A spine running the full height of the viewport with a dot per section. The
   fill tracks scroll position and the active dot branches out to name the
   section.

   Shown at every width, including phones. What changes below 1440px is the
   label: above that there is gutter to park it in, so it simply stays visible
   for the active section. Below it there is no gutter at all, the copy starts
   28px from the edge, so a permanent label would sit on top of the text.
   There it flashes for a moment when the section changes and then fades back
   out, leaving just the dots. .is-flash is driven from main.js.

   Dots are spread with space-between across the padded height rather than
   sitting in a fixed-gap cluster, so the rail reads as a scale for the whole
   page. --rail-top clears the fixed nav; the track is inset by both paddings
   plus half a dot so the line starts and ends on a dot centre, not in space. */

.rail{
  --rail-top: calc(var(--nav-h) + 18px);
  --rail-bottom: 34px;
  --dot: 11px;

  position:fixed;
  left:clamp(10px, 1.1vw, 14px);
  top:0;
  bottom:0;
  z-index:90;
  display:flex;
  flex-direction:column;
  justify-content:space-between;
  padding:var(--rail-top) 0 var(--rail-bottom);
  pointer-events:none;
}

.rail__track{
  position:absolute;
  left:calc(var(--dot) / 2);
  top:calc(var(--rail-top) + var(--dot) / 2);
  bottom:calc(var(--rail-bottom) + var(--dot) / 2);
  width:1px;
  background:var(--line-2);
}
.rail__fill{
  position:absolute;
  left:0; top:0;
  width:1px;
  height:0;
  background:var(--accent);
  box-shadow:0 0 8px rgba(var(--accent-rgb),.5);
}

.rail__item{
  position:relative;
  display:flex;
  align-items:center;
  height:var(--dot);
  cursor:pointer;
  pointer-events:auto;
  width:max-content;
}
.rail__dot{
  flex:none;
  width:var(--dot); height:var(--dot);
  border-radius:50%;
  border:1px solid var(--line-2);
  background:var(--bg);
  position:relative;
  z-index:1;
  transition:border-color .3s var(--ease), background .3s var(--ease),
             box-shadow .3s var(--ease), transform .3s var(--ease);
}

.rail__label{
  font-family:var(--sans);
  font-size:10px;
  font-weight:600;
  letter-spacing:.12em;
  text-transform:uppercase;
  color:var(--muted-2);
  white-space:nowrap;
  opacity:0;
  transform:translateX(-4px);
  /* Lifts the label off whatever it lands on. Below 1440 it can cross the
     copy, and the page is dark everywhere, so a dark halo is enough. */
  text-shadow:0 0 6px rgba(10,11,10,.95), 0 0 14px rgba(10,11,10,.85);
  transition:opacity .32s var(--ease), transform .32s var(--ease), color .32s var(--ease);
}
/* The branch. Grows out of the dot before the text rather than the label just
   fading in beside it, so the dot reads as the thing doing the pointing. */
.rail__label::before{
  content:"";
  display:inline-block;
  vertical-align:middle;
  width:0;
  height:1px;
  margin-right:0;
  background:var(--accent);
  transition:width .32s var(--ease), margin-right .32s var(--ease);
}

.rail__item:hover .rail__dot{ border-color:var(--accent); transform:scale(1.15); }
.rail__item:hover .rail__label{ opacity:1; transform:none; color:var(--muted); }
.rail__item:hover .rail__label::before{ width:12px; margin-right:8px; }

.rail__item.is-on .rail__dot{
  border-color:var(--accent);
  background:var(--accent);
  box-shadow:0 0 0 4px rgba(var(--accent-rgb),.14), 0 0 12px rgba(var(--accent-rgb),.5);
}
.rail__item.is-on .rail__label{ opacity:1; transform:none; color:var(--text); }
.rail__item.is-on .rail__label::before{ width:14px; margin-right:9px; }

.rail__item:focus-visible{ outline:2px solid var(--accent-hi); outline-offset:6px; border-radius:2px; }

/* No gutter to live in: the active label is a flash, not a fixture. */
@media (max-width:1439px){
  .rail__item.is-on .rail__label{ opacity:0; transform:translateX(-4px); }
  .rail.is-flash .rail__item.is-on .rail__label{ opacity:1; transform:none; }
  .rail__item:hover .rail__label{ opacity:1; transform:none; }
}

@media (max-width:720px){
  .rail{ --dot:9px; --rail-bottom:26px; }
  .rail__label{ font-size:9.5px; letter-spacing:.1em; }
  .rail__item.is-on .rail__label::before{ width:11px; margin-right:7px; }
}

@media (prefers-reduced-motion:reduce){
  .rail__dot,
  .rail__label,
  .rail__label::before{ transition:none; }
}

/* =============== HERO ===============
   Copy left, the coach's shot bled off the right edge. It is NOT in a frame:
   it's absolutely positioned, masked to nothing on its left side, and faded
   top and bottom, so it dissolves into the page and the headline can run over
   the top of it. Three things keep the type legible on it, and all three are
   load bearing:

     1. the mask, which kills the image well before the copy column ends
     2. .hero__scrim, a left-to-right darkening across the whole section
     3. .hero__copy::before, a pool of darkness under the text itself

   EVERY NUMBER IN THIS BLOCK WAS MEASURED AGAINST TY'S CLIP, which this
   build does not have. There is no <video> in the hero yet, only the poster,
   so the mask stops and the scrim stops are inherited and UNVERIFIED here.
   When Ivey's footage lands, re-measure the hero sub against the brightest
   frame of the loop rather than against a still, and expect to move these
   stops rather than to keep them. */

.hero{
  position:relative;
  /* Copy sits at the BOTTOM of the hero, not centred against the video. The
     two used to sit side by side like columns; now they're layered, with the
     clip forward and full height and the words tucked low and behind it. */
  min-height:min(92vh, 940px);
  display:flex;
  align-items:flex-end;
  /* The bottom padding is set by the IVY, not by taste. The creeper occupies
     a band along the bottom edge and the copy column ends with .hero__meta,
     which is muted gray: over a foreground leaf that measures about 1.9:1.
     This lifts the whole column clear of the band. If .hero__ivy's height
     changes, this changes with it. */
  padding:calc(var(--nav-h) + 24px) 0 clamp(128px,17vh,215px);
  overflow:hidden;
  isolation:isolate;
}

.hero__media{
  position:absolute; inset:0; z-index:0;
  background:
    radial-gradient(110% 88% at 76% 26%, rgba(var(--accent-rgb),.10), transparent 58%),
    linear-gradient(180deg, #141614 0%, #0a0b0a 74%);
}

/* The bled figure, its mask, its top/bottom fade and the three-layer drifting
   smoke that extended it all lived here and are GONE. They existed to hold
   cream type up over a bright photograph running behind the headline, and the
   headline no longer has anything behind it: the clip sits in its own column
   as .reel. Around 200 lines of mask stops and feTurbulence tuned for a photo
   this build never had.

   If a full-bleed background clip is ever wanted here, take it from the
   FushiFit build rather than reconstructing it, and re-measure every stop
   against the new footage. */

/* Pulled right back. The old stops were near-opaque black across the whole
   copy column because a bright bled photo sat under the type. Nothing sits
   under the type any more, and at the old strength this flattened the
   section into a black rectangle. */
.hero__scrim{
  position:absolute; inset:0; z-index:2;
  background:linear-gradient(180deg, rgba(10,11,10,.42) 0%, transparent 26%, rgba(10,11,10,.5) 100%);
  pointer-events:none;
}
.hero .wrap{ position:relative; z-index:3; }

.hero .wrap{ width:100%; }
/* Two columns now: copy left, the intro reel right. The forked hero had the
   footage BLED off the right edge and running behind the headline, which
   suits wallpaper and does not suit a framed 9:16 clip somebody is meant to
   watch. The clip gets its own column and its own edges instead.

   Everything the old treatment needed is gone with it: no .hero__figure, no
   mask, no smoke. .hero__scrim survives but only has to do a gentle overall
   darkening now, not hold cream type up over a bright photo. */
.hero__grid{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,clamp(260px,30vw,376px));
  gap:clamp(28px,5vw,72px);
  align-items:center;
  width:100%;
}
.hero__copy{
  position:relative;
  grid-column:1;
  /* Narrower than it was with the still photo. That photo was a dark
     silhouette and the copy could sit right across it; the subject is lit and moving
     here, and the copy and the subject were fighting for the same horizontal
     space. Pulling the column in is what lets the mask below finish its fade
     before it reaches him. Copy width and mask stops are a pair. */
  max-width:660px;
}
/* Pool of darkness under the copy. The mask and the scrim between them get
   the background most of the way there, but the headline runs furthest right
   and this is what guarantees it lands on near-black. */
.hero__copy::before{
  content:"";
  position:absolute; z-index:-1;
  left:-40px; right:-90px; top:-56px; bottom:-40px;
  background:radial-gradient(74% 78% at 30% 50%,
    rgba(10,11,10,.92) 0%,
    rgba(10,11,10,.74) 48%,
    rgba(10,11,10,.42) 72%,
    transparent 90%);
  pointer-events:none;
}

.hero__title{
  font-size:clamp(34px,4.2vw,54px);
  text-transform:uppercase;
  letter-spacing:-.025em;
  font-variation-settings:"wdth" 108;
}
.hero__title span{ display:block; color:var(--accent); }
/* Without this the accent clause breaks as "BUILT AROUND YOUR / WEEK." and
   drops one word onto its own line. Balance splits it evenly instead. The
   span is display:block, so it needs the property in its own right; setting
   it on the h1 alone does nothing for it. */
.hero__title,
.hero__title span{ text-wrap:balance; }

.hero__sub{
  color:var(--muted);
  font-size:clamp(17px,1.9vw,20px);
  /* Deliberately shorter than .hero__copy. The headline is meant to run into
     the clip's fading edge, but a veil over 17px body text costs more than it
     does over 54px display type, so this stays clear of it. */
  max-width:34ch;
  margin:26px 0 0;
  line-height:1.62;
}
/* Held well inside the copy column. These are flex rows, so without a cap
   they stretch to the full measure and run under the opaque part of the clip:
   measured at a 41% veil on the ghost button's edge and 35% on the meta row.
   The headline is the only element meant to pass behind the subject. Anything
   interactive, or set small, stays in the clear. */
.hero__actions{ display:flex; flex-wrap:wrap; gap:14px; margin-top:36px; width:fit-content; max-width:100%; }

.hero__meta{
  display:flex; flex-wrap:wrap; align-items:center; gap:10px 22px;
  margin-top:40px;
  max-width:400px;
  font-size:12px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--muted-2); font-weight:500;
}
.hero__meta span{ display:flex; align-items:center; gap:9px; }
.hero__meta span::before{
  content:""; width:5px; height:5px; border-radius:50%;
  background:var(--accent); flex:none;
}

/* Narrow: the hero stacks, reel above copy. The rules that used to live here
   were for the bled photo band (a big padding-top reserving room for it, its
   mask, its fade and the smoke tuning) and went with it. The stacking itself
   is declared next to .reel further down, so it sits with the thing it
   describes. */
@media (max-width:900px){
  .hero{ padding-top:calc(var(--nav-h) + 28px); }
  .hero__scrim{
    background:linear-gradient(180deg, rgba(10,11,10,.38) 0%, transparent 24%, rgba(10,11,10,.45) 60%, rgba(10,11,10,.88) 100%);
  }
  .hero__copy{ max-width:none; }
  /* Ramps from nothing rather than starting at full strength, so it never
     draws a hard horizontal line across the section where the pool begins. */
  .hero__copy::before{
    left:-28px; right:-28px; top:-60px; bottom:-24px;
    background:linear-gradient(180deg,
      transparent 0,
      rgba(10,11,10,.42) 60px,
      rgba(10,11,10,.72) 120px,
      rgba(10,11,10,.72) 60%,
      transparent 96%);
  }
}

@media (max-width:720px){
  .rail{ --dot:9px; --rail-bottom:26px; }
  .rail__label{ font-size:9.5px; letter-spacing:.1em; }
  .rail__item.is-on .rail__label::before{ width:11px; margin-right:7px; }
}

@media (prefers-reduced-motion:reduce){
  .rail__dot,
  .rail__label,
  .rail__label::before{ transition:none; }
}

/* =============== FOLLOW =============== */

.follow{
  padding:clamp(30px,4vw,44px) 0;
  background:
    radial-gradient(58% 130% at 50% 50%, rgba(var(--accent-rgb),.09), transparent 70%),
    var(--bg);
}
.follow__inner{
  display:flex;
  align-items:center;
  justify-content:center;
  flex-wrap:wrap;
  gap:16px 32px;
}
.follow__label{
  margin:0;
  font-size:11px; font-weight:600; letter-spacing:.2em; text-transform:uppercase;
  color:var(--muted-2);
}
.follow__links{ display:flex; flex-wrap:wrap; gap:14px; }

.follow__btn{
  display:inline-flex;
  align-items:center;
  gap:12px;
  padding:13px 24px;
  border:1px solid var(--line-2);
  border-radius:999px;
  background:var(--surface);
  color:var(--text);
  font-size:15px;
  font-weight:600;
  letter-spacing:.01em;
  transition:border-color .22s var(--ease), background .22s var(--ease),
             color .22s var(--ease), transform .22s var(--ease);
}
.follow__btn:hover{
  border-color:var(--accent);
  background:linear-gradient(180deg, rgba(var(--accent-rgb),.14), var(--surface));
  transform:translateY(-2px);
}
.follow__btn:focus-visible{ outline:2px solid var(--accent-hi); outline-offset:3px; }
.follow__btn svg{ width:19px; height:19px; flex:none; color:var(--accent); }

.follow__net{
  font-size:11px; font-weight:600; letter-spacing:.16em; text-transform:uppercase;
  color:var(--muted-2);
  padding-left:12px;
  border-left:1px solid var(--line);
}

@media (max-width:560px){
  .follow__links{ width:100%; flex-direction:column; }
  .follow__btn{ justify-content:center; padding:15px 22px; }
}

/* =============== TRUST BAR =============== */

.trust{
  border-top:1px solid var(--line);
  border-bottom:1px solid var(--line);
  background:var(--bg-alt);
  padding:30px 0;
}
.trust__inner{
  display:flex; flex-wrap:wrap; align-items:center; justify-content:center;
  gap:18px 48px;
}
.trust__label{
  font-size:11px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600;
}
.trust__logos{ display:flex; flex-wrap:wrap; align-items:center; gap:16px 40px; }
.trust__logos a{
  font-family:var(--display);
  font-weight:600; font-size:16px; letter-spacing:.12em; text-transform:uppercase;
  color:var(--muted);
  transition:color .2s var(--ease);
  font-variation-settings:"wdth" 100;
}
.trust__logos a:hover{ color:var(--accent-hi); }

.trust__item{ display:inline-flex; align-items:center; gap:12px; }

.code{
  display:inline-flex; align-items:center; gap:8px;
  font-family:var(--display);
  font-size:13px; font-weight:700; letter-spacing:.14em; text-transform:uppercase;
  color:var(--accent-hi);
  background:var(--accent-dim);
  border:1px dashed var(--accent-line);
  border-radius:999px;
  padding:6px 14px;
  cursor:pointer;
  transition:background .2s var(--ease), border-color .2s var(--ease), color .2s var(--ease);
}
.code:hover{ background:rgba(var(--accent-rgb),.18); border-color:var(--accent); }
.code:focus-visible{ outline:2px solid var(--accent-hi); outline-offset:3px; }
.code__icon{ opacity:.75; }
.code.is-copied{
  color:var(--accent-ink);
  background:var(--accent-btn);
  border-style:solid;
  border-color:var(--accent-btn);
}

/* Must live after the base .code rule above, not up in the hero's mobile
   block, or the base padding wins and the chip stays a 28px thumb target. */
@media (max-width:720px){
  .code{ padding:11px 17px; }
}

/* =============== FIT (who this is / isn't for) =============== */

.fit__grid{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
  gap:24px;
}
.fit__card{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:36px 32px;
}
.fit__card--yes{ border-color:var(--accent-line); background:linear-gradient(180deg, rgba(var(--accent-rgb),.06), var(--surface) 60%); }
.fit__card h3{
  font-size:13px; letter-spacing:.16em; text-transform:uppercase;
  margin:0 0 24px; color:var(--muted-2); font-weight:600;
  font-family:var(--sans);
}
.fit__card--yes h3{ color:var(--accent); }
.fit__list{ list-style:none; margin:0; padding:0; display:grid; gap:16px; }
.fit__list li{
  display:flex; gap:14px; align-items:flex-start;
  color:var(--muted); font-size:16px; line-height:1.55;
}
.fit__list li::before{
  content:""; flex:none; width:18px; height:18px; margin-top:4px;
  border-radius:50%;
  border:1.5px solid var(--line-2);
  background:no-repeat center/10px 10px;
}
.fit__card--yes .fit__list li::before{
  border-color:var(--accent);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2 6.4 4.6 9 10 3.2' fill='none' stroke='%233fa252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.fit__card--no .fit__list li::before{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3.2 3.2 8.8 8.8M8.8 3.2 3.2 8.8' fill='none' stroke='%2378726a' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* =============== PILLARS =============== */

/* Explicit 3 columns, not auto-fit — the container's gray shows through the
   1px gaps, so a fourth empty auto-fit cell reads as a broken box. */
.pillars{
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  gap:1px;
  background:var(--line);
  border:1px solid var(--line);
  border-radius:var(--r);
  overflow:hidden;
}
.pillar{
  background:var(--bg);
  padding:44px 34px;
  transition:background .3s var(--ease);
}
.pillar:hover{ background:var(--surface); }
.pillar__num{
  font-family:var(--display);
  font-size:13px; font-weight:700; letter-spacing:.14em;
  color:var(--accent);
  display:block; margin-bottom:22px;
}
.pillar h3{
  font-size:clamp(24px,2.6vw,30px);
  text-transform:uppercase;
  margin-bottom:16px;
}
.pillar p{ color:var(--muted); font-size:16px; margin:0; }

@media (max-width:880px){
  .pillars{ grid-template-columns:1fr; }
}

/* =============== PROOF / TESTIMONIALS =============== */

.proof__grid{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
  gap:24px;
}
.proof__card{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  overflow:hidden;
  display:flex; flex-direction:column;
  transition:border-color .3s var(--ease), transform .3s var(--ease);
}
.proof__card:hover{ border-color:var(--accent-line); transform:translateY(-3px); }

.proof__media{
  aspect-ratio:4/3;
  background:
    repeating-linear-gradient(45deg, rgba(241,243,241,.02) 0 12px, transparent 12px 24px),
    linear-gradient(160deg, #202024, #131315);
  display:flex; align-items:center; justify-content:center;
  border-bottom:1px solid var(--line);
  position:relative;
}
.proof__media img{ width:100%; height:100%; object-fit:cover; }
.proof__ph{
  font-size:11px; letter-spacing:.18em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600; text-align:center; padding:0 20px;
}
.proof__body{ padding:28px 26px 30px; display:flex; flex-direction:column; gap:18px; flex:1; }
.proof__stat{
  display:flex; flex-wrap:wrap; gap:8px 18px;
  font-family:var(--display);
  font-size:13px; font-weight:700; letter-spacing:.1em; text-transform:uppercase;
  color:var(--accent);
}
.proof__quote{ color:var(--muted); font-size:16px; line-height:1.6; margin:0; flex:1; }
.proof__who{
  font-size:12px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600;
  padding-top:16px; border-top:1px solid var(--line);
}

/* =============== TESTIMONIAL CAROUSEL ===============
   A real scroll container that also drifts on its own, NOT a CSS marquee.
   It started as a transform animation, which cannot be scrolled: the user has
   no way into it and pausing on hover was the only interaction available.
   Driving scrollLeft instead means trackpads, touch and a mouse drag all work,
   and the drift is just something added on top every frame.

   It deliberately does NOT pause on hover. Stopping on a card used to halt the
   strip, which reads as broken when you were only passing over it.

   Two identical .tm__set blocks make the wrap seamless: once scrollLeft passes
   (one set + one gap) the view is identical, so main.js subtracts that and
   nobody sees a jump. The second set is aria-hidden with tabindex="-1" cards,
   so assistive tech and tab order only ever see one copy. */

.tm{
  position:relative;
  margin-top:8px;
  overflow-x:auto;
  overflow-y:hidden;
  /* html has scroll-behavior:smooth, which would fight a scrollLeft written
     every frame. */
  scroll-behavior:auto;
  /* Absorbs the card's hover lift, which overflow-y:hidden would otherwise
     clip. */
  padding:5px 0;
  scrollbar-width:none;
  -ms-overflow-style:none;
  /* Cards dissolve at the edges instead of being cut off by the viewport. */
  -webkit-mask-image:linear-gradient(90deg, transparent 0, #000 5%, #000 95%, transparent 100%);
          mask-image:linear-gradient(90deg, transparent 0, #000 5%, #000 95%, transparent 100%);
}
.tm::-webkit-scrollbar{ display:none; }
.tm.is-dragging{ cursor:grabbing; }

.tm__track{
  display:flex;
  gap:20px;
  width:max-content;
}
.tm__set{ display:flex; gap:20px; }

.tm__card{
  flex:none;
  width:clamp(232px, 25vw, 288px);
  display:flex;
  flex-direction:column;
  text-align:left;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  overflow:hidden;
  padding:0;
  cursor:pointer;
  font:inherit;
  color:inherit;
  position:relative;
  transition:border-color .28s var(--ease), transform .28s var(--ease);
}
.tm__card:hover{ border-color:var(--accent-line); transform:translateY(-3px); }
.tm.is-dragging .tm__card{ cursor:grabbing; }
.tm__card:focus-visible{ outline:2px solid var(--accent-hi); outline-offset:3px; }

/* Says out loud that the content is invented. Delete with the sample data. */
.tm__flag{
  position:absolute; top:10px; right:10px; z-index:2;
  font-size:9.5px; font-weight:600; letter-spacing:.14em; text-transform:uppercase;
  color:var(--muted);
  background:rgba(10,11,10,.78);
  border:1px solid var(--line-2);
  border-radius:999px;
  padding:4px 9px;
}

.tm__media{
  display:flex; align-items:center; justify-content:center;
  aspect-ratio:4/3;
  border-bottom:1px solid var(--line);
  background:
    repeating-linear-gradient(45deg, rgba(241,243,241,.02) 0 12px, transparent 12px 24px),
    linear-gradient(160deg, #202024, #131315);
}
.tm__ph{
  font-size:10px; letter-spacing:.18em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600; text-align:center; padding:0 14px;
}

.tm__body{ display:flex; flex-direction:column; gap:12px; padding:20px 20px 22px; flex:1; }
.tm__stat{
  font-family:var(--display);
  font-size:12px; font-weight:700; letter-spacing:.1em; text-transform:uppercase;
  color:var(--accent);
}
.tm__quote{ color:var(--muted); font-size:14.5px; line-height:1.55; flex:1; }
.tm__who{
  font-size:11px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600;
  padding-top:12px; border-top:1px solid var(--line);
}

.tm__hint{
  margin:22px 0 0;
  font-size:11px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600;
}

@media (prefers-reduced-motion:reduce){
  /* The drift is switched off in main.js; the strip stays scrollable, so both
     sets have to stay in place for the wrap to keep working by hand. */
  .tm__card:hover{ transform:none; }
}

/* --- single testimonial ---
   One client, so the strip doesn't scroll: it centres a wider card that shows
   the before and after side by side. The carousel rules above are untouched
   and still work; removing .tm--single and adding cards brings them back. */

.tm--single{
  overflow:visible;
  padding:0;
  -webkit-mask-image:none;
          mask-image:none;
}
.tm--single .tm__track{ width:100%; justify-content:center; }
.tm--single .tm__set{ width:100%; justify-content:center; }
.tm--single .tm__card{
  width:min(100%, 560px);
  cursor:pointer;
}
.tm--single .tm__card:hover{ transform:translateY(-2px); }

.tm__shots{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:1px;
  background:var(--line);
  border-bottom:1px solid var(--line);
}
.tm__shot{ position:relative; display:block; background:var(--bg); }
.tm__shot img{
  width:100%;
  /* Both files are exported at 3:4 with the subject framed to the same
     height, so cover has almost nothing to crop and the pair reads as a
     genuine comparison rather than two unrelated photos. */
  aspect-ratio:3/4;
  object-fit:cover;
  object-position:center;
  display:block;
}
.tm__shot em{
  position:absolute; left:10px; bottom:10px;
  font-style:normal;
  font-size:10px; font-weight:600; letter-spacing:.16em; text-transform:uppercase;
  color:var(--text);
  background:rgba(10,11,10,.72);
  border-radius:999px;
  padding:4px 10px;
}

.tmodal__shots figure img,
.tmodal__pair img{
  width:100%;
  display:block;
  border:1px solid var(--line);
  border-radius:8px;
}
.tmodal__shots figure img{ aspect-ratio:3/4; object-fit:cover; object-position:center; }
.tmodal__pair{ margin:22px 0 0; display:flex; flex-direction:column; gap:8px; }
.tmodal__pair img{ aspect-ratio:4/5; object-fit:cover; object-position:center 30%; }
.tmodal__pair figcaption{
  font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600; text-align:center;
}

@media (max-width:560px){
  .tm--single .tm__card{ width:100%; }
}

/* =============== TESTIMONIAL POPUP =============== */

.tmodal{
  width:min(92vw, 520px);
  max-height:min(80vh, 680px);
  padding:0;
  border:1px solid var(--line-2);
  border-radius:var(--r);
  background:var(--surface);
  color:var(--text);
  overflow:hidden;
  box-shadow:var(--shadow);
}
.tmodal::backdrop{
  background:rgba(6,6,7,.74);
  -webkit-backdrop-filter:blur(4px);
          backdrop-filter:blur(4px);
}
.tmodal__inner{ display:flex; flex-direction:column; max-height:inherit; }

.tmodal__bar{
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  padding:16px 16px 16px 22px;
  border-bottom:1px solid var(--line);
  flex:none;
}
.tmodal__flag{
  margin:0;
  font-size:10px; font-weight:600; letter-spacing:.16em; text-transform:uppercase;
  color:var(--muted-2);
}
.tmodal__x{
  flex:none;
  width:32px; height:32px;
  display:flex; align-items:center; justify-content:center;
  background:none; border:1px solid var(--line-2); border-radius:8px;
  color:var(--muted); cursor:pointer;
  transition:color .2s var(--ease), border-color .2s var(--ease);
}
.tmodal__x:hover{ color:var(--text); border-color:var(--accent); }
.tmodal__x:focus-visible{ outline:2px solid var(--accent-hi); outline-offset:2px; }

/* The scroll lives here, not on the dialog, so the bar stays put. */
.tmodal__body{ padding:24px 22px 28px; overflow-y:auto; }
.tmodal__body p{ color:var(--muted); font-size:15px; line-height:1.65; }
.tmodal__stat{
  margin:0 0 16px;
  font-family:var(--display);
  font-size:13px; font-weight:700; letter-spacing:.1em; text-transform:uppercase;
  color:var(--accent) !important;
}
.tmodal__shots{ display:grid; grid-template-columns:1fr 1fr; gap:10px; margin:0 0 20px; }
.tmodal__shots figure{
  margin:0;
  display:flex; flex-direction:column; gap:8px;
}
/* 4:5 rather than 3:4. Physique shots want portrait, but the taller ratio
   pushed the whole story below the fold of the popup before you'd read a
   word. The figcaption is the only label now; there used to be a second one
   inside the box saying the same thing. */
.tmodal__shots figure > span{
  display:flex; align-items:center; justify-content:center;
  aspect-ratio:4/5;
  border:1px solid var(--line);
  border-radius:8px;
  background:
    repeating-linear-gradient(45deg, rgba(241,243,241,.02) 0 12px, transparent 12px 24px),
    linear-gradient(160deg, #202024, #131315);
}
.tmodal__shots figcaption{
  font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600; text-align:center;
}
.tmodal__quote{
  margin:0 0 18px;
  font-family:var(--display);
  font-size:17px; font-weight:600; line-height:1.4;
  color:var(--text) !important;
  letter-spacing:-.01em;
}
.tmodal__who{
  margin:20px 0 0 !important;
  padding-top:16px;
  border-top:1px solid var(--line);
  font-size:11px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--muted-2) !important; font-weight:600;
}

@media (max-width:480px){
  .tmodal{ width:94vw; }
  .tmodal__body{ padding:20px 18px 24px; }
}

/* =============== STORY ===============
   His account, sat inside the transformation section straight after the
   photographs it explains. This replaced a whole separate About section with
   its own ivy-wrapped portrait; the portrait never got a real headshot and
   the wrap was already doing its job around the hero video.

   The ivy here is BACK LAYER ONLY, and deliberately much lighter than the
   reel's. That one frames a video and can be as dense as it likes. This one
   sits around three paragraphs somebody has to read, so nothing paints over
   the text, ever. */

.story{
  position:relative;
  max-width:760px;
  margin:clamp(52px,7vw,84px) auto 0;
  isolation:isolate;
}
/* Two square pieces at opposite corners rather than one box stretched to the
   block. Square keeps the leaves true whatever the block's aspect does
   between desktop and phone; the second is the same art rotated. */
.story__ivy{
  position:absolute;
  width:clamp(150px,26vw,270px);
  aspect-ratio:1;
  z-index:0;
  opacity:.66;
  overflow:visible;
}
.story__ivy--tl{ top:0;    left:0;  transform:translate(-24%,-34%); }
.story__ivy--br{ bottom:0; right:0; transform:translate(24%,34%) rotate(180deg); }
.story__body{
  position:relative;
  z-index:1;
  background:var(--bg-alt);
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:clamp(28px,4vw,44px);
}
.story__title{
  font-size:clamp(23px,3vw,32px);
  text-transform:uppercase;
  margin-bottom:22px;
}
.story__body p{ color:var(--muted); font-size:17px; }
.story__pull{
  margin:28px 0 0;
  padding:22px 0 0;
  border-top:1px solid var(--line);
  font-family:var(--display);
  font-size:clamp(19px,2.2vw,24px);
  font-weight:600;
  line-height:1.3;
  color:var(--text);
  letter-spacing:-.01em;
}

/* Footer credit. Its own line under the copyright rather than trailing it, so
   it does not run off the edge on a phone. */
.footer__by{ display:block; margin-top:7px; }
.footer__by a{ color:var(--muted); transition:color .2s var(--ease); }
.footer__by a:hover{ color:var(--accent-hi); }

/* =============== PROCESS ===============
   A rail with small numbered nodes rather than the big stacked numerals this
   started with. Those were fine on a wide screen and fell apart on a phone:
   the single-column fallback put a 34px numeral on its own line above every
   heading, so each step became three loose blocks and five of them read as a
   staggered list rather than a sequence. The rail keeps the same two-column
   shape at every width, which is what makes it hold together on mobile. */

.steps{ display:grid; gap:0; }

.step{
  position:relative;
  display:grid;
  grid-template-columns:34px 1fr;
  column-gap:clamp(16px,2.2vw,26px);
  padding-bottom:clamp(30px,3.6vw,42px);
  align-items:start;
}
.step:last-child{ padding-bottom:0; }

/* The connector, from the bottom of this node to the top of the next one.
   `bottom:0` lands on the padding edge, which is exactly where the next
   node starts, so the run stays flush without hard-coding a height. */
.step::before{
  content:"";
  position:absolute;
  left:16px;          /* (34px node / 2) minus half the 1px line */
  top:38px;
  bottom:0;
  width:1px;
  background:var(--line-2);
}
.step:last-child::before{ display:none; }

.step__num{
  width:34px; height:34px;
  border-radius:50%;
  border:1px solid var(--accent-line);
  background:var(--accent-dim);
  color:var(--accent);
  font-family:var(--display);
  font-size:12.5px; font-weight:700; letter-spacing:.04em;
  display:flex; align-items:center; justify-content:center;
  font-variation-settings:"wdth" 100;
  /* Above the connector, so the line never shows through the node. */
  position:relative; z-index:1;
}

/* Nudged down so the heading's cap height sits level with the node rather
   than its ascender line. */
.step h3{
  font-size:clamp(19px,2.1vw,23px);
  text-transform:uppercase;
  margin:5px 0 10px;
}
.step p{ color:var(--muted); font-size:16px; margin:0; max-width:62ch; }
.step__tag{
  display:inline-block; margin-top:14px;
  font-size:11px; letter-spacing:.16em; text-transform:uppercase; font-weight:600;
  color:var(--accent);
  background:var(--accent-dim);
  border:1px solid var(--accent-line);
  border-radius:999px;
  padding:5px 13px;
}

@media (max-width:560px){
  .step{ grid-template-columns:28px 1fr; column-gap:14px; }
  .step::before{ left:13px; top:32px; }
  .step__num{ width:28px; height:28px; font-size:11.5px; }
  .step h3{ margin-top:2px; }
}

/* =============== FINAL CTA =============== */

.cta{
  position:relative;
  padding:clamp(96px,13vw,168px) 0;
  background:
    radial-gradient(90% 120% at 50% 0%, rgba(var(--accent-rgb),.14), transparent 62%),
    var(--bg-alt);
  border-top:1px solid var(--line);
  text-align:center;
  overflow:hidden;
  isolation:isolate;
}

.cta .wrap{ position:relative; z-index:2; }

/* The Perez build runs a looping gym clip behind this block. There's no
   footage for Ivey yet, so it's a flat gradient. If a clip does land, port
   .cta__video / .cta__scrim back over with it and re-measure the headline
   contrast against the brightest frame. */
.cta__title{
  font-size:clamp(34px,5.6vw,64px);
  text-transform:uppercase;
  max-width:16ch; margin:0 auto;
}
.cta__sub{ color:var(--muted); font-size:clamp(17px,1.9vw,19px); max-width:54ch; margin:24px auto 0; }
.cta__actions{ margin-top:38px; display:flex; justify-content:center; flex-wrap:wrap; gap:14px; }
.cta__note{ margin-top:22px; font-size:13px; color:var(--muted-2); }

/* =============== FOOTER =============== */

.footer{
  border-top:1px solid var(--line);
  padding:52px 0 44px;
  background:var(--bg);
}
.footer__inner{
  display:flex; flex-wrap:wrap; gap:24px 40px;
  align-items:center; justify-content:space-between;
}
.footer__links{ display:flex; flex-wrap:wrap; gap:14px 28px; }
.footer__links a{
  font-size:13px; letter-spacing:.1em; text-transform:uppercase;
  color:var(--muted-2); transition:color .2s var(--ease);
}
.footer__links a:hover{ color:var(--accent-hi); }
.footer__fine{ font-size:12px; color:var(--muted-2); letter-spacing:.04em; }

/* =========================================================
   APPLICATION WIZARD  (apply.html)
   ========================================================= */

.apply-page{
  min-height:100vh;
  display:flex; flex-direction:column;
  background:
    radial-gradient(80% 60% at 50% -10%, rgba(var(--accent-rgb),.10), transparent 60%),
    var(--bg);
}

.apply__bar{
  position:sticky; top:0; z-index:20;
  background:rgba(10,11,10,.92);
  backdrop-filter:blur(14px);
  -webkit-backdrop-filter:blur(14px);
  border-bottom:1px solid var(--line);
}
.apply__bar-inner{
  display:flex; align-items:center; justify-content:space-between;
  gap:20px; height:68px;
}
.apply__count{
  font-size:12px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--muted-2); font-weight:600;
}
.apply__progress{
  height:2px; background:var(--line); position:relative; overflow:hidden;
}
.apply__progress i{
  position:absolute; inset:0 auto 0 0;
  background:var(--accent);
  width:0%;
  transition:width .45s var(--ease);
}

.apply__main{
  flex:1;
  display:flex; align-items:center;
  padding:clamp(40px,8vh,88px) 0;
}
.apply__shell{ width:100%; max-width:720px; margin:0 auto; padding:0 28px; }

.q{ display:none; }
.q.is-active{ display:block; animation:qin .45s var(--ease); }
@keyframes qin{ from{ opacity:0; transform:translateY(14px); } to{ opacity:1; transform:none; } }
@media (prefers-reduced-motion:reduce){ .q.is-active{ animation:none; } }

.q__index{
  font-family:var(--display);
  font-size:12px; font-weight:700; letter-spacing:.18em; text-transform:uppercase;
  color:var(--accent); margin-bottom:18px; display:block;
}
.q__title{
  font-size:clamp(26px,3.8vw,40px);
  line-height:1.15;
  margin-bottom:14px;
}
.q__help{ color:var(--muted); font-size:16px; margin:0 0 30px; max-width:56ch; }

.field{
  width:100%;
  background:var(--surface-2);
  border:1px solid var(--line-2);
  border-radius:10px;
  color:var(--text);
  font-family:var(--sans);
  font-size:17px;
  line-height:1.6;
  padding:16px 18px;
  transition:border-color .2s var(--ease), background .2s var(--ease);
  -webkit-appearance:none;
  appearance:none;
}
.field::placeholder{ color:var(--muted-2); }
.field:focus{
  outline:none;
  border-color:var(--accent);
  background:var(--surface);
}
textarea.field{ min-height:150px; resize:vertical; }

/* Contact screen — several labelled fields on one step. */
.group{ display:grid; gap:18px; }
.group__row{ display:block; }
.group__label{
  display:block;
  font-size:11px; font-weight:600; letter-spacing:.14em; text-transform:uppercase;
  color:var(--muted-2);
  margin-bottom:9px;
}
.group__row:focus-within .group__label{ color:var(--accent); }

.choices{ display:grid; gap:12px; }
.choice{
  display:flex; align-items:flex-start; gap:16px;
  background:var(--surface-2);
  border:1px solid var(--line-2);
  border-radius:10px;
  padding:18px 20px;
  cursor:pointer;
  transition:border-color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
}
.choice:hover{ border-color:var(--line-2); background:var(--surface); transform:translateY(-1px); }
.choice input{ position:absolute; opacity:0; pointer-events:none; }
.choice__dot{
  flex:none; width:20px; height:20px; margin-top:2px;
  border-radius:50%;
  border:1.5px solid var(--line-2);
  position:relative;
  transition:border-color .2s var(--ease);
}
.choice__dot::after{
  content:""; position:absolute; inset:4px;
  border-radius:50%; background:var(--accent);
  transform:scale(0); transition:transform .2s var(--ease);
}
.choice__text b{
  display:block;
  font-family:var(--display);
  font-size:17px; font-weight:600; letter-spacing:-.01em;
  margin-bottom:4px;
}
.choice__text small{ color:var(--muted); font-size:14px; line-height:1.5; display:block; }

/* Multi-select reads as a square with a tick, so nobody mistakes it for a
   pick-one and stops at the first option. */
.choice--check .choice__dot{ border-radius:6px; }
.choice--check .choice__dot::after{
  inset:0;
  border-radius:0;
  background:no-repeat center/11px 11px
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2 6.3 4.6 8.9 10 3.1' fill='none' stroke='%233fa252' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

.choice:has(input:checked){
  border-color:var(--accent);
  background:linear-gradient(180deg, rgba(var(--accent-rgb),.09), var(--surface));
}
.choice:has(input:checked) .choice__dot{ border-color:var(--accent); }
.choice:has(input:checked) .choice__dot::after{ transform:scale(1); }
.choice:focus-within{ outline:2px solid var(--accent-hi); outline-offset:3px; }

.q__error{
  margin-top:14px;
  font-size:14px; color:var(--warn);
  display:none;
}
.q__error.is-on{ display:block; }

.q__nav{
  display:flex; align-items:center; gap:14px;
  margin-top:34px;
  padding-top:26px;
  border-top:1px solid var(--line);
}
.q__nav .spacer{ flex:1; }
.q__hint{ font-size:12px; color:var(--muted-2); letter-spacing:.04em; }
/* Phones: the primary button goes full width on its own row rather than
   fighting the back link for space and wrapping its own label. */
@media (max-width:560px){
  .q__hint{ display:none; }
  .q__nav{ flex-wrap:wrap; gap:12px; }
  .q__nav .spacer{ display:none; }
  .q__nav .btn{
    flex:1 1 100%;
    order:-1;
    justify-content:center;
  }
  .q__nav .back-link{ flex:0 0 auto; }
}

.back-link{
  background:none; border:0; cursor:pointer;
  color:var(--muted-2); font-family:var(--sans); font-size:14px;
  padding:8px 4px;
  transition:color .2s var(--ease);
}
.back-link:hover{ color:var(--text); }

/* --- confirmation --- */
.done{ display:none; text-align:center; }
.done.is-active{ display:block; animation:qin .5s var(--ease); }
.done__mark{
  width:64px; height:64px; margin:0 auto 28px;
  border-radius:50%;
  border:1.5px solid var(--accent);
  background:var(--accent-dim);
  display:flex; align-items:center; justify-content:center;
}
.done__title{ font-size:clamp(30px,4.4vw,46px); text-transform:uppercase; margin-bottom:18px; }
.done__body{ color:var(--muted); font-size:17px; max-width:52ch; margin:0 auto; }
.done__actions{ margin-top:34px; display:flex; justify-content:center; flex-wrap:wrap; gap:14px; }

/* Shown only when delivery failed. Not an error the applicant caused, so it
   reads as a handover, not a rejection. */
.done__fallback{
  margin:22px auto 0;
  max-width:52ch;
  padding:14px 18px;
  border:1px solid var(--line-2);
  border-radius:10px;
  background:var(--surface);
  color:var(--muted);
  font-size:14.5px;
  line-height:1.6;
}
.done__fallback a{ color:var(--accent); text-decoration:underline; }

.done__panel{
  margin-top:44px;
  text-align:left;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--r);
  padding:28px 26px;
}
.done__panel h3{
  font-size:13px; letter-spacing:.16em; text-transform:uppercase;
  font-family:var(--sans); color:var(--muted-2); font-weight:600;
  margin-bottom:18px;
}
.done__panel ol{ margin:0; padding-left:20px; color:var(--muted); font-size:15px; display:grid; gap:10px; }

/* --- setter handoff preview (demo only, ?demo=1) --- */
.handoff{
  display:none;
  margin-top:44px; text-align:left;
  background:var(--surface);
  border:1px dashed var(--accent-line);
  border-radius:var(--r);
  padding:26px;
}
.handoff.is-on{ display:block; }
.handoff__tag{
  font-size:11px; letter-spacing:.18em; text-transform:uppercase;
  color:var(--accent); font-weight:700; margin-bottom:16px;
}
.handoff__score{
  display:inline-flex; align-items:center; gap:10px;
  font-family:var(--display); font-weight:700;
  font-size:13px; letter-spacing:.12em; text-transform:uppercase;
  border-radius:999px; padding:7px 16px; margin-bottom:20px;
}
.handoff__score[data-tier="hot"]{ background:var(--accent-btn); color:var(--accent-ink); }
.handoff__score[data-tier="qualified"]{ background:var(--accent-dim); color:var(--accent-hi); border:1px solid var(--accent-line); }
.handoff__score[data-tier="nurture"]{ background:var(--surface-2); color:var(--muted); border:1px solid var(--line-2); }
.handoff__score[data-tier="declined"]{ background:var(--surface-2); color:var(--muted-2); border:1px solid var(--line); }
.handoff pre{
  margin:0; white-space:pre-wrap; word-break:break-word;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:13px; line-height:1.65; color:var(--muted);
  background:var(--bg); border:1px solid var(--line); border-radius:8px;
  padding:18px; max-height:340px; overflow:auto;
}
/* =========================================================
   SHINE
   Carried over from the Perez build and pulled back hard for
   neon green. Green reads far brighter than the maroon these
   effects were tuned against, so every alpha here is roughly
   half its original value, and the rim pulse is off the follow
   buttons entirely — four of them breathing in a row turned
   that strip into a light show.

   Four effects: a roaming metallic gradient on accent text, a
   sweeping highlight across pills, a breathing rim glow, and a
   bloom under the primary buttons.

   The sweeps animate transform only, so they stay on the
   compositor. The roam animates background-position, which does
   repaint, so it's kept to accent text rather than large areas.
   Everything stops for reduced-motion visitors at the bottom.
   ========================================================= */

@keyframes accentRoam{
  0%   { background-position:   0% 50%; }
  25%  { background-position:  65%  8%; }
  50%  { background-position: 100% 55%; }
  75%  { background-position:  35% 95%; }
  100% { background-position:   0% 50%; }
}
@keyframes pillSweep{
  0%   { transform:translateX(-170%); }
  100% { transform:translateX(330%); }
}
@keyframes accentPulse{
  0%,100% { box-shadow:0 0 12px rgba(var(--accent-rgb),.07), inset 0 0 10px rgba(var(--accent-rgb),.03); }
  50%     { box-shadow:0 0 22px rgba(var(--accent-hi-rgb),.17), inset 0 0 12px rgba(var(--accent-rgb),.07); }
}
@keyframes ctaGlow{
  0%,100% { box-shadow:0 0 0 0 rgba(var(--accent-hi-rgb),0); }
  50%     { box-shadow:0 0 28px 1px rgba(var(--accent-hi-rgb),.22); }
}

/* NO `filter` ON ANY OF THESE.
   WebKit drops the background-clip:text clip when the element also carries a
   filter, and paints the whole gradient as a solid rectangle behind the type.
   Reproduced in Safari at every eyebrow on the page. The Perez and Thar builds
   still carry the drop-shadow this was ported from, so they have the same box.
   The glow is not worth it; the intro keeps its halo on .intro__rule instead. */
@supports (-webkit-background-clip:text) or (background-clip:text){
  .eyebrow,
  .follow__label,
  .trust__label{
    background-image:linear-gradient(115deg,
      var(--accent) 0%, var(--accent) 34%,
      var(--accent-mid) 44%, var(--accent-peak) 50%, var(--accent-mid) 56%,
      var(--accent) 66%, var(--accent) 100%);
    background-size:300% 300%;
    -webkit-background-clip:text; background-clip:text;
    color:transparent;
    animation:accentRoam 4.6s ease-in-out infinite;
  }
  /* FIT in the intro gets a slower, heavier version. */
  .intro__l--b{
    background-image:linear-gradient(115deg,
      var(--accent) 0%, var(--accent) 32%,
      var(--accent-mid) 43%, var(--accent-peak) 50%, var(--accent-mid) 57%,
      var(--accent) 68%, var(--accent) 100%);
    background-size:300% 300%;
    -webkit-background-clip:text; background-clip:text;
    color:transparent;
    /* Both animations, not just the roam. These letters already carry the
       slide-in from .intro__l, and a bare animation here would replace it,
       so FIT would never travel in. */
    animation:
      slideAcross .85s cubic-bezier(.16,.84,.34,1) var(--d) forwards,
      accentRoam 3.4s ease-in-out infinite;
  }
  .brand em{
    background-image:linear-gradient(115deg,
      var(--accent) 0%, var(--accent) 36%,
      var(--accent-mid) 46%, var(--accent-peak) 50%, var(--accent-mid) 54%,
      var(--accent) 64%, var(--accent) 100%);
    background-size:300% 300%;
    -webkit-background-clip:text; background-clip:text;
    color:transparent;
    animation:accentRoam 5.2s ease-in-out infinite;
  }
}

/* Pills: a highlight sweeping across, staggered so they don't move in
   lockstep. The rim pulse is only on the two that appear once or twice
   per page. */
.code,
.follow__btn,
.step__tag{
  position:relative;
  overflow:hidden;
}
.code,
.step__tag{ animation:accentPulse 3.6s ease-in-out infinite; }

.code::after,
.follow__btn::after,
.step__tag::after{
  content:"";
  position:absolute; top:0; bottom:0; left:0;
  width:44%;
  background:linear-gradient(115deg, transparent, rgba(241,243,241,.13), transparent);
  transform:translateX(-170%);
  pointer-events:none;
  animation:pillSweep 4.6s linear infinite;
}
.follow__btn:nth-child(2)::after{ animation-delay:.7s; }
.follow__btn:nth-child(3)::after{ animation-delay:1.4s; }
.follow__btn:nth-child(4)::after{ animation-delay:2.1s; }
.trust__item:nth-child(2) .code,
.trust__item:nth-child(2) .code::after{ animation-delay:1.3s; }
.step__tag{ animation-delay:.5s; }

.code:hover,
.follow__btn:hover{
  animation-play-state:paused;
  box-shadow:0 0 22px rgba(var(--accent-hi-rgb),.2), inset 0 0 12px rgba(var(--accent-rgb),.08);
}

/* The money buttons. A bloom underneath pulls the eye without touching
   the fill, so the ink-on-green contrast is untouched. */
.btn--primary{ animation:ctaGlow 3.2s ease-in-out infinite; }
.btn--primary:hover{
  animation-play-state:paused;
  box-shadow:0 0 26px 1px rgba(var(--accent-hi-rgb),.28);
}

.intro__rule{ box-shadow:0 0 16px rgba(var(--accent-rgb),.3); }
.follow__btn{ box-shadow:0 10px 24px -18px rgba(var(--accent-rgb),.55); }

@media (prefers-reduced-motion:reduce){
  .eyebrow,
  .follow__label,
  .trust__label,
  .intro__l--b,
  .brand em,
  .code,
  .follow__btn,
  .step__tag,
  .btn--primary{
    animation:none;
    background-position:0 0;
  }
  .code::after,
  .follow__btn::after,
  .step__tag::after{ display:none; }
}

/* =============== IVY ===============
   The signature of this build. Filled silhouettes on three depth planes,
   never outlines: monoline foliage at this scale reads as clip art.

   Structure is always the same. A positioned .ivy box holds one .ivy__art
   SVG generated by tools/ivy/build.js, and inside that each vine is a
   stroked stem plus a run of <g class="ivy__leaf"> placed along it.

   Two rules keep this from wrecking the page it decorates:

     1. It never carries text and never sits above text. Every .ivy box is
        pointer-events:none and aria-hidden, and lives BELOW the copy in
        stacking order.
     2. It is deliberately brighter than a dark-on-dark treatment would be.
        See the foliage tokens at the top of this file. */

.ivy{
  position:absolute;
  pointer-events:none;
  z-index:0;
  overflow:hidden;
}
.ivy__art{ width:100%; height:100%; display:block; overflow:visible; }

.ivy__stem{
  fill:none;
  stroke:var(--ivy-stem);
  stroke-linecap:round;
  /* pathLength="1" is set on every stem in the markup, which is what makes
     a dasharray of 1 mean "the whole path". Without it the 1 is one user
     unit, the stem renders as a dotted line that reads as solid, and the
     draw-on below animates nothing anybody can see. */
  stroke-dasharray:1;
  stroke-dashoffset:1;
}
.ivy__pet{
  fill:none;
  stroke:var(--ivy-stem);
  stroke-width:1.7;
  vector-effect:non-scaling-stroke;
}

/* Tone, not plane, decides the fill. The plane classes are still on the
   markup and still set the paint ORDER, they just no longer set colour. */
.ivy__leaf--t0 .ivy__blade{ fill:var(--ivy-t0); }
.ivy__leaf--t1 .ivy__blade{ fill:var(--ivy-t1); }
.ivy__leaf--t2 .ivy__blade{ fill:var(--ivy-t2); }
.ivy__leaf--t3 .ivy__blade{ fill:var(--ivy-t3); }
.ivy__leaf--t4 .ivy__blade{ fill:var(--ivy-t4); }
.ivy__leaf--t5 .ivy__blade{ fill:var(--ivy-t5); }

/* Veins are drawn inside the blade's own transform, so stroke-width is in
   LEAF space and scales with the leaf. That is deliberate: a big leaf has
   thicker veins than a small one, and a non-scaling hairline across a 120px
   blade looks etched rather than grown. */
.ivy__veins{
  fill:none;
  stroke:var(--ivy-vein);
  stroke-width:2;
  stroke-linecap:round;
  opacity:.34;
}
/* Darker leaves take more vein, the way they do in the photograph. */
.ivy__leaf--t0 .ivy__veins{ opacity:.30; }
.ivy__leaf--t1 .ivy__veins{ opacity:.32; }
.ivy__leaf--t4 .ivy__veins{ opacity:.38; }
.ivy__leaf--t5 .ivy__veins{ opacity:.42; }

/* --- growth ---
   Held back until .is-in, which main.js sets when the block scrolls into
   view. The leaf transform is rebuilt from the per-leaf custom properties
   rather than written as a bare scale(), because a CSS transform REPLACES
   the presentation attribute instead of composing with it: a keyframe of
   `scale(.2)` alone would drop every translate and rotate and stack the
   whole cluster on the SVG origin. */
.ivy .ivy__leaf{
  opacity:0;
  transform:translate(var(--x),var(--y)) rotate(var(--r)) scale(.16);
}
.ivy.is-in .ivy__stem{
  animation:ivyGrow 1.25s cubic-bezier(.33,.72,.4,1) forwards;
}
/* Tendrils uncurl a beat after the runner that carries them. */
.ivy.is-in .ivy__tendril{
  animation:ivyGrow 1.1s cubic-bezier(.33,.72,.4,1) .5s forwards;
}
.ivy.is-in .ivy__leaf{
  animation:ivyUnfurl .72s cubic-bezier(.2,.9,.3,1) forwards;
  /* Leaves trail the stem that carries them, and each one waits its turn, so
     the cluster reads as growing along the vine rather than fading in. */
  animation-delay:calc(220ms + var(--i) * 46ms);
}
@keyframes ivyGrow{ to{ stroke-dashoffset:0; } }
@keyframes ivyUnfurl{
  from{ opacity:0; transform:translate(var(--x),var(--y)) rotate(calc(var(--r) - 30deg)) scale(.16); }
  to  { opacity:1; transform:translate(var(--x),var(--y)) rotate(var(--r)) scale(1); }
}

/* No JS, no observer, no growth. The foliage still has to be there, so the
   finished state is the fallback rather than the blank one. */
.no-js .ivy .ivy__leaf,
.ivy.is-static .ivy__leaf{
  opacity:1;
  transform:translate(var(--x),var(--y)) rotate(var(--r)) scale(1);
  animation:none;
}
.no-js .ivy .ivy__stem, .no-js .ivy .ivy__tendril,
.ivy.is-static .ivy__stem, .ivy.is-static .ivy__tendril{ stroke-dashoffset:0; animation:none; }

@media (prefers-reduced-motion:reduce){
  .ivy .ivy__leaf{
    opacity:1;
    transform:translate(var(--x),var(--y)) rotate(var(--r)) scale(1);
    animation:none;
  }
  .ivy .ivy__stem,
  .ivy .ivy__tendril{ stroke-dashoffset:0; animation:none; }
}

/* --- placements --- */

/* Hero: a creeper along the bottom edge, so the section grows out of
   foliage instead of ending on a rule.

   Sits at z-index 2 and comes AFTER .hero__scrim in the markup, so it paints
   over the scrim rather than under it. Under it the scrim's bottom stop is
   60% black and the whole creeper went to almost nothing. The copy is at
   z-index 3 and .hero__copy::before still lays its own pool of darkness, so
   nothing here reaches the type. The clip stays above everything at 4. */
.hero__ivy{
  left:0; right:0; bottom:0;
  height:clamp(150px, 22vw, 260px);
  z-index:2;
  opacity:1;
  /* Feathered at the top so the vine emerges from the dark rather than
     starting on a visible edge. */
  -webkit-mask-image:linear-gradient(180deg, transparent 0%, rgba(0,0,0,.5) 34%, #000 68%);
          mask-image:linear-gradient(180deg, transparent 0%, rgba(0,0,0,.5) 34%, #000 68%);
}

/* Section corners. One square spec covers all four corners; the corner is
   chosen by CSS, which flips the art rather than generating it again. */
.ivy--corner{
  width:clamp(190px, 26vw, 360px);
  aspect-ratio:1;
  opacity:.62;
}
.ivy--tl{ top:0; left:0; }
.ivy--tr{ top:0; right:0; transform:scaleX(-1); }
.ivy--bl{ bottom:0; left:0; transform:scaleY(-1); }
.ivy--br{ bottom:0; right:0; transform:scale(-1); }

/* The sections themselves need a stacking context and a floor for the
   absolute boxes above, and their copy needs to sit over the foliage. */
.section{ position:relative; overflow:hidden; }
.section > .wrap{ position:relative; z-index:1; }
.cta{ position:relative; overflow:hidden; }
.cta > .wrap{ position:relative; z-index:1; }

@media (max-width:720px){
  /* Corner clusters eat the copy on a phone. The hero creeper stays,
     because it runs along an edge nothing is written on. */
  .ivy--corner{ display:none; }
}

/* =============== INTRO IVY ===============
   The set piece. Vines climb both sides of the screen and the leaves unfurl
   along them, then the name lands through the foliage.

   Timed against the 3.9s introAuto below, not against itself. If either
   duration moves the other has to move with it or the overlay clears while
   the ivy is still growing. */

.intro__ivy{
  position:absolute;
  top:0; bottom:0;
  width:clamp(170px, 32vw, 420px);
  z-index:0;
  pointer-events:none;
  opacity:.95;
}
.intro__ivy--l{ left:0; }
/* Mirrored rather than generated a second time. Foliage is symmetric enough
   that nobody reads the two columns as the same vine twice. */
.intro__ivy--r{ right:0; transform:scaleX(-1); }
/* Plays on load rather than on scroll, so it never waits for the observer. */
.intro__ivy .ivy__stem{
  animation:ivyGrow 1.5s cubic-bezier(.3,.7,.35,1) .1s forwards;
}
.intro__ivy .ivy__tendril{
  animation:ivyGrow 1.2s cubic-bezier(.3,.7,.35,1) .7s forwards;
}
.intro__ivy .ivy__leaf{
  animation:ivyUnfurl .78s cubic-bezier(.2,.9,.3,1) forwards;
  animation-delay:calc(340ms + var(--i) * 42ms);
}
/* The name rises out of the foliage, so it has to be over it. */
.intro__inner{ position:relative; z-index:1; }

@media (prefers-reduced-motion:reduce){
  .intro__ivy .ivy__stem,
  .intro__ivy .ivy__tendril,
  .intro__ivy .ivy__leaf{ animation:none; }
}

/* =============== PLACEHOLDER FLAG ===============
   Anything still carrying stand-in content wears this. It is loud by
   design: the point is that none of it can reach a real visitor unnoticed.
   Delete the chip and the class in the same commit as the real asset. */
.eg{
  display:inline-flex; align-items:center; gap:6px;
  font-family:var(--sans);
  font-size:10.5px; font-weight:700;
  letter-spacing:.16em; text-transform:uppercase;
  color:var(--accent-ink);
  background:var(--accent-btn);
  padding:4px 9px; border-radius:999px;
  white-space:nowrap;
}
.eg--float{
  position:absolute; top:12px; left:12px; z-index:5;
}

/* --- intro name: rise, not slide ---
   The forked build slid the two halves of the name in from opposite edges.
   Here the vines are climbing those same edges, so horizontal entry has the
   letters crossing the foliage. Rising suits growth anyway.

   Overridden at (0,2,0) so it beats the base .intro__l rule, and repeated
   inside the shine block below at matching specificity: the gradient rule
   for the accent half re-declares `animation`, and a shorthand there would
   otherwise replace this and leave those letters sitting still. */
.intro__name--rise .intro__l{
  transform:translateY(.46em);
  animation:introRise .82s cubic-bezier(.16,.84,.34,1) var(--d) forwards;
}
@keyframes introRise{ to{ opacity:1; transform:none; } }

@supports (-webkit-background-clip:text) or (background-clip:text){
  .intro__name--rise .intro__l--b{
    animation:
      introRise .82s cubic-bezier(.16,.84,.34,1) var(--d) forwards,
      accentRoam 3.4s ease-in-out infinite;
  }
}

/* The overlay has to outlast the ivy. The vines finish growing at roughly
   .1s + 1.5s and the last leaf lands at 340ms + 24 leaves * 42ms + 780ms,
   so the name needs to be readable past ~2.2s and the whole thing clears at
   3.9s. Move either and the ivy gets cut off mid-growth. */
.intro{ animation-duration:3.9s; }

/* =============== HERO REEL ===============
   Ivey's intro clip, framed and wrapped in ivy, sitting beside the headline.

   Three layers, back and front on either side of the frame, because ivy drawn
   entirely over the video, or entirely under it, reads as a border. Runners
   that pass behind the frame on one edge and return over it on another read
   as growing around it.

     0  .reel__ivy--back    runners BEHIND the video
     1  .reel__frame        the video
     2  .reel__ivy--front   runners OVER it

   There was a fourth layer, a sound control, until the audio track was
   stripped out of the file. Nothing here touches volume now.

   The ivy boxes are inset -11% on all sides, which keeps them at the frame's
   own 9:16 ratio. tools/ivy/build.js relies on that: its viewBox is 9:16 and
   the runners are positioned against the frame's edges within it. Change this
   inset and the wrap slides off the frame. */

.reel{
  position:relative;
  grid-column:2;
  width:100%;
  aspect-ratio:9/16;
  isolation:isolate;
  justify-self:end;
}

.reel__ivy{
  position:absolute;
  inset:-11%;               /* paired with the viewBox in tools/ivy/build.js */
  width:auto; height:auto;
  overflow:visible;
}
.reel__ivy--back { z-index:0; }
.reel__ivy--front{ z-index:2; }

.reel__frame{
  position:absolute;
  inset:0;
  z-index:1;
  border-radius:14px;
  overflow:hidden;
  background:var(--surface);
  box-shadow:
    0 0 0 1px rgba(var(--accent-rgb),.26),
    0 26px 60px rgba(0,0,0,.6);
}
.reel__video{
  width:100%; height:100%;
  object-fit:cover;
  display:block;
}
/* Feathers the clip into the page rather than letting it end on a hard
   rectangle. The gym footage is already near-black at its edges, so this is
   a short ramp: any longer and it eats into him. */
.reel__frame::after{
  content:"";
  position:absolute; inset:0;
  pointer-events:none;
  background:
    linear-gradient(180deg, rgba(10,11,10,.34) 0%, transparent 16%, transparent 76%, rgba(10,11,10,.55) 100%),
    radial-gradient(120% 78% at 50% 42%, transparent 52%, rgba(10,11,10,.42) 100%);
}

@media (max-width:900px){
  /* Stacked, with the HEADLINE FIRST and the clip under it. A phone shows one
     thing at a time, and what the page is selling has to be the thing it
     shows first: the video led here originally and pushed the offer below the
     fold on a small screen.

     The gap is set by the IVY, not by taste. .reel__ivy hangs 11% of the
     frame's height ABOVE it as well as below, so a gap smaller than that puts
     leaves on the copy. The z-index is the second half of that: the reel now
     comes after the copy in both markup and layout, so without it the foliage
     paints over the text instead of passing behind it. */
  .hero__grid{ grid-template-columns:1fr; gap:clamp(52px,11vw,78px); }
  .hero__copy{ grid-column:1; grid-row:1; position:relative; z-index:1; }
  .reel{ grid-column:1; grid-row:2; justify-self:center; width:min(300px,74vw); }
}

@media (prefers-reduced-motion:reduce){
  /* An autoplaying loop is exactly what this preference is about. The poster
     still shows, and the control still starts it on purpose. */
  .reel__video{ }
}
