/* ============================================================================
   THE BRAND OPERATOR — WEBINAR FUNNEL STYLES
   Plain CSS, mobile-first. No frameworks.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   THEME TOKENS
   >>> TO RESKIN THE WHOLE SITE: change --accent (bright fills/buttons),
       --accent-strong (blue text on light backgrounds), and --accent-wash below.
       Everything else (buttons, highlights, glow, checks) derives from them.
       NOTE: --accent is bright, so text ON it uses the dark --accent-ink (for AA),
       and blue TEXT on light surfaces uses the deeper --accent-strong (for AA).
       Do not hardcode colours anywhere else in this file.
   ---------------------------------------------------------------------------- */
:root {
  --canvas:        #F7F6F3;               /* warm off-white base */
  --grid-line:     rgba(20,20,15,0.05);   /* faint graph-paper lines */
  --ink:           #16150F;               /* near-black headline text */
  --ink-soft:      #56544C;               /* muted body / subhead text */
  --accent:        #3B82F6;               /* bright blue — primary brand color (fills, buttons, blocks) */
  --accent-strong: #1D4ED8;               /* deeper blue for blue TEXT on light backgrounds (keeps AA) */
  --accent-ink:    #0A1226;               /* near-black text on bright-blue fills (AA ~5:1) */
  --accent-wash:   rgba(59,130,246,0.12); /* corner glow + highlight backgrounds */
  --accent-wash-soft: rgba(59,130,246,0.06); /* even fainter secondary wash */
  --highlight:     #B8892B;               /* warm gold — used VERY sparingly */
  --card:          #FFFFFF;               /* card surfaces float on the canvas */
  --card-border:   rgba(20,20,15,0.08);
  --danger:        #B23A2E;               /* red X's in the consequence split only */
  --danger-wash:   rgba(178,58,46,0.08);
  /* seat meter heatmap: earliest seats green -> filling up orange -> nearly full red */
  --seat-green:    #1FA25A;
  --seat-orange:   var(--accent);
  --seat-red:      #E0402C;
  --ok:            var(--accent);
  --shadow:        0 10px 30px rgba(20,20,15,0.08);
  --shadow-lg:     0 20px 50px rgba(20,20,15,0.12);

  /* spacing rhythm (8px base) */
  --sp-1: 8px;  --sp-2: 16px; --sp-3: 24px; --sp-4: 32px;
  --sp-5: 40px; --sp-6: 56px; --sp-7: 80px;

  /* Vertical rhythm between page sections. Adjacent sections each apply this as
     padding, so the gap a visitor SEES is 2x this number. One place to tune the
     whole page's density -- raise for airier, lower for tighter. */
  --section-y: 24px;   /* mobile  -> 48px between sections */

  --radius:    20px;
  --radius-sm: 12px;
  --radius-pill: 999px;
  --maxw:      980px;   /* content column */
  --measure:   65ch;    /* readable text measure */

  /* Instrument Sans tops out at 700, so 700 IS the site's bold. Do not set 800
     anywhere: the browser would synthesise it and the result looks smeared. */
  --font: "Instrument Sans", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
          Roboto, Helvetica, Arial, sans-serif;
}

/* ----------------------------------------------------------------------------
   RESET / BASE
   ---------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  position: relative;
  min-height: 100%;
  font-family: var(--font);
  color: var(--ink);
  line-height: 1.6;
  font-size: 17px;
  overflow-x: hidden;                 /* never allow horizontal scroll */
  background-color: var(--canvas);
  /* the signature graph-paper grid */
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 48px 48px;
}

/* soft corner colour-wash: strong in the hero, a fainter one lower down */
body::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(900px 620px at 88% -8%, var(--accent-wash), transparent 60%),
    radial-gradient(760px 560px at 6% 52%, var(--accent-wash-soft), transparent 65%);
}

/* keep real content above the wash layer */
.site-header, main, .site-footer { position: relative; z-index: 1; }

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

h1, h2, h3 { color: var(--ink); line-height: 1.12; letter-spacing: -0.01em; margin: 0 0 var(--sp-2); font-weight: 700; }
h1 { font-size: clamp(30px, 6vw, 46px); }
h2 { font-size: clamp(24px, 4.4vw, 34px); }
h3 { font-size: clamp(19px, 3vw, 22px); }
p  { margin: 0 0 var(--sp-2); }

a { color: var(--accent-strong); text-underline-offset: 2px; }

/* accent highlight behind key words in headlines */
/* An <em> inside a highlight is the hero's coined term ('Profit Pockets'):
   Instrument Serif italic, which is the designed companion to Instrument Sans,
   so the accent reads as calligraphic without becoming a script face.

   Scoped to .hl em rather than to em generally, so ordinary emphasis anywhere
   else keeps the body font. Georgia is the fallback because it is the widest
   installed serif with a real italic; a generic serif alone would drop to
   Times, which is noticeably narrower and would reflow the headline while the
   webfont loads. */
.hl em {
  font-family: "Instrument Serif", Georgia, "Times New Roman", serif;
  font-style: italic;
  /* Instrument Serif is a SINGLE-WEIGHT family — Google returns 400 even when
     500/600/700 are requested — so weight here is synthesised by the browser,
     never a real bold cut. At 400 the phrase read lighter than the 700 headline
     around it, which is what made it look thin.

     700 plus a hairline stroke rather than weight alone: synthesis thickens a
     serif italic unevenly, smearing the thin strokes while the thick ones barely
     move, and the stroke fills that back in uniformly. currentColor keeps it on
     --accent-strong from .hl, so it thickens rather than outlines. Dial the
     stroke down to 0.2px, or drop it and go back to 600, if it looks heavy. */
  font-weight: 700;
  -webkit-text-stroke: 0.35px currentColor;
  /* Instrument Serif sits on a smaller x-height than Instrument Sans at the
     same px, so this brings it back level with the words either side. */
  font-size: 1.08em;
  letter-spacing: 0.01em;
}

.hl {
  background: var(--accent-wash);
  color: var(--accent-strong);
  padding: 0.02em 0.28em;
  border-radius: 8px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.ink-soft { color: var(--ink-soft); }
.measure  { max-width: var(--measure); }
.center   { text-align: center; }

/* screen-reader-only utility */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ----------------------------------------------------------------------------
   LAYOUT
   ---------------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: 20px;
}
section { padding-block: var(--section-y); }
@media (min-width: 720px) { :root { --section-y: 36px; } }  /* -> 72px between sections */

.section-head { text-align: center; margin-bottom: var(--sp-2); }
.section-head p { color: var(--ink-soft); margin-inline: auto; max-width: var(--measure); }

/* ----------------------------------------------------------------------------
   CARDS
   ---------------------------------------------------------------------------- */
.card {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: var(--sp-4);
}
@media (min-width: 720px) { .card { padding: var(--sp-5); } }

.card--accent {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: transparent;
}
.card--accent h1, .card--accent h2, .card--accent h3 { color: var(--accent-ink); }

/* ----------------------------------------------------------------------------
   TOP BAR
   ---------------------------------------------------------------------------- */
.site-header {
  padding-block: var(--sp-3);
  text-align: center;
}
.wordmark {
  display: inline-flex; align-items: center; gap: 10px;
  font-weight: 700; letter-spacing: 0.14em; font-size: 14px; color: var(--ink);
  text-transform: uppercase; text-decoration: none;
}
.wordmark img { width: 26px; height: 26px; }

/* ----------------------------------------------------------------------------
   PILLS
   ---------------------------------------------------------------------------- */
.pill {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  max-width: 100%;                        /* never exceed the container on mobile */
  background: var(--accent-wash); color: var(--accent-strong);
  border: 1px solid var(--card-border);
  padding: 8px 16px; border-radius: var(--radius-pill);
  font-size: 13px; font-weight: 700; letter-spacing: 0.01em; line-height: 1.4;
}
.pill .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex: none; }

/* Breathing dot: the core softens in and out while a ring expands and fades out
   of it, so it reads as a live signal rather than a static bullet. Same rhythm
   family as the seat meter and the Live badge. The ring is a pseudo-element so
   it costs no markup, and it is parked behind the dot with a negative z-index.
   Cancelled wholesale by the reduced-motion block at the end of this file. */
.pill--breathe .dot {
  position: relative;
  animation: dotbreathe 2.4s ease-in-out infinite;
}
.pill--breathe .dot::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: 50%;
  background: var(--accent);
  z-index: -1;
  animation: dotring 2.4s ease-out infinite;
}
@keyframes dotbreathe {
  0%, 100% { transform: scale(1);    opacity: 1; }
  50%      { transform: scale(0.82); opacity: 0.75; }
}
@keyframes dotring {
  0%   { transform: scale(1); opacity: 0.45; }
  70%  { transform: scale(2.6); opacity: 0; }
  100% { transform: scale(2.6); opacity: 0; }
}
.pill-text { text-wrap: balance; }        /* wraps to tidy, balanced lines on small screens */
@media (max-width: 430px) {
  .pill { font-size: 12px; padding: 7px 14px; gap: 7px; }
}

/* ----------------------------------------------------------------------------
   BUTTONS (one style everywhere)
   ---------------------------------------------------------------------------- */
.btn {
  appearance: none; cursor: pointer; border: 0;
  font-family: inherit; font-weight: 700; font-size: 17px; letter-spacing: 0.01em;
  color: var(--accent-ink); background: var(--accent);
  padding: 16px 30px; border-radius: var(--radius-pill);
  box-shadow: var(--shadow), 0 0 0 0 var(--accent-wash);
  transition: transform .18s ease, box-shadow .25s ease, filter .18s ease;
  text-decoration: none; display: inline-flex; align-items: center; justify-content: center;
  gap: 10px; line-height: 1;
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg), 0 0 26px var(--accent-wash); filter: saturate(1.05); }
.btn:active { transform: translateY(0); }
.btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.btn--block { display: flex; width: 100%; }

.btn--ghost {
  background: transparent; color: var(--accent-strong);
  border: 1.5px solid var(--card-border); box-shadow: none;
}
.btn--ghost:hover { box-shadow: none; background: var(--accent-wash); }

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

/* Opt-in CTA treatment: a gloss sweeps across on an interval (and speeds up on
   hover), plus a springy lift with the arrow sliding right. The sweep is a
   pseudo-element so it needs no markup and no JS; pointer-events:none keeps it
   from swallowing the click. Reduced motion is handled in the block at the
   bottom of this file, which cancels every animation and both transforms. */
.btn--fx {
  position: relative;
  overflow: hidden;                       /* clips the gloss to the pill radius */
  transition: transform .18s cubic-bezier(.34,1.56,.64,1),
              box-shadow .25s ease, filter .18s ease;
}
.btn--fx::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  background: linear-gradient(105deg,
    transparent 35%, rgba(255,255,255,0.55) 50%, transparent 65%);
  transform: translateX(-120%);
  animation: btn-sweep 3.4s ease-in-out infinite;
}
.btn--fx:hover::after { animation-duration: 1.1s; }
/* Lift the label and arrow ABOVE the gloss. Without this the ::after (positioned,
   z-index:auto) paints over the in-flow text and the label's contrast collapses
   from 6.6:1 to ~1.9:1 each time the sweep crosses it, failing AA on the primary
   CTA. The glint should ride on the fill, not wash out the words. */
.btn--fx > * { position: relative; z-index: 1; }
/* Holds off-screen for the first 55% of the cycle, so the gloss reads as an
   occasional glint rather than a continuous shimmer. */
@keyframes btn-sweep {
  0%, 55% { transform: translateX(-120%); }
  100%    { transform: translateX(120%); }
}

/* .btn--on-accent is a near-black button, where the same 55% white gloss reads as
   a harsh flash instead of a sheen. Same motion, much softer highlight. */
.btn--on-accent.btn--fx::after {
  background: linear-gradient(105deg,
    transparent 35%, rgba(255,255,255,0.18) 50%, transparent 65%);
}

.btn--fx:hover  { transform: translateY(-3px) scale(1.02); }
.btn--fx:active { transform: translateY(1px) scale(0.985); }

.btn-arrow { display: inline-block; transition: transform .22s cubic-bezier(.34,1.56,.64,1); }
.btn--fx:hover .btn-arrow { transform: translateX(5px); }

/* Mid-submit the button should stop asking for attention. */
.btn--fx:disabled { cursor: default; opacity: 0.85; }
.btn--fx:disabled::after { animation: none; opacity: 0; }
.btn--fx:disabled:hover { transform: none; }
.btn--fx:disabled:hover .btn-arrow { transform: none; }

/* ----------------------------------------------------------------------------
   REUSABLE CTA CLUSTER
   ---------------------------------------------------------------------------- */
.cta-cluster { text-align: center; }
.cta-microcopy {
  color: var(--ink-soft); font-size: 14px; margin-top: var(--sp-2); margin-bottom: var(--sp-2);
}

/* social-proof strip */
.social-proof {
  display: inline-flex; align-items: center; gap: 14px; flex-wrap: wrap; justify-content: center;
}
.avatars { display: inline-flex; }
.avatars img, .avatars .avatar {
  width: 34px; height: 34px; border-radius: 50%;
  border: 2px solid var(--canvas); margin-left: -10px; background: var(--accent-wash);
}
/* The shipped avatars are square crops, so this changes nothing today. It is
   here so a non-square replacement dropped into config fills the circle
   instead of squashing. */
.avatars img { object-fit: cover; }
.avatars .avatar:first-child, .avatars img:first-child { margin-left: 0; }
.stars { display: inline-flex; gap: 2px; color: var(--highlight); }
.stars svg { width: 18px; height: 18px; }
.proof-label { font-size: 14px; color: var(--ink-soft); font-weight: 600; }
.proof-label strong { color: var(--ink); }

/* ----------------------------------------------------------------------------
   HERO
   ---------------------------------------------------------------------------- */
.hero { padding-top: var(--sp-4); }
/* No wordmark bar above it any more, so the pill is the first thing on the page.
   Just enough room to clear the viewport edge, not a full section's worth. */
.hero--top { padding-top: var(--sp-2); }
.hero .pill { margin-bottom: var(--sp-3); }
.hero--top .pill { margin-bottom: var(--sp-2); }
.hero h1 { margin-bottom: var(--sp-3); }
.hero .subhead { font-size: clamp(17px, 2.4vw, 20px); color: var(--ink-soft); max-width: var(--measure); margin-inline: auto; }

/* /replay-optin's hero at a wider rhythm than the shared --sp-2, which read as
   one crowded block. The countdown rules that used to live here went with the
   countdown when the replay became evergreen; only pill, h1 and subhead stack
   above the teaser now. Gaps only, nothing here changes size or order. */
.hero--replay { padding-top: var(--sp-4); }
.hero--replay .pill { margin-bottom: var(--sp-4); }
.hero--replay h1 { margin-bottom: var(--sp-3); }
.hero--replay .subhead { margin-bottom: var(--sp-5); }
@media (max-width: 619px) {
  /* Same rhythm, one step down, so the teaser still starts near the fold. */
  .hero--replay { padding-top: var(--sp-3); }
  .hero--replay .pill { margin-bottom: var(--sp-3); }
  .hero--replay .subhead { margin-bottom: var(--sp-4); }
}

.hero-grid {
  display: grid; gap: var(--sp-3); margin-top: var(--sp-5);
  grid-template-columns: 1fr;
}
@media (min-width: 820px) {
  .hero-grid { grid-template-columns: 1.05fr 0.95fr; align-items: stretch; }
}

/* media frame (video / photo placeholders) */
.media-frame {
  border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow);
  border: 1px solid var(--card-border); background: var(--card);
}
.media-frame img { width: 100%; height: 100%; object-fit: cover; }

/* the scarcity stack on the right of the hero */
.scarcity-stack { display: flex; flex-direction: column; gap: var(--sp-3); }
.scarcity-stack .card { padding: var(--sp-3); }

/* Countdown and CTA share one card. They used to be separate stack items, so
   the flex gap did the spacing; inside a single card the cluster needs its own. */
.countdown-cta .cta-cluster { margin-top: var(--sp-3); }

/* ----------------------------------------------------------------------------
   CAPACITY METER
   ---------------------------------------------------------------------------- */
.meter-label, .countdown-label {
  font-size: 12px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-soft); margin-bottom: var(--sp-2);
}
.seat-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 4px;
  margin-bottom: var(--sp-2);
}
.seat {
  aspect-ratio: 1 / 1; border-radius: 3px;
  background: var(--accent-wash); border: 1px solid var(--card-border);
  transition: background-color 0.45s ease, border-color 0.45s ease, transform 0.3s ease;
}
.seat.taken { background: var(--accent); border-color: transparent; }
/* heatmap: filled seats are green near the start, orange in the middle, red near the end */
.seat.taken.zone-green  { background: var(--seat-green); }
.seat.taken.zone-orange { background: var(--seat-orange); }
.seat.taken.zone-red    { background: var(--seat-red); }
.meter-caption { font-size: 14px; color: var(--ink); font-weight: 700; }
.meter-caption .num { color: var(--accent-strong); }

/* gentle pulse on the meter to feel live */
.meter-caption .num { animation: softpulse 2.6s ease-in-out infinite; display: inline-block; }

/* ---- live "waitroom" motion (only active when config fakeUrgency is on) ---- */
.meter-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: var(--sp-2); }
.meter-head .meter-label { margin-bottom: 0; }
.live-badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--accent-strong);
}
.live-dot { position: relative; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex: none; }
.live-dot::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%; background: var(--accent);
  animation: livering 1.8s ease-out infinite;
}
/* dark stars/decorations on the bright-orange accent card stay legible */
.card--accent .stars { color: var(--accent-ink); }
/* live mode: the whole grid breathes together as one soft, continuous shimmer */
.seat-grid.is-live .seat.taken { animation: seatbreathe 2.8s ease-in-out infinite; }
@keyframes seatbreathe {
  0%, 100% { opacity: 1;    transform: scale(1); }
  50%      { opacity: 0.55; transform: scale(0.84); }
}
@keyframes livering { 0% { transform: scale(1); opacity: 0.55; } 100% { transform: scale(2.6); opacity: 0; } }

/* hero scarcity stack is centered on its own now that the intro video is gone */
.hero-scarcity { max-width: 460px; margin: var(--sp-5) auto 0; }

/* ----------------------------------------------------------------------------
   COUNTDOWN (flip-style cards)
   ---------------------------------------------------------------------------- */
.countdown { display: flex; align-items: flex-start; gap: 6px; justify-content: center; }
.cd-unit { text-align: center; }
.cd-box {
  position: relative;
  background: var(--ink); color: #fff;
  border-radius: 10px; min-width: 50px; padding: 12px 8px;
  font-size: clamp(22px, 5vw, 30px); font-weight: 700; font-variant-numeric: tabular-nums;
  box-shadow: var(--shadow);
}
/* the "flip" seam line across the middle of each card */
.cd-box::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 1px; background: rgba(255,255,255,0.14);
}
.cd-tag { font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-soft); margin-top: 6px; font-weight: 700; }
.cd-sep { color: var(--accent-strong); font-size: clamp(20px, 4vw, 26px); font-weight: 700; padding-top: 10px; animation: softpulse 1.4s ease-in-out infinite; }
.countdown.is-live .cd-box { background: var(--accent); }
.countdown-live-msg { text-align: center; font-weight: 700; color: var(--accent-strong); }

/* ----------------------------------------------------------------------------
   BENEFIT / CHECK LISTS
   ---------------------------------------------------------------------------- */
.check-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3); }
.check-item { display: grid; grid-template-columns: 28px 1fr; gap: var(--sp-2); align-items: start; }
.check-badge {
  width: 26px; height: 26px; border-radius: 50%; background: var(--accent);
  display: grid; place-items: center; flex: none; margin-top: 2px;
}
.check-badge svg { width: 15px; height: 15px; color: var(--accent-ink); }
.check-item .line { font-weight: 700; color: var(--ink); }
.check-item .sub  { color: var(--ink-soft); font-size: 15px; }

/* X list for the consequence split */
.x-badge { background: var(--danger); }
.x-list .check-item .line { color: var(--ink); }

/* compact outcome list (host section) */
.outcome-list { display: grid; gap: var(--sp-2); }
.outcome-list .check-item .line { font-weight: 600; }

/* ----------------------------------------------------------------------------
   BONUSES
   ---------------------------------------------------------------------------- */
/* align-items: stretch (the grid default) is what makes every card in a row
   match the tallest one. The old 3-up rule used align-items:center, which sized
   each card to its own content and left the row ragged. */
.bonus-grid { display: grid; gap: var(--sp-3); grid-template-columns: 1fr; align-items: stretch; }

/* Four-card variant: one column on phones, 2x2 everywhere above that.
   A single row of four inside the 860px content column left each card about
   187px wide, which is too narrow for a 30-word body sitting under a 56px icon
   tile. 2x2 gives roughly 398px per card instead. To go back to one row of
   four, add: @media (min-width:1024px){ .bonus-grid--4 { grid-template-columns:
   repeat(4,1fr); } } and widen .bonus-section .container. */
@media (min-width: 560px) { .bonus-grid--4 { grid-template-columns: repeat(2, 1fr); } }

/* the original 3-up layout, for any grid that does not opt into --4 */
@media (min-width: 720px) {
  .bonus-grid:not(.bonus-grid--4) { grid-template-columns: repeat(3, 1fr); align-items: center; }
}
.bonus { text-align: center; }
.bonus .icon-wrap {
  width: 56px; height: 56px; border-radius: 16px; margin: 0 auto var(--sp-2);
  background: var(--accent-wash); display: grid; place-items: center;
}
.bonus .icon-wrap svg { width: 28px; height: 28px; color: var(--accent-strong); }
.bonus h3 { margin-bottom: 6px; }
.bonus p { color: var(--ink-soft); font-size: 15px; margin: 0; }
.bonus--featured {
  background: var(--accent); color: var(--accent-ink);
  transform: none;
}
/* The featured card used to sit 5% larger than its neighbours. In a four-up row
   with equal-height cards that scale makes it taller than the rest, which is the
   one thing the layout is supposed to avoid, so the emphasis is now carried by
   the orange fill, the badge, and a deeper shadow instead of size. */
@media (min-width: 720px) { .bonus--featured { box-shadow: var(--shadow-lg); } }
.bonus--featured h3 { color: var(--accent-ink); }
.bonus--featured p { color: rgba(10,18,38,0.74); }
.bonus--featured .icon-wrap { background: rgba(10,18,38,0.12); }
.bonus--featured .icon-wrap svg { color: var(--accent-ink); }
.bonus--featured .tag {
  display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; background: rgba(10,18,38,0.14); color: var(--accent-ink);
  padding: 4px 12px; border-radius: var(--radius-pill); margin-bottom: var(--sp-2);
}

/* ----------------------------------------------------------------------------
   TWO-COLUMN (host, media + text)
   ---------------------------------------------------------------------------- */
.two-col { display: grid; gap: var(--sp-4); grid-template-columns: 1fr; align-items: center; }
@media (min-width: 720px) { .two-col { grid-template-columns: 1fr 1fr; } .two-col.media-right .media { order: 2; } }

/* result cards (social proof) */
.result-grid { display: grid; gap: var(--sp-3); grid-template-columns: 1fr; }
@media (min-width: 620px) { .result-grid { grid-template-columns: repeat(3, 1fr); } }
/* (.result-card .stat / .who removed with the placeholder stat cards; the grid
   now holds only the result screenshots) */

/* Result screenshots sitting in the same grid as the stat cards. The base
   .media-frame img uses object-fit:cover with height:100%, which would stretch
   each screenshot to its grid row and crop the numbers out of it. These are tall
   portrait captures, so they keep their own aspect ratio and sit at the top of
   the row instead. */
.result-grid .media-frame { align-self: start; }
.result-grid .media-frame img { height: auto; object-fit: contain; }

.results-disclaimer {
  font-size: 12px; line-height: 1.6; text-align: center;
  max-width: var(--measure); margin: var(--sp-3) auto 0;
}

/* ---- video testimonials (Wistia, vertical 9:16) ---------------------------
   Three across on desktop, one column on mobile. The frame reuses the same
   rounded/bordered/shadowed treatment as .media-frame so the videos sit in the
   section as if they were part of the original card set. */
/* Videos lead the section, screenshots follow underneath. */
.video-testimonials {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: 1fr;      /* one testimonial per row, always */
  margin-bottom: var(--sp-3);
}

/* Each testimonial is its own two-column row on desktop (player | caption) and
   collapses to player-above-caption on mobile. The player column is capped so a
   9:16 vertical clip cannot grow to full width and push the caption off-screen. */
.vt-item { margin: 0; display: grid; gap: var(--sp-3); grid-template-columns: 1fr; }
@media (min-width: 720px) {
  .vt-item {
    grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
    gap: var(--sp-4);
    align-items: center;
  }
}

.vt-frame {
  border-radius: var(--radius);
  overflow: hidden;                    /* clips the player to the rounded corners */
  box-shadow: var(--shadow);
  border: 1px solid var(--card-border);
  background: var(--card);
}
/* 16:9 embeds (YouTube). The wrapper owns the box; the iframe fills it, because
   an embed's own width/height attributes do not survive a fluid column. */
.vt-frame--wide { aspect-ratio: 16 / 9; }
.vt-frame--wide iframe { display: block; width: 100%; height: 100%; border: 0; }
@media (min-width: 720px) {
  /* a 16:9 clip in a 300px column is tiny, so let the wide ones run larger */
  .vt-item:has(.vt-frame--wide) { grid-template-columns: minmax(0, 460px) minmax(0, 1fr); }
}

.vt-item figcaption {
  font-size: 16px; line-height: 1.6; color: var(--ink-soft);
  align-self: center;
}
/* Orange headline above each testimonial caption. Uses --accent-strong, not
   --accent: the bright orange fails AA as text on a light background. */
.vt-headline {
  display: block;
  color: var(--accent-strong);
  font-weight: 700;
  font-size: 18px;
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin-bottom: 6px;
}
@media (min-width: 720px) { .vt-headline { font-size: 20px; } }
@media (min-width: 720px) {
  .vt-item figcaption { font-size: 17px; max-width: 52ch; }
}

/* Wistia's own placeholder: a blurred swatch holding the 9:16 box while the
   web component boots. Kept from the embed snippets, moved here so the markup
   stays free of inline <style> blocks. padding-top:177.78% == 16/9. */
wistia-player[media-id='r185w1sed0']:not(:defined),
wistia-player[media-id='ejjhtlaboh']:not(:defined) {
  display: block;
  filter: blur(5px);
  padding-top: 177.78%;
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
}
wistia-player[media-id='r185w1sed0']:not(:defined) { background-image: url('https://fast.wistia.com/embed/medias/r185w1sed0/swatch'); }
wistia-player[media-id='ejjhtlaboh']:not(:defined) { background-image: url('https://fast.wistia.com/embed/medias/ejjhtlaboh/swatch'); }

/* The main 16:9 videos: qtzd60x0s7 on the registration confirmations,
   nb4irg23tf on /confirmation, vu4aaivfx4 on /replay. Unlike the 9:16
   testimonials above, these sit inside .video-embed, which already reserves a
   16:9 box, so their undefined state fills that box instead of reserving its
   own with padding-top. (Wistia's own snippet ships padding-top:56.25% for
   this; inside .video-embed that would stack a second aspect box on top of the
   one the container already has.) Same blurred swatch, same purpose: no layout
   jump on boot. */
wistia-player[media-id='qtzd60x0s7']:not(:defined),
wistia-player[media-id='nb4irg23tf']:not(:defined),
wistia-player[media-id='vu4aaivfx4']:not(:defined),
wistia-player[media-id='7cbytlrnqp']:not(:defined),
wistia-player[media-id='xu2wo99rcf']:not(:defined) {
  display: block;
  width: 100%;
  height: 100%;
  filter: blur(5px);
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
}
wistia-player[media-id='qtzd60x0s7']:not(:defined) { background-image: url('https://fast.wistia.com/embed/medias/qtzd60x0s7/swatch'); }
wistia-player[media-id='nb4irg23tf']:not(:defined) { background-image: url('https://fast.wistia.com/embed/medias/nb4irg23tf/swatch'); }
wistia-player[media-id='vu4aaivfx4']:not(:defined) { background-image: url('https://fast.wistia.com/embed/medias/vu4aaivfx4/swatch'); }
wistia-player[media-id='7cbytlrnqp']:not(:defined) { background-image: url('https://fast.wistia.com/embed/medias/7cbytlrnqp/swatch'); }
wistia-player[media-id='xu2wo99rcf']:not(:defined) { background-image: url('https://fast.wistia.com/embed/medias/xu2wo99rcf/swatch'); }

/* consequence split */
.split { display: grid; gap: var(--sp-3); grid-template-columns: 1fr; }
@media (min-width: 720px) { .split { grid-template-columns: 1fr 1fr; } }
/* The column headers no longer carry an icon, so this is a plain block heading
   sitting flush with the card's left padding. The bullets below stay indented by
   their own 28px badge column, which reads as a normal heading-over-list. */
.split .card h3 { display: block; }
.split-neg { background: var(--danger-wash); border-color: rgba(178,58,46,0.18); }

/* ----------------------------------------------------------------------------
   FINAL CTA
   ---------------------------------------------------------------------------- */
.final-cta { text-align: center; }
.final-cta .countdown { margin: var(--sp-4) 0; }

/* ----------------------------------------------------------------------------
   FOOTER
   ---------------------------------------------------------------------------- */
.site-footer {
  padding-block: var(--sp-4);
  border-top: 1px solid var(--card-border);
  margin-top: var(--sp-3);
}
.site-footer .container { max-width: 720px; }
.site-footer p, .site-footer small { color: var(--ink-soft); font-size: 13px; line-height: 1.6; }
.site-footer .disclaimer { margin-bottom: var(--sp-3); }
.footer-links { display: flex; gap: var(--sp-2); flex-wrap: wrap; justify-content: center; margin-top: var(--sp-2); }
.footer-links a { color: var(--ink-soft); font-size: 13px; }

/* ----------------------------------------------------------------------------
   REPLAY FUNNEL EMBEDS
   ---------------------------------------------------------------------------- */
/* The booking calendar. Tall enough that Calendly-style widgets do not scroll
   inside themselves on a phone, which is the usual reason a booking page feels
   broken. min() keeps it from eating the whole screen on a short window. */
.booking-embed {
  border-radius: var(--radius); overflow: hidden;
  border: 1px solid var(--card-border); background: var(--card);
  box-shadow: var(--shadow);
  height: min(1050px, 148vh);
}
.booking-embed iframe { width: 100%; height: 100%; border: 0; display: block; }
@media (min-width: 720px) { .booking-embed { height: 760px; } }

/* Bare variant: the calendar sits straight on the page background. iClosed's
   widget draws its own card, so the frame above double-framed it. Sizing is
   kept, only the chrome goes. overflow returns to visible on purpose, so the
   widget's own dropdowns (timezone, month) are not clipped at the edge. */
.booking-embed--bare {
  border: 0; background: none; box-shadow: none;
  border-radius: 0; overflow: visible;
}

/* /vsl. The player gets an accent glow so it reads as the page's centre of
   gravity, the way the reference site's video does. The apply card below is a
   normal .card whose heading block centres over the calendar. */
.vsl-frame { box-shadow: 0 0 0 1px var(--accent-wash), 0 18px 60px var(--accent-wash); }
.apply-card { text-align: center; }
.apply-card .booking-embed { margin-top: var(--sp-3); text-align: left; }

/* The policy links on /book-call, sitting under the calendar instead of in a
   footer. .footer-links already handles the row itself; this only buys the
   separation the removed footer's border and padding used to provide. */
.booking-legal { margin-top: var(--sp-3); padding-bottom: var(--sp-4); }

/* /book-call deliberately has NO one-screen / overflow:hidden treatment.
   It was tried and reverted: iClosed's flow does not end at the time picker,
   it continues into its booking questions, and pinning the page to the
   viewport left no way to scroll down and answer them. The calendar must be
   free to grow and the page free to scroll past it. */

/* ---------------------------------------------------------------------------
   REPLAY TEASER (/replay-optin)

   A locked stand-in for the replay, NOT the replay. Nothing here loads the
   Wistia player, because the whole page exists to collect details before the
   video is reachable; an embedded player would hand it over for free. What the
   visitor sees is the video's own poster frame, blurred hard enough that it
   reads as "there is something here" without reading as "watch it now".
   --------------------------------------------------------------------------- */
.replay-teaser {
  position: relative; isolation: isolate;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius); overflow: hidden;
  border: 1px solid var(--card-border); box-shadow: var(--shadow);
  background: var(--ink);
  display: grid; place-items: center;
  padding: var(--sp-3);
}
/* The poster sits in its own layer so the blur never touches the button or the
   label stacked on top of it. Scaled up because blur() samples past the edges
   and would otherwise show a soft transparent border. */
.replay-teaser__poster {
  position: absolute; inset: 0; z-index: -2;
  background-position: center; background-size: cover; background-repeat: no-repeat;
  /* Fallback when replayVideoId is still a placeholder: an accent wash rather
     than a blank black box, so an unconfigured page still looks deliberate. */
  background-image: linear-gradient(140deg, var(--accent) 0%, var(--ink) 70%);
  filter: blur(18px) saturate(1.1);
  transform: scale(1.12);
}
/* Darkens the poster so white text and the orange button both clear AA against
   whatever frame the video happens to open on. */
.replay-teaser__veil {
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(20,20,15,0.45) 0%, rgba(20,20,15,0.72) 100%);
}
.replay-teaser__body { text-align: center; display: grid; justify-items: center; gap: var(--sp-2); max-width: 34ch; }
.replay-teaser__play {
  width: 64px; height: 64px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255,255,255,0.16);
  border: 1px solid rgba(255,255,255,0.35);
  backdrop-filter: blur(2px);
}
.replay-teaser__play svg { width: 26px; height: 26px; fill: #fff; margin-left: 3px; }
.replay-teaser__label { color: #fff; font-weight: 700; font-size: clamp(16px, 2.6vw, 20px); line-height: 1.25; }
.replay-teaser__note { color: rgba(255,255,255,0.78); font-size: 13px; margin: 0; }
.replay-teaser .btn { width: 100%; max-width: 320px; }

/* The play badge is decorative and the button underneath is the real control,
   so the pulse is pure motion with no information in it. */
@media (prefers-reduced-motion: no-preference) {
  .replay-teaser__play { animation: softpulse 2.2s ease-in-out infinite; }
}
@media (max-width: 419px) {
  /* 16:9 leaves too little height for a play badge, a line of copy, a button
     and a note at 375px, so the badge goes and the box grows taller. */
  .replay-teaser { aspect-ratio: 4 / 3; padding: var(--sp-2); }
  .replay-teaser__play { display: none; }
}

/* ---------------------------------------------------------------------------
   SURVEY EMBED (the confirmation pages' "Your Next Steps" card)

   brand-operator-survey.html in an iframe. It is a multi-step form, so its own
   height changes as the visitor moves through it, and a same-origin iframe
   cannot resize itself from the inside without a postMessage handshake on both
   ends. Rather than add that, the frame is given a fixed height tall enough for
   the LONGEST step (the five-option experience question) and the survey scrolls
   internally on anything shorter. min() stops it eating a whole short laptop
   window.
   --------------------------------------------------------------------------- */
.survey-embed {
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--card-border);
  /* Matches the survey's own #0D0D0D, so the frame does not flash white while
     it loads. */
  background: #0D0D0D;
  height: min(680px, 86vh);
}
.survey-embed iframe { width: 100%; height: 100%; border: 0; display: block; }
@media (max-width: 559px) {
  /* Taller on a phone: the survey is single-column there, so the same content
     runs longer and a 680px frame would scroll for every step. */
  .survey-embed { height: min(760px, 88vh); }
}

/* Shown where the replay player was once replayDeadlineISO has passed. The
   deliberate opposite of .embed-placeholder below: that one is loud because it
   means someone forgot to configure the page, this one is a finished state a
   visitor is meant to see, so it stays calm and on-brand. */
.embed-offline {
  display: grid; place-items: center; text-align: center;
  min-height: 220px; height: 100%; padding: var(--sp-3);
  color: var(--ink-soft); font-weight: 700; font-size: 16px; line-height: 1.5;
  background: var(--card);
  border: 1px solid var(--card-border); border-radius: var(--radius);
}

/* Shown in place of an embed whose config value is still a placeholder. Loud
   on purpose: an unset booking link should be impossible to miss in review. */
.embed-placeholder {
  display: grid; place-items: center; text-align: center;
  min-height: 220px; height: 100%; padding: var(--sp-3);
  color: var(--accent-strong); font-weight: 700; font-size: 15px; line-height: 1.5;
  background: var(--accent-wash);
  border: 2px dashed var(--accent); border-radius: var(--radius);
}

/* ----------------------------------------------------------------------------
   MODAL (native <dialog>)
   ---------------------------------------------------------------------------- */
dialog.modal {
  border: 0; padding: 0; border-radius: var(--radius);
  width: min(440px, calc(100vw - 32px)); max-width: 440px;
  box-shadow: var(--shadow-lg); background: var(--card); color: var(--ink);
}
dialog.modal::backdrop { background: rgba(20,20,15,0.55); backdrop-filter: blur(2px); }
.modal-inner { padding: var(--sp-4); }
.modal-close {
  position: absolute; top: 12px; right: 12px; width: 36px; height: 36px;
  border-radius: 50%; border: 1px solid var(--card-border); background: var(--card);
  cursor: pointer; display: grid; place-items: center; color: var(--ink-soft);
}
.modal-close:hover { background: var(--accent-wash); color: var(--ink); }
.modal-close:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* The heading and subhead were removed from the markup, and the close button
   used to sit alongside them. With them gone the first row rides up under it:
   measured, the button (x 311-347, y 22-58) overlapped the "Last name" label
   box (x 193-343, y 46-67), so a tap at the top-right of that label closed the
   form instead of focusing the field. The glyphs never touched, which is
   exactly why this is worth a comment - it looked fine and was not.
   34px puts the first label below the button's bottom edge. */
.modal-inner > form { padding-top: 34px; }

.field { margin-bottom: var(--sp-2); text-align: left; }
.field label { display: block; font-weight: 700; font-size: 14px; margin-bottom: 6px; }
.field input, .field select, .field textarea {
  width: 100%; font-family: inherit; font-size: 16px; /* 16px avoids iOS zoom */
  padding: 13px 14px; border-radius: var(--radius-sm);
  border: 1.5px solid var(--card-border); background: #fff; color: var(--ink);
}
.field textarea { resize: vertical; min-height: 108px; line-height: 1.5; }
/* native select arrow varies by OS; normalise the box, keep the OS control */
.field select { appearance: none; background-image: none; cursor: pointer; padding-right: 40px; }
.field .select-wrap { position: relative; }
.field .select-wrap::after {
  content: ""; position: absolute; right: 16px; top: 50%; width: 9px; height: 9px;
  border-right: 2px solid var(--ink-soft); border-bottom: 2px solid var(--ink-soft);
  transform: translateY(-70%) rotate(45deg); pointer-events: none;
}
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash);
}
/* Country code + number: two controls that read as one field.
   min-width:0 on the number input is load-bearing. A flex item defaults to
   min-width:auto, so the 16px input would refuse to shrink below its intrinsic
   width and push the row past the modal at 375px, which is the one width this
   site is not allowed to break at. */
.tel-group { display: flex; align-items: stretch; gap: 8px; }
/* Pinned width, NOT auto. A select sizes to its widest option, and with 229
   countries in the list that is "Saint Vincent and the Grenadines", which blew
   the row out and left 30px for the number. Fixed width clips the label; the
   options are written dial-code-first so what survives the clip is the part
   that matters while typing. */
.tel-group .tel-cc { flex: 0 0 88px; }
.tel-group .tel-cc select { width: 100%; padding-right: 26px; text-overflow: ellipsis; }
@media (min-width: 480px) { .tel-group .tel-cc { flex-basis: 96px; } }
.tel-group input { flex: 1 1 auto; min-width: 0; }
.field .hint { font-size: 12px; color: var(--ink-soft); margin-top: 4px; }
.field .error-msg { color: var(--danger); font-size: 13px; margin-top: 5px; display: none; }
.field.has-error input, .field.has-error select, .field.has-error textarea { border-color: var(--danger); }
.field.has-error .error-msg { display: block; }
.modal .btn { margin-top: var(--sp-1); }
.modal .form-foot { font-size: 12px; color: var(--ink-soft); margin-top: var(--sp-2); text-align: center; }

/* First and last name share a row. Two ~145px inputs are comfortable even at
   375px, and collapsing that pair is worth ~90px of dialog height. */
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.field-row .field { margin-bottom: var(--sp-2); }

/* ----------------------------------------------------------------------------
   MODAL DENSITY ON SHORT SCREENS

   The opt-in form must be usable without scrolling INSIDE the dialog: the
   submit button being below the fold of a box that is itself below the fold is
   the single easiest way to lose a registration. At 375x667 the content ran
   873px inside a 627px dialog, so the button sat 143px past the bottom of the
   screen and nothing on screen suggested there was more.

   Keyed on max-height, not max-width, because this is a vertical problem. A
   tall phone has room and keeps the roomier spacing; a short laptop window gets
   the same treatment a phone does, which is correct.

   Two things are deliberately NOT touched:
     - input font-size stays 16px. Anything smaller makes iOS zoom the page on
       focus, which is far worse than a slightly taller form.
     - tap targets stay at/above 44px. Padding is trimmed only as far as that
       floor allows.
   ---------------------------------------------------------------------------- */
@media (max-height: 760px) {
  /* Anchored near the top instead of vertically centred. A <dialog> centres
     itself in the LAYOUT viewport, which does not change when the keyboard
     opens, so a centred dialog spends its lower half behind the keyboard the
     moment a field is focused. Starting high puts the form in the strip that
     stays visible. margin-inline stays auto so it is still centred sideways. */
  dialog.modal { margin-block: 10px auto; }
  .modal-inner { padding: 18px; }
  .modal .field { margin-bottom: 10px; }
  .modal .field-row .field { margin-bottom: 10px; }
  .modal .field label { font-size: 13px; margin-bottom: 4px; }
  .modal .field input, .modal .field select { padding: 11px 12px; }
  .modal .tel-group .tel-cc select { padding-right: 24px; }
  /* The reassurance under the phone field is nice, not load-bearing. */
  .modal #reg-phone-hint { display: none; }
  .modal .choice-field legend { font-size: 13px; line-height: 1.32; margin-bottom: 6px; }
  .modal .choice span { padding: 11px 12px; min-height: 44px; font-size: 14px; }
  .modal .btn { margin-top: 4px; }
  .modal .form-foot { font-size: 11px; margin-top: 4px; }
  /* The last few pixels. Without these the form overflowed its own box by 4-6px
     on a 320x568 SE and on a 375x667 phone once Safari's chrome is counted, and
     "almost fits" still shows a scrollbar. */
  .modal-inner { padding: 16px; }
  .modal-actions { padding-top: 0; }
}

/* Safety net, and it must stay. Aiming to fit is not the same as guaranteeing
   it: a landscape phone, a 250px-tall browser window or a visitor with large
   text can still overflow. Clipping there would put the submit button
   permanently out of reach, so the dialog is allowed to scroll as a last
   resort rather than hide its own button.

   The height is deliberately NOT tied to window.visualViewport. Doing that made
   the dialog shrink the instant the keyboard appeared, and a container visibly
   collapsing under your thumb reads as the page breaking. It keeps its size and
   simply sits high enough that the fields being typed into stay above the
   keyboard. */
dialog.modal {
  /* 16px, not 24px. The form is 546px on a 320x568 SE and a 24px inset caps the
     box at 544, which is a 2px overflow and therefore a scrollbar on the one
     device with the least room to spare. Pairs with the 10px top margin below,
     so the dialog still sits clear of both screen edges. */
  max-height: calc(100dvh - 16px);
  overflow-y: auto;
}

/* Pinned to the bottom of the dialog's scroll box. On every size that fits -
   which is all of them from 320x568 up - this does nothing at all. It only
   earns its keep where the content genuinely overflows, such as landscape,
   where it keeps "Save my seat" reachable instead of stranded below the fold.
   The background is required, or the form would scroll visibly underneath. */
.modal-actions {
  position: sticky; bottom: 0; z-index: 2;
  background: var(--card);
  padding-top: var(--sp-1);
  margin-top: auto;
}

/* ----------------------------------------------------------------------------
   THANK-YOU PAGE
   ---------------------------------------------------------------------------- */
.success-mark {
  width: 84px; height: 84px; border-radius: 50%; background: var(--accent);
  display: grid; place-items: center; margin: 0 auto var(--sp-3); box-shadow: var(--shadow);
}
.success-mark svg { width: 44px; height: 44px; color: var(--accent-ink); }
.ty-details { display: grid; gap: var(--sp-2); }
.ty-detail-row { display: flex; align-items: center; gap: 12px; }
.ty-detail-row svg { width: 20px; height: 20px; color: var(--accent-strong); flex: none; }
.cal-buttons { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-3); }
.cal-buttons .btn { flex: 1 1 auto; }
.step-list { counter-reset: step; list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3); }
.step-item { display: grid; grid-template-columns: 34px 1fr; gap: var(--sp-2); align-items: start; }
.step-num {
  counter-increment: step; width: 32px; height: 32px; border-radius: 50%;
  background: var(--accent); color: var(--accent-ink); display: grid; place-items: center;
  font-weight: 700; flex: none;
}
.step-num::before { content: counter(step); }

/* the two "watch first" videos: no card wrapper, just headline + caption above
   each player, stacked straight down the page. The frame around the still is
   the player itself, not a container. */
.video-stack { display: grid; gap: var(--sp-5); grid-template-columns: 1fr; }
.video-head { text-align: center; margin-bottom: var(--sp-3); }
.video-head h2 { margin-bottom: var(--sp-2); }
.video-head p { color: var(--ink-soft); max-width: var(--measure); margin: 0 auto; }
.video-link { display: block; border-radius: var(--radius); text-decoration: none; }
.video-link:focus-visible { outline: 3px solid var(--accent); outline-offset: 4px; }
.video-link .media-frame { transition: transform .18s ease, box-shadow .25s ease; }
.video-link:hover .media-frame { transform: translateY(-2px); box-shadow: var(--shadow-lg); }

/* ----------------------------------------------------------------------------
   PROGRESS BAR (top of the thank-you / almost-registered page)
   Default (no-JS) state = the fill already sits at its final width. JS adds
   .js-armed to start it at zero, then animates it to the real value.
   ---------------------------------------------------------------------------- */
.progress-band { padding-top: var(--sp-3); }
.progress-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2);
  margin-bottom: 10px;
}
.progress-step {
  font-size: 12px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent-strong);
}
.progress-pct { font-size: 12px; font-weight: 700; color: var(--ink-soft); }
.progress-track {
  height: 10px; border-radius: var(--radius-pill); background: var(--accent-wash);
  border: 1px solid var(--card-border); overflow: hidden;
}
.progress-fill {
  height: 100%; width: var(--progress, 88%); border-radius: var(--radius-pill);
  background: var(--accent);
  transition: width 1.1s cubic-bezier(.22,.7,.24,1);
}
/* arming must be instant, otherwise the bar visibly rewinds from full to zero
   before it fills. Transitions use the after-change style, so releasing the
   class still animates 0 -> target. */
.progress-band.js-armed .progress-fill { width: 0; transition: none; }

/* ----------------------------------------------------------------------------
   INLINE INTAKE FORM (bottom of the thank-you page)
   Same field styling as the modal, laid out two-up on wider screens.
   ---------------------------------------------------------------------------- */
.intake-grid { display: grid; gap: 0 var(--sp-3); grid-template-columns: 1fr; }
@media (min-width: 640px) {
  .intake-grid { grid-template-columns: 1fr 1fr; }
  .intake-grid .field--wide { grid-column: 1 / -1; }
}
.intake-form .btn { margin-top: var(--sp-2); }
.intake-form .form-foot {
  font-size: 12px; color: var(--ink-soft); margin-top: var(--sp-2); margin-bottom: 0;
}
/* (the old in-place success panel is gone: the intake form now redirects to
   confirmed.html, so the .intake-done rules were removed with it) */
[hidden] { display: none !important; }

/* ----------------------------------------------------------------------------
   TRAINING TIMELINE (scroll-activated checklist + confetti payoff)
   Default state = fully lit (JS-off fallback). JS adds .js-armed to enter the
   dormant state, then .is-active per row on scroll.
   ---------------------------------------------------------------------------- */
.timeline { list-style: none; margin: 0; padding: 0; }
.tl-item {
  position: relative;
  display: flex; gap: 22px; align-items: flex-start;
  padding-bottom: 26px;
}
.tl-item:last-child { padding-bottom: 0; }

/* connector: neutral track (::before) + accent fill (::after), threaded through
   each badge center (13px) and extending exactly to the next badge center */
.tl-item::before, .tl-item::after {
  content: ""; position: absolute; left: 12px; top: 13px; width: 2px;
  height: 100%; border-radius: 2px; z-index: 0;
}
.tl-item::before { background: var(--card-border); }
.tl-item::after  { background: var(--accent); transition: height 420ms ease; } /* default: lit */
.tl-item:last-child::before, .tl-item:last-child::after { display: none; }
/* dormant: fill collapses; each filled segment grows back as activation passes it */
.timeline.js-armed .tl-item::after { height: 0; }
.timeline.js-armed .tl-item.seg-lit::after { height: 100%; }

/* badge (default = lit, so JS-off shows a finished checklist) */
.tl-badge {
  position: relative; z-index: 1; flex: none;
  width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent); color: #fff; border: 2px solid transparent;
  transition: background-color 400ms ease, border-color 400ms ease, color 400ms ease, transform 400ms ease;
}
.tl-badge .check-icon { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
/* ring-flash pseudo (around the badge) */
.tl-badge::after {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  border: 2px solid var(--accent); opacity: 0; pointer-events: none; z-index: 0;
}
/* dormant badge, only once JS arms the section */
.timeline.js-armed .tl-badge { background: transparent; border-color: var(--card-border); color: var(--card-border); }
.timeline.js-armed .tl-item.is-active .tl-badge {
  background: var(--accent); border-color: transparent; color: #fff;
  animation: badge-pop 440ms ease;
}
.timeline.js-armed .tl-item.is-active .tl-badge::after { animation: ring-flash 620ms ease-out; }

/* row text */
.tl-text { display: block; }
/* One sentence per row: the promise is bold, the "so ..." payoff that follows
   is not. This replaced the old tl-lead + tl-sub pair, which were two separate
   blocks and so could never share a line box or wrap as one sentence. */
.tl-copy { display: block; color: var(--ink-soft); line-height: 1.4; }
.tl-copy strong { color: var(--ink); font-weight: 700; }
.timeline .tl-text { transition: opacity 400ms ease, transform 400ms ease; }
.timeline.js-armed .tl-text { opacity: 0.45; transform: translateY(4px); }
.timeline.js-armed .tl-item.is-active .tl-text { opacity: 1; transform: none; }

/* ---- celebration: CSS confetti burst on the final badge ----
   The final node used to swap its checkmark for a party emoji. The emoji was
   removed so all five nodes read identically; the badge keeps its orange fill
   and checkmark throughout, and only the confetti + pop remain. */
.confetti-layer { position: absolute; inset: 0; z-index: 4; pointer-events: none; }
.confetti-piece {
  position: absolute; top: 50%; left: 50%;
  width: 3.5px; height: 3.5px; border-radius: 1px;
  opacity: 0; will-change: transform, opacity;
  animation: confetti-burst 700ms ease-out forwards;
}
.timeline.js-armed .tl-item.is-active .tl-badge[data-celebrated] { animation: badge-celebrate 520ms ease; }

@keyframes badge-pop       { 0% { transform: scale(0.6); } 60% { transform: scale(1.15); } 100% { transform: scale(1); } }
@keyframes badge-celebrate { 0% { transform: scale(1); } 45% { transform: scale(1.22); } 100% { transform: scale(1); } }
@keyframes ring-flash      { 0% { opacity: 0.7; transform: scale(0.7); } 100% { opacity: 0; transform: scale(1.5); } }
@keyframes confetti-burst {
  0%   { transform: translate(-50%,-50%) scale(0) rotate(0); opacity: 0; }
  15%  { opacity: 1; }
  60%  { opacity: 1; }
  100% { transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) rotate(var(--rot)) scale(1); opacity: 0; }
}

/* ----------------------------------------------------------------------------
   SCROLL REVEAL (progressive; hidden state only applies when JS marks it ready)
   ---------------------------------------------------------------------------- */
.reveal { opacity: 1; transform: none; }
.js-ready .reveal { opacity: 0; transform: translateY(18px); transition: opacity .6s ease, transform .6s ease; }
.js-ready .reveal.is-visible { opacity: 1; transform: none; }

@keyframes softpulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}

/* ----------------------------------------------------------------------------
   REDUCED MOTION
   ---------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------
   CONFIRMATION PAGE (confirmed.html)
   ---------------------------------------------------------------------------- */

/* Page-wide confetti burst. Fixed layer over everything, never interactive.
   JS injects the pieces; with JS off the layer stays empty and invisible. */
.celebrate-layer {
  position: fixed; inset: 0;
  z-index: 60;
  pointer-events: none;
  overflow: hidden;
}
.celebrate-piece {
  position: absolute;
  top: 22%; left: 50%;                 /* launches from behind the headline */
  width: 9px; height: 14px;
  border-radius: 2px;
  opacity: 0;
  will-change: transform, opacity;
  animation: celebrate-arc var(--dur, 2600ms) cubic-bezier(.18,.7,.42,1) var(--delay, 0ms) forwards;
}
/* Up and outward first, then falls past the fold: a burst, not a snowfall. */
@keyframes celebrate-arc {
  0%   { opacity: 0; transform: translate(0, 0) rotate(0deg) scale(0.6); }
  8%   { opacity: 1; }
  32%  { transform: translate(calc(var(--dx) * 0.42), var(--peak, -22vh))
                    rotate(calc(var(--rot) * 0.35)) scale(1); }
  100% { opacity: 0; transform: translate(var(--dx), 96vh) rotate(var(--rot)) scale(0.9); }
}

/* The completed progress fill, when no JS is driving it. */
.progress-fill.is-complete { width: 100%; }

/* (the .quote-* written-testimonial styles were removed with the placeholder
   "What operators say" block; real video and screenshot proof replaced it) */

/* FAQ — native <details>, so it opens with JS off */
/* chevron */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .js-ready .reveal { opacity: 1 !important; transform: none !important; }
  .btn:hover { transform: none; }
  /* the opt-in CTA: no lift, no arrow slide, and the gloss parked out of frame */
  .btn--fx:hover, .btn--fx:active { transform: none; }
  .btn--fx:hover .btn-arrow { transform: none; }
  .btn--fx::after { opacity: 0; }
  /* no confetti at all: JS skips generating it, this is the belt-and-braces */
  .celebrate-layer { display: none; }
  }

/* ============================================================================
   BONUS SECTION ANIMATION

   Four deliberate deviations from the spec as written, each fixing a defect
   that would otherwise land silently. All four are noted at the point of use.
   ============================================================================ */

/* [1] --bonus-scale carries each card's resting scale through every stage of the
   animation. It is 1 for all four cards now that the featured card is no longer
   scaled up (equal-height row), but the hook stays so that reintroducing an
   emphasis scale does not require rewriting the entrance and hover transforms. */
.bonus-grid .bonus { --bonus-scale: 1; --bonus-rest-shadow: var(--shadow); }
@media (min-width: 720px) {
  /* --bonus-scale stays 1: the featured card is no longer scaled up, so that the
     four cards keep equal height. Only its resting shadow differs. */
  .bonus-grid .bonus--featured { --bonus-rest-shadow: var(--shadow-lg); }
}

/* Transitions live on the un-armed selector so hover still works with JS off. */
.bonus-grid .bonus {
  transition:
    opacity 620ms cubic-bezier(0.22, 1, 0.36, 1),
    transform 620ms cubic-bezier(0.22, 1, 0.36, 1),
    box-shadow 260ms ease,
    border-color 260ms ease;
  transition-delay: calc(var(--i, 0) * 110ms);
  will-change: opacity, transform;
}

/* [2] The spec gated its JS-off fallback on a `.no-js` class that this project
   never sets, so with JS disabled the cards would have stayed at opacity:0
   forever. This project's convention is the inverse and is used here: the
   default CSS is the finished, lit state, and JS adds .js-armed to enter the
   dormant state. JS off means no .js-armed, means nothing ever hides. */
.bonus-grid.js-armed .bonus {
  opacity: 0;
  transform: translateY(22px) scale(calc(var(--bonus-scale) * 0.985));
}
.bonus-grid.js-armed.is-in .bonus {
  opacity: 1;
  transform: translateY(0) scale(var(--bonus-scale));
}

.bonus-grid .bonus .icon-wrap {
  transition:
    opacity 500ms cubic-bezier(0.34, 1.56, 0.64, 1),
    transform 500ms cubic-bezier(0.34, 1.56, 0.64, 1);
  transition-delay: calc(var(--i, 0) * 110ms + 190ms);
}
.bonus-grid.js-armed .bonus .icon-wrap {
  opacity: 0;
  transform: scale(0.72) rotate(-10deg);
}
.bonus-grid.js-armed.is-in .bonus .icon-wrap {
  opacity: 1;
  transform: scale(1) rotate(0deg);
}

/* Icon stroke draws itself in a beat after the card lands. */
.bonus-grid .bonus .icon-wrap svg [stroke],
.bonus-grid .bonus .icon-wrap svg path,
.bonus-grid .bonus .icon-wrap svg circle {
  transition: stroke-dashoffset 760ms cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: calc(var(--i, 0) * 110ms + 280ms);
}
.bonus-grid.js-armed .bonus .icon-wrap svg [stroke],
.bonus-grid.js-armed .bonus .icon-wrap svg path,
.bonus-grid.js-armed .bonus .icon-wrap svg circle {
  stroke-dasharray: 90;
  stroke-dashoffset: 90;
}
.bonus-grid.js-armed.is-in .bonus .icon-wrap svg [stroke],
.bonus-grid.js-armed.is-in .bonus .icon-wrap svg path,
.bonus-grid.js-armed.is-in .bonus .icon-wrap svg circle {
  stroke-dashoffset: 0;
}

/* "Most valued" tag: drops in, then one sheen passes over it. */
.bonus-grid .bonus--featured .tag {
  position: relative;
  overflow: hidden;
  transition:
    opacity 420ms cubic-bezier(0.34, 1.56, 0.64, 1),
    transform 420ms cubic-bezier(0.34, 1.56, 0.64, 1);
  transition-delay: 620ms;
}
.bonus-grid.js-armed .bonus--featured .tag {
  opacity: 0;
  transform: translateY(-6px) scale(0.94);
}
.bonus-grid.js-armed.is-in .bonus--featured .tag {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.bonus-grid .bonus--featured .tag::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  transform: translateX(-140%);
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(255, 255, 255, 0.55) 50%,
    transparent 100%
  );
}
.bonus-grid.js-armed.is-in .bonus--featured .tag::after {
  animation: bonus-sheen 1100ms ease-out 1000ms 1 forwards;
}
@keyframes bonus-sheen {
  from { transform: translateX(-140%); }
  to   { transform: translateX(140%); }
}

/* [3] The spec's keyframes started and ended at `box-shadow: 0 0 0 0 rgba(0,0,0,0)`,
   which would have stripped the card's resting shadow (--shadow, or --shadow-lg
   on desktop) the moment the loop began. Both stops now carry the resting shadow
   plus a second layer whose alpha is the only thing animating, so the pulse adds
   depth instead of replacing it, and the two stops interpolate smoothly because
   the shadow count matches. */
.bonus-grid.js-armed.is-in .bonus--featured {
  animation: bonus-breathe 5.5s ease-in-out 1600ms infinite;
}
@keyframes bonus-breathe {
  0%, 100% { box-shadow: var(--bonus-rest-shadow), 0 10px 34px -14px rgba(0, 0, 0, 0); }
  50%      { box-shadow: var(--bonus-rest-shadow), 0 10px 34px -14px rgba(0, 0, 0, 0.28); }
}

@media (hover: hover) and (pointer: fine) {
  /* [4] Selectors carry .js-armed.is-in so they out-specify the entrance rules
     above; without that the hover state would never win. transition-delay is
     also reset, otherwise the third card inherits its 220ms stagger delay and
     takes a fifth of a second to react to the pointer. */
  .bonus-grid.js-armed.is-in .bonus:hover {
    transform: translateY(-5px) scale(var(--bonus-scale));
    box-shadow: 0 16px 38px -18px rgba(0, 0, 0, 0.34);
    transition-delay: 0ms;
  }
  .bonus-grid.js-armed.is-in .bonus:hover .icon-wrap {
    transform: scale(1.07) rotate(-4deg);
    transition-delay: 0ms;
    transition-duration: 260ms;
  }
  .bonus-grid.js-armed.is-in .bonus--featured:hover {
    animation-play-state: paused;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bonus-grid .bonus,
  .bonus-grid.js-armed .bonus,
  .bonus-grid .bonus .icon-wrap,
  .bonus-grid.js-armed .bonus .icon-wrap,
  .bonus-grid .bonus--featured .tag,
  .bonus-grid.js-armed .bonus--featured .tag {
    opacity: 1;
    transform: none;
    transition: none;
    animation: none;
  }
  /* (the featured card is no longer scaled up, so there is no emphasis scale to
     restore here. Re-adding one would make it taller than the other three.) */
  .bonus-grid .bonus .icon-wrap svg [stroke],
  .bonus-grid .bonus .icon-wrap svg path,
  .bonus-grid .bonus .icon-wrap svg circle,
  .bonus-grid.js-armed .bonus .icon-wrap svg [stroke],
  .bonus-grid.js-armed .bonus .icon-wrap svg path,
  .bonus-grid.js-armed .bonus .icon-wrap svg circle {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
    transition: none;
  }
  .bonus-grid.js-armed.is-in .bonus--featured,
  .bonus-grid.js-armed.is-in .bonus--featured .tag::after {
    animation: none;
  }
}

/* ============================================================================
   CALL PREP BLOCK (confirmed.html)
   Lives INSIDE the Event details .card, so it inherits the site's light
   language: white card, near-black headings, muted grey body, peach icon tiles
   with orange line-art. A hairline is the only separation between the two
   halves; there is deliberately no second background or theme break.
   ============================================================================ */

/* the same peach rounded-square tile the bonus cards use */
@media (max-width: 559px) {
  /* stack: tile above the copy, both centred */
  }

/* ---- illustrative calendar-invite mock ---- */

/* an arrow sitting immediately beside the thing it points at */

/* ---- mail app marks ---- */

/* Honeypot wrapper. Must NOT be display:none or visibility:hidden — bots skip
   those, which defeats the trap. Pushed off-screen instead, and kept out of the
   tab order and the accessibility tree by the markup. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

/* ---- segmented Yes/No choice in the opt-in form -------------------------
   The real radios stay in the DOM (keyboard, screen readers, form semantics)
   but are visually replaced by their label, so the whole pill is the hit
   target. Sized past 44px for thumbs. */
.choice-field { border: 0; padding: 0; margin: 0 0 var(--sp-2); }
.choice-field legend {
  padding: 0; margin-bottom: 8px;
  font-weight: 700; font-size: 14px; line-height: 1.45; color: var(--ink);
}
.choice-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.choice { position: relative; display: block; cursor: pointer; }
/* not display:none - that would drop it from the accessibility tree */
.choice input {
  position: absolute; opacity: 0;
  width: 100%; height: 100%; margin: 0; inset: 0; cursor: pointer;
}
.choice span {
  display: block; text-align: center;
  padding: 13px 14px; min-height: 46px;
  border: 1.5px solid var(--card-border); border-radius: var(--radius-sm);
  background: var(--card); color: var(--ink);
  font-weight: 700; font-size: 15px; line-height: 1.4;
  transition: border-color .15s ease, background .15s ease, color .15s ease;
}
.choice:hover span { border-color: var(--accent); }
.choice input:checked + span {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
}
.choice input:focus-visible + span { outline: 3px solid var(--accent); outline-offset: 2px; }
/* The generic .field.has-error rule paints an input border, but these radios are
   opacity:0 and the visible control is the sibling span, so the error state has
   to be forwarded to it or the group would go red-flagged with nothing to see. */
.choice-field.has-error .choice span { border-color: var(--danger); }

/* ---- video placeholders (confirmation page) -----------------------------
   A 16:9 box that keeps its shape before any embed exists, so dropping a real
   iframe in later cannot shift the layout. Replace the inner <span> with the
   embed and the wrapper keeps the rounded corners and clipping. */
.video-embed {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--card-border);
  background: var(--card);
  box-shadow: var(--shadow);
}
.video-embed iframe,
.video-embed video,
.video-embed wistia-player { display: block; width: 100%; height: 100%; border: 0; }

.video-embed--placeholder {
  display: grid; place-items: center;
  background: var(--accent-wash-soft);
  border-style: dashed;
  border-color: rgba(59,130,246,0.35);
}
.video-embed--placeholder span {
  display: grid; gap: 4px; justify-items: center; text-align: center;
  color: var(--accent-strong); font-weight: 700; font-size: 15px;
}
.video-embed--placeholder small { color: var(--ink-soft); font-weight: 400; font-size: 12.5px; }

.video-embed-grid {
  display: grid; gap: var(--sp-3); grid-template-columns: 1fr;
  margin-top: var(--sp-3);
}
@media (min-width: 720px) { .video-embed-grid { grid-template-columns: 1fr 1fr; } }

/* ---- callback countdown ---- */
@media (min-width: 560px) {
  }

/* arrows are decorative; below ~420px the mock bar has no room for them */
@media (max-width: 419px) {  }

/* ============================================================================
   CONFIRMATION PAGE v2
   ============================================================================ */

/* ----------------------------------------------------------------------------
   STICKY MOBILE CTA

   A fixed bar carrying the register button, shown on phones once the hero's own
   CTA has scrolled out of view. Two deliberate constraints:

   1. MOBILE ONLY. On desktop the hero CTA and the five in-page CTA clusters are
      never far away, and a fixed bar would just eat vertical space on a page
      that does not need it. Hidden from 720px up.
   2. It only appears once the hero CTA is GONE. Showing it while that button is
      still on screen puts two identical buttons in view at once, which reads as
      a mistake rather than as help. initStickyCta() in main.js owns the toggle.

   z-index sits under .topbar-alert (50) and .celebrate-layer (60) so the banner
   still reads over it and confetti still passes above it.
   ---------------------------------------------------------------------------- */
.sticky-cta {
  position: fixed; left: 0; right: 0; bottom: 0;
  z-index: 40;
  padding: 10px var(--sp-2);
  /* Clears the iPhone home indicator; 0 everywhere that has no inset. */
  padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  background: rgba(255,255,255,0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-top: 1px solid var(--card-border);
  box-shadow: 0 -6px 24px rgba(20,20,15,0.10);
  /* Parked off-screen rather than display:none, so the reveal can animate and
     so the button keeps its layout while hidden. aria-hidden + inert-by-
     translate is not enough on its own, hence visibility. */
  transform: translateY(110%);
  visibility: hidden;
  transition: transform 220ms ease, visibility 220ms ease;
}
.sticky-cta.is-visible { transform: none; visibility: visible; }
.sticky-cta .btn { width: 100%; min-height: 50px; }
/* The registration dialog is in the top layer, so a fixed bar would sit dimmed
   behind its backdrop rather than being covered. Pull it away instead. */
body.modal-open .sticky-cta { transform: translateY(110%); visibility: hidden; }

/* Keeps the last of the page reachable above the bar. Only applied while the
   bar is actually showing, so a page that never reveals it keeps its spacing. */
@media (max-width: 719px) {
  body.has-sticky-cta { padding-bottom: 78px; }
}
@media (min-width: 720px) {
  .sticky-cta { display: none; }
  body.has-sticky-cta { padding-bottom: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .sticky-cta { transition: none; }
}

/* Attention banner. Sticky, so it stays with them as they scroll. */
.topbar-alert {
  position: sticky; top: 0; z-index: 50;
  background: var(--accent); color: #FFFFFF;
  text-align: center;
  font-weight: 700; font-size: 14px; line-height: 1.4;
  padding: 10px 0;
  box-shadow: 0 2px 10px rgba(20,20,15,0.12);
}
@media (min-width: 560px) { .topbar-alert { font-size: 15px; } }

/* Bar only: no step label, no percentage text. Shorter and thicker. */
.progress-band--bare { padding-block: var(--sp-3); }
.progress-band--bare .container { max-width: 420px; }
.progress-track--thick { height: 14px; border-radius: 999px; }
.progress-track--thick .progress-fill { border-radius: 999px; }

/* "You'll get a call from ..." */
.contact-notice {
  /* Single column since the phone icon was removed. It was `auto 1fr` for the
     icon-then-text pair; left as-is the body would have landed in the `auto`
     column and shrink-wrapped instead of filling the box. */
  display: grid; grid-template-columns: 1fr; gap: var(--sp-2);
  align-items: start;
  margin-top: var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  background: var(--accent-wash-soft);
}
.contact-notice__body { display: grid; gap: 4px; }
/* Bold and in full-strength ink: this is the line that has to land, so it is
   sized to match h2 exactly (clamp 24/4.4vw/34) rather than sitting under it.
   h2 is the ceiling on purpose: going past it would make a notice louder than
   the section titles it sits between. Line-height stays 1.3 because 1.5 is
   body-copy leading and looks slack at this size. */
.contact-notice__copy {
  margin: 0; font-size: clamp(24px, 4.4vw, 34px); line-height: 1.3;
  font-weight: 700; color: var(--ink);
}
/* Bumped alongside the line above so the pair still reads as one block; at
   14.5px it looked like a footnote once the headline grew. */
.contact-notice__timer { margin: 0; font-size: clamp(14px, 1.9vw, 16px); line-height: 1.5; color: var(--ink-soft); font-weight: 400; }

/* Centred variant. The icon stops being a left-hand column and sits above the
   text, because a centred block hanging off a left-aligned icon reads as a
   mistake rather than a choice. */
.contact-notice--center {
  justify-items: center;
  text-align: center;
}
.contact-notice--center .contact-notice__body { justify-items: center; }
.contact-notice--center .contact-notice__copy,
.contact-notice--center .contact-notice__timer { max-width: 46ch; }
.contact-notice__clock {
  font-weight: 700; color: var(--accent-strong);
  font-variant-numeric: tabular-nums;
}

/* Breakout videos: one per row by default so each plays large. */
.video-stack { display: grid; gap: var(--sp-3); grid-template-columns: 1fr; }

/* A breakout video with its question above it (/vslconfirmation). The stack's
   gap separates questions; this only ties each heading to its own player. */
.faq-video__q {
  font-size: clamp(18px, 2.4vw, 22px); line-height: 1.3;
  text-align: center; margin-bottom: var(--sp-2);
}
.video-stack:has(.faq-video) { gap: var(--sp-5); }
@media (min-width: 720px) {
  .video-stack--2up { grid-template-columns: 1fr 1fr; }
}

/* Short-form proof: two per row on desktop, one on mobile. */
.proof-grid { display: grid; gap: var(--sp-3); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 620px) { .proof-grid { grid-template-columns: 1fr 1fr; } }

/* Mobile-only CTAs threaded through the proof grid, so a long scroll of
   testimonials is never more than a few screens from a way to register.
   Below 620px the grid is a single column and each of these is a full-width
   row between testimonials. At 620px and up the grid goes two-up, where a
   button landing in one of two columns reads as a broken cell rather than a
   CTA, so they are removed from the layout entirely. */
.proof-cta { display: grid; justify-items: center; gap: var(--sp-1); margin: var(--sp-1) 0; }
@media (min-width: 620px) { .proof-cta { display: none; } }
.proof-grid .media-frame img { height: auto; object-fit: contain; }
/* inside the two-up grid a testimonial stacks: player above, caption below */
.vt-item--stacked { grid-template-columns: 1fr !important; gap: var(--sp-2) !important; }

/* Long-form case study: video beside the context and outcome bullets. */
.case-study { display: grid; gap: var(--sp-3); grid-template-columns: 1fr; }
@media (min-width: 860px) {
  .case-study { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: var(--sp-4); align-items: center; }
}
.case-study { margin-bottom: var(--sp-5); }
.case-study__context { color: var(--ink-soft); font-size: 15.5px; }
.case-study__label {
  font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent-strong); margin-bottom: 8px;
}
.case-study__body .check-list { gap: var(--sp-2); }

/* ---- steps inside one card (confirmation page) --------------------------
   Restored: the two steps read as one block rather than separate cards, with
   the event details continuing in the same container below a hairline. */
.prep-title { text-align: center; font-size: clamp(21px, 3.2vw, 28px); margin-bottom: var(--sp-4); }

.prep-steps {
  list-style: none; margin: 0 auto; padding: 0;
  display: grid; gap: var(--sp-4);
  max-width: 620px;
}
.prep-step { display: grid; grid-template-columns: 46px 1fr; gap: var(--sp-3); align-items: start; }
/* the same peach tile the bonus cards use, carrying the step number */
.prep-icon {
  width: 46px; height: 46px; border-radius: 14px;
  display: grid; place-items: center;
  background: var(--accent-wash); color: var(--accent-strong);
  font-weight: 700; font-size: 19px; line-height: 1;
  flex: none;
}
.prep-copy h3 { font-size: 17px; line-height: 1.35; margin: 0 0 6px; }
.prep-copy p { color: var(--ink-soft); font-size: 15px; line-height: 1.65; margin: 0 0 var(--sp-2); }
.prep-copy .cal-buttons { margin-top: var(--sp-2); }
/* The "click the button above..." line under each step's buttons. .prep-copy p
   carries margin-bottom only, so this paragraph was sitting flush against the
   button row and the two read as one congested block. Adjacent-sibling so it
   only applies where a paragraph actually follows buttons, rather than adding
   top margin to every paragraph in a step. Covers both button containers: the
   calendar and WhatsApp steps use .cal-buttons, the inbox step .mail-apps. */
.prep-copy .cal-buttons + p,
.prep-copy .mail-apps + p { margin-top: var(--sp-3); }
@media (max-width: 559px) {
  .prep-step { grid-template-columns: 1fr; justify-items: center; text-align: center; gap: var(--sp-2); }
}

/* ---------------------------------------------------------------------------
   STEPS AS SEPARATE CARDS (the three confirmation pages)

   One card per step. The heading sits outside them, so the section reads as a
   title over a short stack rather than one long card. .card supplies the
   surface; everything here is spacing and type, since a step that owns its box
   can afford a slightly larger heading and does not need the cramped rhythm it
   used inside a shared one.
   --------------------------------------------------------------------------- */
.prep-steps--cards { gap: var(--sp-3); max-width: 680px; }

/* Tighter than .card's own sp-4/sp-5: four stacked boxes at that padding push
   the last step and the event details a long way down the page. */
.prep-steps--cards .prep-step { padding: var(--sp-3); }
@media (min-width: 720px) { .prep-steps--cards .prep-step { padding: var(--sp-4); } }

.prep-steps--cards .prep-copy h3 { font-size: 18px; margin-bottom: var(--sp-1); }
/* Kills the trailing margin under whichever element ends the step, so every
   card has the same visual padding at the bottom as it does at the top. This
   matters here because the three steps end differently: a paragraph, a button
   row, and the mail-app links. */
.prep-steps--cards .prep-copy > :last-child { margin-bottom: 0; }

@media (max-width: 559px) {
  /* The centred single-column layout inherited from .prep-step needs the number
     tile to keep its own breathing room once it is inside a box. */
  .prep-steps--cards .prep-step { gap: var(--sp-2); padding: var(--sp-3); }
}

.event-strip {
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--card-border);
}
/* As its own card the hairline and the offset are redundant: the card's border
   already separates it from the last step. */
.event-strip--card {
  margin-top: var(--sp-3);
  border-top: 0;
  max-width: 680px;
  margin-inline: auto;
  /* MUST match .prep-steps--cards .prep-step, not .card's own sp-4/sp-5. The
     step cards override that padding to be tighter, so inheriting .card here
     inset this card's contents 8px further than the cards stacked above it and
     nothing lined up down the left edge. */
  padding: var(--sp-3);
}
@media (min-width: 720px) { .event-strip--card { padding: var(--sp-4); } }
/* Content-sized rows that wrap as a group, NOT three forced columns.
   repeat(3, 1fr) was fine while this sat inside the full-width steps card, but
   the strip is its own 680px card now, so each column got about 180px — too
   narrow for "TUESDAY, August 25, 2026" beside a 20px icon, and the date broke
   mid-line. Flex sizes each row to its own text and drops a whole row to the
   next line when width runs out, which holds for any date or timezone string. */
.event-strip .ty-details {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-2) var(--sp-4);
  justify-content: flex-start;
}
/* Flex items default to min-width:auto, which lets a long value push past the
   card edge instead of wrapping inside it. */
.event-strip .ty-detail-row,
.event-strip .ty-detail-row span { min-width: 0; }
@media (max-width: 559px) {
  /* One per line and left-aligned, so the icons form a column to read down. */
  .event-strip .ty-details {
    flex-direction: column; align-items: flex-start; justify-content: flex-start;
  }
}

/* ---- mail provider links (confirmation page, step 1) --------------------
   These are real anchors, not decoration, so they carry hover/active/focus
   states and a 44px-plus hit target. */
.mail-apps { display: flex; flex-wrap: wrap; gap: 10px; margin-top: var(--sp-2); }
.mail-app {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 14px 9px 10px;
  border: 1.5px solid var(--card-border); border-radius: var(--radius-pill);
  background: var(--card); color: var(--ink); text-decoration: none;
  font-weight: 700; font-size: 14px; line-height: 1;
  min-height: 44px;
  transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
.mail-app svg { width: 22px; height: 22px; flex: none; }
.mail-app:hover { border-color: var(--accent); transform: translateY(-1px); box-shadow: var(--shadow); }
.mail-app:active { transform: translateY(0); }
.mail-app:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
@media (max-width: 559px) { .mail-apps { justify-content: center; } }

/* the percentage, sitting just above the bar */
.progress-pct-row { display: flex; justify-content: flex-end; margin-bottom: 6px; }
.progress-pct {
  font-size: 13px; font-weight: 700; color: var(--accent-strong);
  font-variant-numeric: tabular-nums; letter-spacing: .01em;
}

/* the arrow points back up the page, since the button scrolls rather than leaves */
.btn-arrow--up { transform: none; }
.btn--fx:hover .btn-arrow--up { transform: translateY(-3px); }

/* The banner is sticky, so an anchor jump would land the target underneath it.
   scroll-margin-top offsets the landing point by the banner's height plus a
   little breathing room. Applies to smooth and instant scrolling alike. */
[id="steps-title"] { scroll-margin-top: 78px; }
