/* ==========================================================================
   Foresight Premier — product page
   ========================================================================== */

@font-face {
  font-family: "TT Interphases Pro";
  /* woff2 first — same variable font at a third of the bytes (699KB -> 242KB);
     the ttf stays as the fallback for anything that cannot take it */
  src: url("../fonts/TTInterphasesProVariable.woff2") format("woff2-variations"),
       url("../fonts/TTInterphasesProVariable.ttf") format("truetype-variations");
  font-weight: 100 900;
  font-stretch: 75% 100%;
  font-display: swap;
}

@font-face {
  font-family: "TT Interphases Pro Mono";
  src: url("../fonts/TTInterphasesProMonoVar.woff2") format("woff2-variations"),
       url("../fonts/TTInterphasesProMonoVar.ttf") format("truetype-variations");
  font-weight: 400 700;
  font-display: swap;
}

:root {
  /* Palette = Product UI Library tokens (Figma). Base/Black·White, Brand/500·600,
     and the Alpha system: text on dark = Alpha/Text (#fafafa at 100/80/60),
     inks on light = alpha-black-90/70/50, hairlines = alpha 10. */
  --black: #000;              /* Base/Black */
  --elev: #0d0d0d;            /* alpha-white-5 over black */
  --card-dark: #1a1a1a;       /* alpha-white-10 over black */
  --light: #f5f5f6;           /* Gray (dark mode)/50 */
  --white: #fff;              /* Base/White */

  --ink-d1: #f5f5f6;                    /* text-primary (dark) */
  --ink-d2: rgba(250, 250, 250, .6);    /* Alpha/Text/Tertiary */
  --ink-d3: rgba(255, 255, 255, .5);    /* alpha-white-50 */
  --ink-l1: rgba(0, 0, 0, .9);          /* alpha-black-90 */
  --ink-l2: rgba(0, 0, 0, .7);          /* alpha-black-70 */

  --blue: #0348d9;            /* Brand/500 */
  --blue-light: #3ba5ff;      /* no library equivalent — loose value */
  --grad-blue: linear-gradient(94deg, #8fd8ff 0%, #3ba5ff 48%, #1e6dff 100%);

  --line-d: rgba(255, 255, 255, .1);    /* alpha-white-10 */
  --line-l: rgba(0, 0, 0, .1);          /* alpha-black-10 */

  --r-card: 28px;
  --r-panel: 18px;

  /* Scroll parallax: how far a media plate overhangs its clipped window on
     each side (percent of the window's HEIGHT — top/bottom insets resolve
     against it). The JS travel is 5.5%, so this leaves 2.5% of slack; without
     it the plate's edge slips into frame on short cards, where the Foresight+
     deck clamps to 420px and the overhang shrinks with it. */
  --par-over: 8%;

  --font: "TT Interphases Pro", -apple-system, BlinkMacSystemFont,
          "Helvetica Neue", Helvetica, Arial, sans-serif;
  --mono: "TT Interphases Pro Mono", ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;

  --ease: cubic-bezier(.22, .61, .36, 1);
  --nav-h: 52px;
}

/* ---------- Reset / base ---------- */

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

/* Safari drew the whole page oblique (user 2026-09-18): TT Interphases Pro's
   slnt axis runs 0..11 — positive, where OpenType expects negative — and at
   font-style: normal WebKit lands on the far end of it (measured 11.1deg in
   Safari 26.4, 0 in Chrome). Pinning the axis is the one thing that holds it
   upright there; no font-style range or @font-face setting does.
   On every element and !important because the `font` shorthand resets
   font-variation-settings wherever it is used, and that failure is silent
   outside Safari. Nothing here drives an axis by hand: weight and width go
   through font-weight / font-stretch. */
*, *::before, *::after { font-variation-settings: "slnt" 0 !important; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 12px);
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.5;
  letter-spacing: -.011em;
  background: var(--black);
  color: var(--ink-d1);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

::selection { background: rgba(59, 165, 255, .35); }

:focus-visible {
  outline: 2px solid var(--blue-light);
  outline-offset: 3px;
  border-radius: 4px;
}

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

/* ---------- Layout helpers ---------- */

.container        { width: min(1080px, 92vw); margin-inline: auto; }
.container-wide   { width: min(1320px, 94vw); margin-inline: auto; }
.container-narrow { width: min(760px, 92vw);  margin-inline: auto; }

.theme-dark  { background: var(--black); color: var(--ink-d1); }
.theme-light { background: var(--light); color: var(--ink-l1); }

/* ---------- Type ---------- */

/* Display 2xl/Bold — 72/90, −2% (Product UI Library). The old 4.2rem/1.06
   ramp was off-system: it capped at 67px and ran a much tighter leading. */
.h2 {
  font-size: clamp(2.4rem, 5.4vw, 4.5rem);
  font-weight: 700;
  /* 1.25 -> 1.1 (user 2026-09-04): tighter leading between the lines of the
     two-line section heads ("See your game / in new detail" and the like) */
  line-height: 1.1;
  letter-spacing: -.02em;
}
/* Display 3xl/Bold — 96/120, −2%; same step as the hero H1 */
.h2--xl { font-size: clamp(2.7rem, 6.4vw, 6rem); }

.h3-lg {
  font-size: clamp(1.7rem, 3.2vw, 2.4rem);
  font-weight: 700;
  letter-spacing: -.018em;
}

.lede {
  font-size: clamp(1.12rem, 1.9vw, 1.4rem);
  line-height: 1.42;
  letter-spacing: -.013em;
  max-width: 46ch;
  /* Optical gap, not box gap: the h2's line-height went 1.06 → 1.25 with the
     Display 2xl ramp, which added ~5px of half-leading between title and lede
     for free. Pulled 1.1rem → .8rem so the SEEN gap lands back at ~23px. */
  margin-top: .8rem;
}
.theme-dark .lede  { color: var(--ink-d2); }
.theme-light .lede { color: var(--ink-l2); }

/* Corner-bracket tag (per Figma 11720:28332) — Mono sm caps between hairline
   brackets. White on dark, black on light; the brackets follow currentColor. */
.eyebrow {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: .1em; /* 1.2px at 12px */
  text-transform: uppercase;
  color: #fff;
  margin-bottom: .9rem;
}
.theme-light .eyebrow { color: #000; }

.eyebrow::before,
.eyebrow::after {
  content: "";
  flex: none;
  height: 20.03px;
  background: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
}
.eyebrow::before {
  width: 5.132px;
  -webkit-mask-image: url("../assets/tag-bracket-l.svg");
  mask-image: url("../assets/tag-bracket-l.svg");
}
.eyebrow::after {
  width: 5px;
  -webkit-mask-image: url("../assets/tag-bracket-r.svg");
  mask-image: url("../assets/tag-bracket-r.svg");
}

/* Gradient text. Unused on the home since 2026-07-31 (the Foresight+ headline
   went flat white) — kept because the cinematic still builds on --grad-blue. */
.grad {
  background: var(--grad-blue);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.footnote {
  font-size: .8rem;
  line-height: 1.5;
  letter-spacing: 0;
  color: var(--ink-l2);
  max-width: 64ch;
}
.theme-dark .footnote { color: var(--ink-d3); }

/* ---------- Buttons & links ---------- */

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
  margin-top: 1.9rem;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 13px 26px;
  border-radius: 980px;
  font-size: 1rem; /* Text md/Semibold */
  font-weight: 600;
  letter-spacing: -.01em;
  transition: transform .35s var(--ease), filter .35s var(--ease),
              background .35s var(--ease), border-color .35s var(--ease);
  white-space: nowrap;
}
.btn:active { transform: scale(.97); }

.btn--primary {
  /* FSP Button — brand gradient 600→500 */
  background: linear-gradient(90deg, #0342c6, #0348d9);
  border: 1px solid rgba(255, 255, 255, .1);
  color: #fff;
}
.btn--primary:hover { filter: brightness(1.18); transform: translateY(-1px); }

.btn--ghost {
  border: 1.5px solid currentColor;
  color: var(--ink-d1);
}
.theme-light .btn--ghost { color: var(--ink-l1); }
.btn--ghost:hover { transform: translateY(-1px); background: rgba(127,127,127,.09); }

.btn--mini { padding: 8px 14px; font-size: .875rem; line-height: 1.43; } /* Text sm/Semibold, FSP sm */

/* Label roll — the capsule stays still, the text rolls up on hover.
   Markup: <span class="roll"><b>Label</b><b aria-hidden="true">Label</b></span> */
.btn .roll { display: grid; overflow: hidden; }
.btn .roll b {
  grid-area: 1 / 1;
  font-weight: inherit;
  transition: transform .35s var(--ease);
  will-change: transform;
}
.btn .roll b:nth-child(2) { transform: translateY(115%); }
.btn:hover .roll b:first-child,
.btn:focus-visible .roll b:first-child { transform: translateY(-115%); }
.btn:hover .roll b:nth-child(2),
.btn:focus-visible .roll b:nth-child(2) { transform: translateY(0); }

.link-arrow {
  display: inline-block;
  position: relative;
  font-weight: 600;
  font-size: 1.05rem;
  color: #fff;
}
.theme-light .link-arrow { color: var(--ink-l1); }
/* underline is always on; hover renews it — it runs out the right edge and
   re-enters from the left (transform-origin swaps mid-keyframes) */
.link-arrow::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -.18em;
  height: 1.5px;
  background: currentColor;
  transform: scaleX(1);
}
.link-arrow:hover::after,
.link-arrow:focus-visible::after { animation: link-renew .91s var(--ease); }

@keyframes link-renew {
  0%   { transform: scaleX(1); transform-origin: right center; }
  49%  { transform: scaleX(0); transform-origin: right center; }
  50%  { transform: scaleX(0); transform-origin: left center; }
  100% { transform: scaleX(1); transform-origin: left center; }
}
.link-arrow--lg { margin-top: 1.8rem; }

/* ---------- Reveal-on-scroll ---------- */

.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity .9s var(--ease) var(--d, 0s),
              transform .9s var(--ease) var(--d, 0s);
}
.reveal.in { opacity: 1; transform: none; }

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

/* ==========================================================================
   0 · GLOBAL NAV — matches the CURRENT foresightsports.com header (user
   2026-08-14): logo left, uppercase dropdown nav, Specials + Contact Sales
   pills, search and cart. Sits in flow at the very top so it scrolls away;
   the fixed localnav starts just under it and rides up to the viewport top
   (offset in main.js). The slide-in drawer below remains the mobile menu.
   ========================================================================== */

.gnav {
  --gnav-h: 64px;
  position: relative;
  z-index: 101; /* above the localnav (100) while both are on screen */
  height: var(--gnav-h);
  background: #000;
  font-family: "Termina", "Roboto", var(--font);
}
.gnav__inner {
  height: 100%;
  display: flex;
  align-items: center;
  gap: clamp(16px, 2vw, 36px);
  padding-inline: max(3vw, calc((100vw - 1760px) / 2));
}
.gnav__brand { line-height: 0; flex: 0 0 auto; }
.gnav__brand img { display: block; height: 17px; width: auto; }
.gnav__nav {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.6vw, 30px);
  margin-inline: auto;
}
.gnav__item { position: relative; }
.gnav__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 22px 2px; /* tall hit area bridges the hover gap to the dropdown */
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgba(255, 255, 255, .85);
  transition: color .25s var(--ease);
}
.gnav__link:hover { color: #fff; }
.gnav__caret { width: 9px; height: 6px; opacity: .7; transition: transform .25s var(--ease); }
.gnav__item:hover .gnav__caret,
.gnav__item:focus-within .gnav__caret { transform: rotate(180deg); }
/* ---------- mega menus (2026-08-25) ----------
   Rebuilt to match foresightsports.com: a full-width strip under the bar
   rather than a floating card. Launch Monitors carries four product cards and
   a side column of links; every other tab is a centred row of links.
   The panel spans the header, not the item, so .gnav__item--mega goes static
   and the panel positions against .gnav itself. */
.gnav__item--mega { position: static; }
.gnav__mega {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  z-index: 100;
  background: #111;
  border-top: 1px solid rgba(255, 255, 255, .06);
  opacity: 0;
  visibility: hidden;
  transition: opacity .2s var(--ease), visibility 0s linear .2s;
}
.gnav__item--mega:hover .gnav__mega,
.gnav__item--mega:focus-within .gnav__mega {
  opacity: 1;
  visibility: visible;
  transition: opacity .2s var(--ease);
}
.gnav__mega-inner {
  width: min(1152px, 100%);
  margin-inline: auto;
  padding: 3px 24px 5px;
}
.gnav__mega-inner--cards {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 220px;
  gap: 18px;
}
.gnav__cards {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 15px;
  padding-right: 32px;
  border-right: 1px solid rgba(255, 255, 255, .08);
}
.gnav__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 12px 0;
  color: #fff;
  text-decoration: none;
}
.gnav__card-media {
  display: flex;
  align-items: center;
  justify-content: center;
  /* clips rather than trusting the image to constrain itself: in a grid area
     sized by its own content, max-height:100% has nothing definite to resolve
     against and the renders spilled straight out of the panel */
  overflow: hidden;
  width: 100%;
  height: 130px;
  padding: 8px;
  border-radius: 14px;
  background: rgba(255, 255, 255, .06);
  transition: background .2s var(--ease);
}
.gnav__card:hover .gnav__card-media { background: rgba(255, 255, 255, .11); }
.gnav__card-media img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.gnav__card-title { margin-top: 10px; font-size: 16px; font-weight: 700; }
.gnav__card-sub { margin-top: 2px; font-size: 13px; color: #00b3fe; }
.gnav__mega-links {
  display: flex;
  justify-content: center;
  gap: 8px;
  flex-wrap: wrap;
}
.gnav__mega-links--side {
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  gap: 2px;
  padding-top: 8px;
}
.gnav__mega-links a {
  padding: 9px 6px;
  font-size: 15px;
  font-weight: 500;
  color: rgba(255, 255, 255, .7);
  text-decoration: none;
  transition: color .2s var(--ease);
}
.gnav__mega-links a:hover { color: #fff; }
.gnav__mega-links a[aria-current="page"] { color: #00b3fe; }
/* divides the FSX links from our own three pages in the Software panel */
.gnav__mega-sep {
  width: 1px;
  align-self: stretch;
  margin: 6px 10px;
  background: rgba(255, 255, 255, .14);
}
.gnav__mega-links--side .gnav__mega-sep { width: auto; height: 1px; align-self: stretch; margin: 6px 0; }

/* ---- the strips retire exactly where the bar does, at 1060px, where the
   burger drawer takes over. 1280 was wrong: between 1060 and 1280 the desktop
   nav is still on screen, so the tabs sat there with no menus at all. ---- */
@media (max-width: 1060px) {
  .gnav__mega { display: none; }
}

.gnav__dd {
  position: absolute;
  left: 50%;
  top: 100%;
  transform: translateX(-50%) translateY(6px);
  min-width: 230px;
  padding: 10px;
  border-radius: 14px;
  background: rgba(8, 8, 10, .96);
  border: 1px solid rgba(255, 255, 255, .1);
  box-shadow: 0 18px 50px rgba(0, 0, 0, .5);
  opacity: 0;
  visibility: hidden;
  transition: opacity .25s var(--ease), transform .25s var(--ease), visibility 0s linear .25s;
}
.gnav__item:hover .gnav__dd,
.gnav__item:focus-within .gnav__dd {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
  transition: opacity .25s var(--ease), transform .25s var(--ease);
}
.gnav__dd a {
  display: block;
  padding: 9px 12px;
  border-radius: 8px;
  font-size: 13.5px;
  font-weight: 500;
  color: rgba(255, 255, 255, .8);
  transition: background .2s var(--ease), color .2s var(--ease);
}
.gnav__dd a:hover { background: rgba(255, 255, 255, .08); color: #fff; }
.gnav__dd a[aria-current="page"] { color: #00b3fe; }
.gnav__actions { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; margin-left: auto; }
.gnav__pill {
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  padding: 10px 18px;
  border-radius: 27px;
  color: #fff;
  transition: background .25s var(--ease), border-color .25s var(--ease);
}
/* Brand/500, not the old cyan (user 2026-08-25) — the same blue the primary
   CTA is built from, so the bar's one colour accent matches the buttons */
.gnav__pill--specials { background: var(--blue, #0348d9); }
.gnav__pill--specials:hover { background: #1257ea; }
.gnav__pill--ghost { border: 1.5px solid rgba(255, 255, 255, .85); }
.gnav__pill--ghost:hover { border-color: #fff; background: rgba(255, 255, 255, .08); }
.gnav__icon {
  position: relative;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  color: #fff;
}
.gnav__icon svg { width: 20px; height: 20px; }
.gnav__burger {
  display: none; /* mobile only — the drawer takes over below 1280px */
  appearance: none;
  background: none;
  border: 0;
  cursor: pointer;
  flex-direction: column;
  gap: 5px;
  padding: 10px 2px;
}
.gnav__burger span { display: block; width: 22px; height: 2px; background: #fff; border-radius: 1px; }
.gnav__scrim {
  position: fixed;
  inset: 0;
  z-index: 190;
  background: rgba(0, 0, 0, .5);
  opacity: 0;
  visibility: hidden;
  transition: opacity .4s var(--ease), visibility 0s linear .4s;
}
/* right-hand drawer, same recipe as the live site's burger menu */
.gnav__menu {
  position: fixed;
  inset: 0 0 0 auto;
  z-index: 200;
  width: min(600px, 100vw);
  background: #000;
  padding: clamp(56px, 9vh, 84px) clamp(28px, 5vw, 60px) 48px;
  overflow-y: auto;
  transform: translateX(100%);
  visibility: hidden;
  transition: transform .5s var(--ease), visibility 0s linear .5s;
}
.gnav.menu-open .gnav__scrim { opacity: 1; visibility: visible; transition: opacity .4s var(--ease); }
.gnav.menu-open .gnav__menu { transform: none; visibility: visible; transition: transform .5s var(--ease); }
.gnav__close {
  position: absolute;
  top: 20px;
  right: 24px;
  appearance: none;
  background: none;
  border: 0;
  cursor: pointer;
  color: #fff;
  padding: 8px;
}
.gnav__close svg { width: 18px; height: 18px; display: block; }
.gnav__group { margin-bottom: clamp(28px, 4.5vh, 44px); }
.gnav__group-title {
  display: inline-block;
  font-family: "Termina", "Roboto", var(--font);
  font-size: clamp(26px, 3.2vw, 34px);
  font-weight: 700;
  letter-spacing: .01em;
  color: #fff;
  margin-bottom: 14px;
}
.gnav__group ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  column-gap: 28px;
  row-gap: 12px;
}
.gnav__group ul a { color: rgba(255, 255, 255, .6); font-size: 17px; transition: color .2s var(--ease); }
.gnav__group ul a:hover { color: #fff; }
.gnav__group ul a[aria-current="page"] { color: #00b3fe; }
.gnav__foot { display: flex; gap: 28px; margin-top: 8px; }
.gnav__foot a {
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.gnav__foot a:hover { color: #00b3fe; }
/* squeeze tier: keep the full nav alive on laptop widths before collapsing */
@media (max-width: 1380px) {
  .gnav__inner { gap: 14px; }
  .gnav__brand img { height: 16px; }
  .gnav__nav { gap: 11px; }
  .gnav__link { font-size: 11px; letter-spacing: .05em; }
  .gnav__pill { font-size: 12px; padding: 9px 14px; }
  .gnav__icon { width: 32px; }
  .gnav__actions { gap: 8px; }
}
@media (max-width: 1060px) {
  .gnav__nav,
  .gnav__pill--ghost { display: none; }
  .gnav__burger { display: inline-flex; }
}
@media (max-width: 640px) {
  .gnav__pill--specials,
  .gnav__icon--cart { display: none; }
  .gnav__brand img { height: 16px; }
}

.localnav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  background: rgba(8, 8, 10, .4);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid transparent;
  transition: background .4s var(--ease), border-color .4s var(--ease);
}
.localnav.scrolled {
  background: rgba(8, 8, 10, .72);
  border-color: rgba(255, 255, 255, .08);
}

.localnav__inner {
  width: min(1320px, 94vw); /* matches .container-wide — the why-cards row sets the page max width */
  margin-inline: auto;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}

.localnav__brand { display: flex; align-items: center; }
.localnav__brand img { height: 14px; width: auto; }

.localnav__links {
  position: relative;
  display: flex;
  gap: 28px;
  font-size: .875rem; /* Text sm/Semibold */
  font-weight: 600;
  line-height: 1.43;
  color: var(--ink-d2);
}
.localnav__links a { transition: color .3s; }
.localnav__links a:hover,
.localnav__links a.active { color: var(--ink-d1); }

.localnav__ind {
  position: absolute;
  top: calc(100% + 3px);
  left: 0;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--blue);
  opacity: 0;
  pointer-events: none;
  transition: transform .45s var(--ease), opacity .3s var(--ease);
  will-change: transform;
}
.localnav__ind.on { opacity: 1; }

.localnav__actions { display: flex; align-items: center; gap: 14px; }

.localnav__burger {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 34px;
  height: 34px;
  padding: 6px;
}
.localnav__burger span {
  display: block;
  height: 1.5px;
  background: var(--ink-d1);
  transition: transform .35s var(--ease), opacity .35s;
}
.menu-open .localnav__burger span:first-child { transform: translateY(3.25px) rotate(45deg); }
.menu-open .localnav__burger span:last-child  { transform: translateY(-3.25px) rotate(-45deg); }

.localnav__sheet {
  display: none;
  flex-direction: column;
  padding: 12px 4vw 26px;
  gap: 4px;
}
.localnav__sheet a {
  padding: 12px 4px;
  font-size: 1.125rem; /* Text lg/Semibold */
  font-weight: 600;
  color: var(--ink-d1);
  border-bottom: 1px solid rgba(255, 255, 255, .08);
}
.menu-open .localnav__sheet { display: flex; }
.menu-open { background: rgba(8, 8, 10, .96); }

/* ==========================================================================
   1 · HERO
   ========================================================================== */

.hero {
  padding: calc(var(--nav-h) + clamp(64px, 11vh, 120px)) 0 40px;
  text-align: center;
  position: relative;
  overflow: clip;
}
.hero::before {
  /* soft brand glow behind the stage */
  content: "";
  position: absolute;
  left: 50%;
  top: 46%;
  width: 1200px;
  height: 700px;
  transform: translateX(-50%);
  background: radial-gradient(closest-side, rgba(3, 72, 217, .18), transparent 70%);
  pointer-events: none;
  z-index: 1; /* glow above the dot field — same stack as the closer */
}
.hero__copy { position: relative; z-index: 2; }

/* Ambient dot-field canvas (js/dot-field.js), per Figma 11902:3701. The
   design's overlay is a black gradient born opaque at the bottom edge and
   gone by half height (transform resolves to exactly 2×), so the dots live
   in the top half and dissolve behind the copy. Over a black section a mask
   is visually identical to that overlay and keeps the glow stacking simple. */
.hero__shader {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to top, transparent 0%, #000 50%);
          mask-image: linear-gradient(to top, transparent 0%, #000 50%);
}

.hero__kicker { display: flex; justify-content: center; }
.hero__kicker img { height: clamp(15px, 1.6vw, 21px); width: auto; }

.hero__title {
  /* Display 3xl/Bold — 96/120, −2% */
  font-size: clamp(3.2rem, 8.5vw, 6rem);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -.02em;
  margin-top: .5rem;
}

.hero__lede {
  /* Text xl — 20/30 */
  font-size: clamp(1.1rem, 1.4vw, 1.25rem);
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: 0;
  color: var(--ink-d2);
  max-width: 46ch;
  margin: 1.4rem auto 0;
}

.hero .cta-row { justify-content: center; }

/* --- Legend dots --- */

.dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--blue-light);
  display: inline-block;
  flex: none;
}
.dot--white { background: #f5f5f7; }
.dot--green { background: var(--blue-light); }

/* ==========================================================================
   3 · WHY PREMIER — three exported artboards (per Figma)
   ========================================================================== */

.why {
  padding: clamp(90px, 11.1vw, 160px) 0; /* py-160 per Figma */
  background: var(--white);
}

.why__head { text-align: center; }

.why__title {
  /* Display xl/Bold — 60/72, −2% per Figma */
  font-size: clamp(2.3rem, 4.17vw, 3.75rem);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -.02em;
  color: #000;
}

.why__sub {
  margin-top: .8rem; /* was 8px — the one header running tighter than the rest */
  font-size: clamp(1.25rem, 1.67vw, 1.5rem);
  font-weight: 600;
  letter-spacing: -.01em;
  line-height: 1.333;
  color: rgba(0, 0, 0, .5);
}

/* Card geometry + caption ramp mirror the analysis gallery (`.ag`) 1:1 —
   user 2026-07-30: "misma sección See every shot". Tiles 372×450 r28 gap 20. */
.why__grid {
  margin-top: clamp(44px, 6vh, 72px);
  width: min(1156px, 94vw); /* 3×372 + 2×20, like the ag tile row */
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

/* min-width: 0 defeats the automatic minimum size of a grid/flex item. Without
   it WebKit sizes .why-card from its aspect-ratio and never shrinks it back
   when the column narrows — the card kept a stale 343px inside a 328px grid
   after a viewport change and hung 15px past the margin. Chrome reflows it, so
   this only ever showed up on Safari (i.e. on a phone being rotated). */
.why-col { display: flex; flex-direction: column; min-width: 0; }

.why-card {
  aspect-ratio: 372 / 450;
  min-width: 0;
  border-radius: 28px;
  overflow: hidden;
  background: #222;
}
.why-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.why-cap {
  /* = .ag-tile__caption */
  padding: 28px 0 0;
  font-size: 17px;
  line-height: 1.2353;
  font-weight: 600;
  letter-spacing: -.022em;
  color: var(--ink-l2);
}
.why-cap h3 { font: inherit; color: var(--ink-l1); }
.why-cap p { margin: .35em 0 0; }

/* ==========================================================================
   4 · PLAY — parallax hero (per Figma)
   ========================================================================== */

/* Everything is measured in design px of the 1440×850 Figma frame through the
   local --px unit: proportional below 1440, capped at 1:1 above it, so the
   type stays pixel-registered with the horizon at every width. */
.playhero {
  --px: min(0.069444vw, 1px); /* 1 design px */
  position: relative;
  height: calc(850 * var(--px));
  overflow: clip;
  isolation: isolate;
  background: #0a1520;
}

.playhero__layer {
  /* one 2624-wide scene render, centered; the cutout is the same render
     cropped to the top 552 with the sky alpha'd out (assets pre-cropped) */
  position: absolute;
  left: 50%;
  top: 0;
  width: calc(2624 * var(--px));
  max-width: none;
  height: auto;
  transform: translateX(-50%);
  will-change: transform;
}
.playhero__scene { z-index: 0; }
.playhero__cutout { z-index: 3; }

.playhero__fade {
  /* sits UNDER the type per Figma — it darkens the scene, not the headline */
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: calc(301 * var(--px));
  background: linear-gradient(0deg, #000, transparent);
  z-index: 1;
}

.playhero__back, .playhero__front {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.playhero__back { z-index: 2; }
.playhero__back--ghost { z-index: 4; }
.playhero__front { z-index: 5; }

.playhero__l1, .playhero__l2 {
  position: absolute;
  font-size: calc(196.579 * var(--px));
  font-weight: 700;
  line-height: .8;
  letter-spacing: -.05em;
  /* Word gap = 35.57 + this (natural space 45.4 + tracking −9.83). Figma's
     16.25 read glued on screen (the y tail eats the air), so this runs wider:
     ≈28 design px. To open it further, make the number less negative. */
  word-spacing: calc(-7.6 * var(--px));
  color: #fff;
  white-space: nowrap;
}
.playhero__l1 { left: calc(64 * var(--px)); top: calc(307.93 * var(--px)); }
.playhero__l2 { right: calc(64 * var(--px)); top: calc(436 * var(--px)); }
.playhero__back--ghost .playhero__l1 { color: transparent; }
.playhero__back--ghost b { color: #fff; font-weight: inherit; }

/* copy (per Figma 11802:93794) — Text/DemiBold 24/1.2, centered 540 box,
   bottom-anchored over the fade; floors keep it readable below ~900px */
.playhero__copy {
  position: absolute;
  left: 50%;
  bottom: max(calc(33.83 * var(--px)), 14px);
  transform: translateX(-50%);
  width: max(calc(540 * var(--px)), min(88vw, 400px)); /* floor only bites below ~1060px */
  font-size: clamp(0.95rem, calc(24 * var(--px)), 1.5rem);
  font-weight: 584;
  line-height: 1.2;
  text-align: center;
  color: #fff;
  z-index: 6;
}

/* ==========================================================================
   1b+2 · VIDEO → PRACTICE, PLAY, COMPETE (texto calado, per Figma 11843:2669)
   Coreografía 100% scroll-nativa: el pin del video y el panel calado son DOS
   stickies hermanos. El panel (slab negro con la tipografía calada vía SVG
   <mask> inline — un data-URI no puede usar la fuente del documento) entra
   1:1 con el scroll sobre el video clavado, dockea arriba y ambos quedan
   ~60vh con el video corriendo dentro de las letras. Al asomar el panel, el
   frame escala a full-bleed por detrás (solo transform, GPU) para que las
   letras siempre calen sobre video.
   ========================================================================== */

.vt {
  height: 215vh; /* 100vh pin + 100vh panel travel + 15vh hold.
                    HARD FLOOR IS 200vh: below that the panel never reaches
                    top:0, `is-docked` never fires and the white sub never
                    appears. The old 60vh hold read as the page freezing at
                    the end of the section (user 2026-08-03). */
  background: var(--black);
  position: relative;
}

/* Hero over the video (user 2026-08-03, same treatment as Foresight+): the
   standalone hero section is gone — the copy sits on the full-bleed video
   under a light black scrim, and the calado panel is the next beat. The frame
   is born full-bleed, so the card entrance / is-fill growth is neutralised. */
.vt--hero .vt__frame {
  width: 100vw;
  height: 100svh;
  aspect-ratio: auto;
  max-height: none;
  min-height: 0;
  border-radius: 0;
  transition: none;
}
.vt--hero.is-fill .vt__frame { transform: none; }

.vt__hero {
  position: absolute;
  inset: 0;
  z-index: 1; /* above the frame (z-auto), below the panel (sticky z2) */
  display: grid;
  place-items: center;
  text-align: center;
  background: rgba(0, 0, 0, .35); /* light black scrim over the footage */
  pointer-events: none;
  transition: opacity .45s var(--ease);
}
.vt__hero .container { pointer-events: auto; }
.vt__hero .cta-row { justify-content: center; }
/* gone before the panel's letter zone crosses the copy — otherwise the white
   text would read through the calado letters */
.vt.is-rising .vt__hero { opacity: 0; }

.vt__pin {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  display: grid;
  place-items: start center; /* el frame pega a la sección de arriba */
  overflow: clip;
}

.vt__frame {
  position: relative;
  width: min(1360px, 95.5vw); /* ya grande por default — no hay fase de growth */
  aspect-ratio: 1312 / 738;
  max-height: 92svh;          /* ratio+max-height: el ancho queda, cover absorbe */
  min-height: 84svh;          /* viewports angostos: sin esto el 16:9 queda corto
                                 y las letras caladas caen sobre negro */
  background: var(--card-dark);
  border-radius: 28px;
  overflow: hidden;
  transform-origin: 50% 0;
  transition: transform 1.1s var(--ease), border-radius 1.1s var(--ease);
  will-change: transform;
}
.vt.is-fill .vt__frame {
  transform: scale(var(--fs, 1.2)); /* --fs = max(vw/frameW, vh/frameH), via JS */
  border-radius: 0;
}

.vt__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  animation: vt-drift 16s ease-in-out infinite alternate; /* Ken Burns en las letras */
}
@keyframes vt-drift {
  from { transform: scale(1); }
  to   { transform: scale(1.06); }
}

/* Panel negro con la tipografía calada — sticky, sube en flujo normal */
.vt__panel {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  z-index: 2;
  /* the maskart enters at scale(1.035), so it pokes ~3.5% past both sides and
     that became document scroll width (the mobile sideways-swipe). The panel
     is exactly viewport-wide, so clipping here costs nothing visually — the
     bleed was always meant to sit off-screen. */
  overflow: hidden;
}
@supports (overflow: clip) {
  /* clip keeps the panel from becoming a scroll container */
  .vt__panel { overflow: clip; }
}

.vt__maskart {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  transform: scale(1.035);     /* micro-settle: entra 3.5% grande y asienta */
  transform-origin: 50% 41%;   /* centro óptico del bloque de texto */
  transition: transform .9s var(--ease);
}
.vt.is-docked .vt__maskart { transform: none; }

.vt__masktext {
  font-family: var(--font);
  font-weight: 900; /* Black, per Figma */
  font-size: 141.068px;
  letter-spacing: -2.8214px; /* −2% per Figma 11843:2653 */
  /* The tspan y's are fixed attributes, so the gap/size relationship (125.3 /
     141.068 = .89) only holds at THIS font size — shrinking the font alone
     stretched the lines apart. Fit narrow screens by scaling the whole block
     instead: --ms is measured in main.js so the widest line always lands
     inside the screen, and the proportions stay exactly as designed. */
  transform: scale(var(--ms, 1));
  transform-box: view-box;
  transform-origin: 720px 331px; /* centre of the three-line block */
}

/* White-10% veil between the video and the black calado (user 2026-07-31):
   lifts the footage read through the letters so it separates from the #000
   mask. Fades in with .is-rising so the takeover phase keeps the clean video. */
.vt__veil {
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, .1); /* alpha-white-10 */
  opacity: 0;
  transition: opacity .8s var(--ease);
  pointer-events: none;
  z-index: 1; /* over the video, under the media toggle (z2) */
}
.vt.is-rising .vt__veil { opacity: 1; }

/* El sub viaja EN el panel y entra CASI JUNTO al calado (user 2026-07-31):
   .is-rising se prende cuando el panel escaló la mitad del viewport, no al
   dockear — .is-docked queda para el settle del masktext. */
.vt__sub {
  position: absolute;
  left: 50%;
  top: 74.5%;
  transform: translate(-50%, 14px);
  width: min(700px, 86vw);
  text-align: center;
  font-size: clamp(1.05rem, 1.67vw, 1.5rem); /* Display xs/Semibold */
  font-weight: 584;
  line-height: 1.34;
  color: #fff;
  opacity: 0;
  transition: opacity .6s var(--ease) .18s, transform .6s var(--ease) .18s;
}
.vt.is-rising .vt__sub {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* Media play/pause toggle */
.media-toggle {
  position: absolute;
  top: 50%;
  right: 20px;
  transform: translateY(-50%);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(210, 210, 215, .64);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  display: grid;
  place-items: center;
  z-index: 2;
  transition: background .3s var(--ease), transform .3s var(--ease);
}
.media-toggle:hover { background: rgba(223, 223, 227, .72); }
.media-toggle:active { transform: translateY(-50%) scale(.94); }
.media-toggle svg { width: 36px; height: 36px; fill: #1d1d1f; }
.media-toggle .media-toggle__play { display: none; }
.media-toggle.is-paused .media-toggle__pause { display: none; }
.media-toggle.is-paused .media-toggle__play { display: block; }

/* ==========================================================================
   4b2 · FSX COURSE MARQUEE (per Figma 11755:90740)
   Each course card is Figma-s own 2x render (551×600 PNG, transparent
   corners) — nothing is rebuilt in HTML. Desktop card = 275.217px wide with a
   19.355px gap (ratio .070325), per the updated node. The belt loops forever
   in JS (rAF) and speeds up while the user scrolls; see main.js.
   ========================================================================== */

.fsx {
  --cw: clamp(216px, 66vw, 275.217px);
  --gap: calc(var(--cw) * .070325);
  padding: 96px 0 clamp(96px, 12vh, 160px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 48px;
}

/* clip solo en X: el arc wheel (main.js) baja las cards de los bordes ~60px
   y un clip vertical las cortaría recto */
.fsx__viewport { width: 100%; overflow-x: clip; overflow-y: visible; }

.fsx__track {
  display: flex;
  gap: var(--gap);
  width: max-content;
  padding-inline: var(--gap);
}

.fsx-card { flex: none; width: var(--cw); will-change: transform; }
.fsx-card img { width: 100%; height: auto; display: block; }

/* CTA — Figma FSP Button (16/10, Text md/Semibold), no trailing icon */
.fsx__cta {
  padding: 10px 16px;
  font-weight: 584;
  line-height: 1.5;
}

/* ==========================================================================
   4b3 · COURSE SHOWCASE (per Figma 11784:92145)
   Cash-App-style pinned media. The sticky stage holds one 958×539 r24 frame
   (x 634 on the 1440 canvas — it bleeds ~152px off the right edge) while the
   alternating black/white panels scroll behind it. Images stack inside the
   frame; layers 2+ are revealed with clip-path driven by their panel's top
   edge (main.js), so the section boundary doubles as the wipe line.
   ========================================================================== */

.showcase { position: relative; }

/* Full-viewport sticky stage; the panels wrapper's negative top margin lays
   the panels underneath it. The negative margin must live on the WRAPPER —
   on the sticky element itself it zeroes the sticky constraint height and
   the frame stays pinned into the next section. With it here, the stage
   unpins exactly when one viewport of section remains, so the frame rides
   out together with panel 3. */
.showcase__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  z-index: 2;
  pointer-events: none;
  /* the frame is anchored at right:-8.5vw so ~16% of the card runs off-screen
     by design — clip it here instead of leaning on body's overflow-x, which
     iOS ignores (that's what let the page swipe sideways in production) */
  overflow: hidden;
}
@supports (overflow: clip) {
  .showcase__stage { overflow: clip; }
}
.showcase__panels { margin-top: -100svh; }

.showcase__frame {
  position: absolute;
  right: -8.5vw; /* keeps the design's crop: ~16% of the card runs off-edge */
  top: 50%;
  translate: 0 -50%;
  width: min(53.2vw, 108svh); /* design size −20%; height ≤ 61svh via ratio */
  aspect-ratio: 958 / 539;
  border-radius: 24px;
  overflow: hidden;
  background: var(--elev);
}
/* Split layers: the wrapper carries the wipe clip (scrubbed in main.js), the
   par shell carries the scroll parallax, and the img carries the Ken Burns
   drift. All three must stay separate — a transform on the clipped element
   drags the wipe line, and the drift would fight the parallax offset. */
.showcase__layer {
  position: absolute;
  inset: 0;
}
/* Overhangs the frame by --par-over top and bottom, so the image has room to
   drift against the scroll without ever exposing an edge — an oversized
   plate inside a clipped window. */
.showcase__par {
  position: absolute;
  inset: calc(var(--par-over) * -1) 0;
  transform: translate3d(0, var(--par, 0px), 0);
  will-change: transform;
}
.showcase__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  will-change: transform;
  animation: showcase-drift 16s ease-in-out infinite alternate;
}
/* out of phase per layer so the panels never breathe in sync */
.showcase__layer:nth-child(2) .showcase__img { animation-delay: -6s; animation-direction: alternate-reverse; }
.showcase__layer:nth-child(3) .showcase__img { animation-delay: -11s; }

/* slow zoom + diagonal drift; starts pre-scaled so edges never show */
@keyframes showcase-drift {
  from { transform: scale(1.06) translate3d(1.6%, -1%, 0); }
  to   { transform: scale(1.15) translate3d(-1.8%, 1.3%, 0); }
}

/* layers 2+ start hidden; main.js scrubs the inset down to 0 as their panel
   edge sweeps up across the frame */
.showcase__layer + .showcase__layer { clip-path: inset(100% 0 0 0); }

.showcase__panel {
  min-height: 100svh;
  display: flex;
  align-items: center;
  background: var(--black);
  color: var(--ink-d1);
}
.showcase__panel--light {
  background: var(--white);
  color: #06090f;
}

.showcase__inner { width: min(1320px, 94vw); margin-inline: auto; }

/* per-panel stills: mobile-only replacement for the pinned frame */
.showcase__still { display: none; }

.showcase__copy {
  width: min(428px, 38vw); /* stays clear of the frame at every desktop width */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
}

/* pill tag — Text sm/Medium under the library's soft top-light */
.showcase__tag {
  padding: 9px 14px;
  border-radius: 28px;
  font-size: .875rem;
  font-weight: 492;
  line-height: 1.43;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: rgba(250, 250, 250, .6);
  background:
    radial-gradient(56% 90% at 50% 0%, rgba(185, 192, 204, .22), rgba(138, 148, 165, .04) 40%, rgba(138, 148, 165, 0) 70%),
    rgba(0, 0, 0, .2);
  border: 1px solid rgba(232, 236, 243, .08);
}
.showcase__panel--light .showcase__tag {
  color: rgba(6, 9, 15, .6);
  background: radial-gradient(56% 90% at 50% 0%, rgba(185, 192, 204, .3), rgba(138, 148, 165, .06) 40%, rgba(138, 148, 165, 0) 70%);
  border-color: rgba(6, 9, 15, .08);
}

.showcase__title {
  /* Display lg/Bold — 48/50, −2% */
  font-size: clamp(2.1rem, 3.34vw, 3rem);
  font-weight: 700;
  line-height: 1.042;
  letter-spacing: -.02em;
}
.showcase__text {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 7px; /* 24 + 7 = Figma's 31 title→text */
}
.showcase__sub {
  font-size: 1.25rem; /* Text xl/Semibold — 20/30 */
  font-weight: 584;
  line-height: 1.5;
}
.showcase__body {
  font-size: 1.125rem; /* Text lg/Medium — 18/28 */
  font-weight: 492;
  line-height: 1.556;
  color: rgba(250, 250, 250, .6);
}
.showcase__panel--light .showcase__body { color: rgba(6, 9, 15, .6); }

@media (max-width: 820px) {
  /* The pinned stage + boundary wipe is a desktop pattern. On mobile each
     panel carries its own still IN FLOW, sitting right above its tag — fixed
     to the section, not the device (user call, 2026-07-30). */
  .showcase__stage { display: none; }
  .showcase__panels { margin-top: 0; } /* no stage to overlap anymore */
  /* The still deliberately bleeds 7vw past the right edge (3vw offset +
     104vw). Nothing was cutting it, so it became the document's scroll width
     and the page swiped sideways on mobile — `body { overflow-x: clip }` masks
     this in Chrome but iOS ignores it. The panel is exactly viewport-wide, so
     clipping HERE kills the overflow at its source and keeps the crop. */
  .showcase__panel { overflow-x: hidden; }
  @supports (overflow: clip) {
    /* clip doesn't create a scroll container the way hidden does */
    .showcase__panel { overflow-x: clip; }
  }
  .showcase__still {
    display: block;
    /* left edge on the text margin, right side crops off-screen */
    width: 104vw;
    max-width: none; /* the global img reset caps width at 100% */
    height: auto;    /* the height attr would otherwise pin 1078px */
    aspect-ratio: 958 / 539;
    object-fit: cover;
    border-radius: 24px;
    margin-bottom: 28px;
  }
  .showcase__copy { width: 100%; max-width: 520px; }
}

/* --- 4K COURSES — SHADER SHOWCASE (user 2026-08-03 v3). One block on the
   section black (same as the modules around it): a single rounded frame whose
   imagery lives on a WebGL canvas — switching tabs runs a noise-melt shader
   transition (main.js) instead of moving cards. The <img> fallback only shows
   when WebGL is unavailable (.ctabs--nogl). Replaces the pinned scroll story
   above in the markup (those rules stay for reference, same as the legacy
   bento). --- */

.ctabs {
  padding: clamp(40px, 7vh, 90px) 0;
  background: var(--black);
}
.ctabs__frame {
  position: relative;
  width: min(1680px, 94vw);
  margin-inline: auto;
  height: clamp(480px, 82svh, 880px);
  border-radius: 28px;
  overflow: hidden;
  background: var(--card-dark);
}
.ctabs__gl,
.ctabs__fallback {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}
.ctabs__fallback { display: none; }
.ctabs--nogl .ctabs__gl { display: none; }
.ctabs--nogl .ctabs__fallback { display: block; transition: opacity .3s var(--ease); }
.ctabs__scrim {
  position: absolute;
  inset: 0;
  /* bottom 0 -> 55%: the stacked copy needs the darken at every width
     (user 2026-08-18) */
  background:
    linear-gradient(180deg, rgba(0, 0, 0, .26), rgba(0, 0, 0, 0) 34%),
    linear-gradient(to top, rgba(0, 0, 0, .55), rgba(0, 0, 0, 0) 55%);
}
.ctabs__mark {
  position: absolute;
  top: clamp(24px, 4.5svh, 52px);
  left: clamp(24px, 3.4vw, 64px);
  max-width: 7ch; /* wraps to "4K / Remastered" like the comp */
  font-size: clamp(1.9rem, 2.9vw, 3rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -.02em;
  color: #fff;
}
.ctabs__tabs {
  position: absolute;
  top: clamp(20px, 4svh, 44px);
  right: clamp(20px, 2vw, 40px);
  z-index: 2;
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .3);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
}
.ctabs__tab {
  appearance: none;
  border: 0;
  background: transparent;
  color: rgba(22, 24, 28, .62);
  font-family: var(--font);
  font-size: 15px;
  font-weight: 584;
  line-height: 1;
  letter-spacing: -.01em;
  padding: 12px 24px;
  border-radius: 999px;
  cursor: pointer;
  transition: background .3s var(--ease), color .3s var(--ease);
}
.ctabs__tab:hover:not(.is-active) { color: rgba(22, 24, 28, .92); }
.ctabs__tab.is-active { background: #fff; color: #000; }
.ctabs__copy {
  position: absolute;
  left: clamp(24px, 3.4vw, 64px);
  bottom: clamp(24px, 5svh, 56px);
  max-width: 620px;
  padding-left: clamp(18px, 1.8vw, 34px);
  border-left: 3px solid rgba(255, 255, 255, .92);
  /* stacked panels: outgoing copy sinks + fades, incoming rises in after the
     shader melt is mostly through (the .45s delay) */
  opacity: 0;
  transform: translateY(14px);
  visibility: hidden;
  pointer-events: none;
  transition: opacity .4s var(--ease), transform .4s var(--ease), visibility 0s linear .4s;
}
.ctabs__copy.is-active {
  opacity: 1;
  transform: none;
  visibility: visible;
  pointer-events: auto;
  transition: opacity .5s var(--ease) .45s, transform .5s var(--ease) .45s;
}
.ctabs__title {
  font-size: clamp(1.7rem, 2.5vw, 2.5rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -.02em;
  color: #fff;
}
.ctabs__body {
  margin-top: 10px;
  max-width: 480px;
  font-size: 1.0625rem; /* Text lg/Medium step down */
  font-weight: 492;
  line-height: 1.55;
  color: rgba(255, 255, 255, .8);
}

@media (max-width: 820px) {
  .ctabs__frame { width: 90vw; height: min(72svh, 620px); }
  .ctabs__mark { font-size: 1.5rem; }
  /* no room next to the mark on phones — the control drops to the bottom of
     the frame (it clips overflow, so it can't hang below) and the copy lifts
     out of its way */
  .ctabs__tabs { top: auto; right: auto; left: 50%; bottom: 18px; transform: translateX(-50%); }
  .ctabs__tab { padding: 10px 16px; font-size: 13.5px; }
  .ctabs__copy { bottom: 84px; }
  .ctabs__body { font-size: .9375rem; }
}

/* ==========================================================================
   CAROUSEL MACHINERY (partly live since 2026-07-30)
   `.paddle` is now used by the analysis gallery (`.ag`), whose stepping
   lives in main.js. The `.gallery` / `.gallery-wrap` scroll-snap row below
   is still dormant (the old 4c course slider); reviving it needs its own
   wiring — main.js now targets `.ag__scroller`, not `.gallery`.
   ========================================================================== */

.gallery-wrap { margin-top: clamp(48px, 7vh, 90px); padding-bottom: clamp(90px, 12vh, 150px); }

.gallery {
  display: flex;
  gap: clamp(20px, 2.8vw, 40px);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: max(4vw, calc((100vw - 1320px) / 2));
  scrollbar-width: none;
  padding: 6px max(4vw, calc((100vw - 1320px) / 2)) 26px;
}
.gallery::-webkit-scrollbar { display: none; }

.gallery-paddles {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
}
.paddle {
  /* media-control style */
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(210, 210, 215, .64);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: #1d1d1f;
  display: grid;
  place-items: center;
  transition: background .3s, opacity .3s, transform .3s var(--ease);
}
.paddle svg { width: 36px; height: 36px; transition: transform .35s var(--ease); }
.paddle:hover:not(:disabled) { background: rgba(223, 223, 227, .72); }
/* the chevron peeks 2px in its travel direction */
.paddle:hover:not(:disabled) svg { transform: translateX(2px); }
.paddle[data-dir="-1"]:hover:not(:disabled) svg { transform: translateX(-2px); }
.paddle:active:not(:disabled) { transform: scale(.94); }
.paddle:disabled { opacity: .36; cursor: default; }

/* ==========================================================================
   5 · REALISM SPLIT
   ========================================================================== */

/* Full-bleed video background with the copy over it (per Figma 11753:6362 —
   video plus a flat rgba(0,0,0,.2) scrim). The scrubbed split graphic is gone,
   so the section no longer needs the sticky pin. */
.realism {
  position: relative;
  isolation: isolate;
  overflow: clip;
  display: flex;
  align-items: center;
  min-height: min(720px, 86vh);
  padding: clamp(64px, 10vh, 120px) 0;
}

/* Expanding mask, shared by every [data-clip] plate (the realism video and the
   venues backdrop): one progress value drives BOTH the side inset and the
   corner radius, so the window widens and squares off in a single move.
   --clip runs 1 → 0 (windowed → full-bleed), scrubbed in main.js. The inset
   is horizontal only — the band's own height frames it. */
[data-clip] {
  --clip: 1;
  clip-path: inset(0 calc(var(--clip) * 6.25vw) round calc(var(--clip) * 28px));
  will-change: clip-path;
}
.realism__media {
  position: absolute;
  inset: 0;
  z-index: -1;
}

.realism__video {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.realism__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  /* Figma specifies a flat rgba(0,0,0,.2), but its mock used a much darker frame
     than this footage: over the bright grass the gray lede fell to 4.4:1. Raised
     to .35 and the lede is white here, which puts both well past AA. */
  background: rgba(0, 0, 0, .35);
}

.realism__head { text-align: center; }
.realism__head .lede {
  max-width: 48ch;
  margin-inline: auto;
  font-weight: 600;
  color: rgba(255, 255, 255, .85);
}

/* ==========================================================================
   6 · ANALYSIS BENTO
   ========================================================================== */

.analysis { padding: clamp(96px, 14vh, 170px) 0; }
/* Left-aligned head with the paddles on the right (user 2026-08-03; was a
   centered stack with the paddles under the row) */
.analysis__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  text-align: left;
}
/* .eyebrow is a centering flex row globally — pin it left here */
.analysis__head .eyebrow { justify-content: flex-start; }

/* --- Analysis gallery — caption-tile carousel.
   Card 372×450 (344×416 ≤1068), radius 28, gap 20,
   caption 17/600 below the tile, full-bleed snap row starting at the
   content inset. ≤734 stacks the tiles (user request). Gray card blocks
   are placeholders until the real imagery lands. --- */

.ag {
  /* row starts exactly on the header's text edge: same math as .container
     (min(1080px, 92vw) centered), user 2026-08-03 */
  --ag-inset: max(4vw, calc((100vw - 1080px) / 2));
  --ag-card-w: 372px;
  --ag-card-h: 450px;
  --ag-gap: 20px;
  margin-top: clamp(44px, 6vh, 72px);
}
.ag__scroller {
  overflow-x: scroll;
  scrollbar-width: none;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--ag-inset);
  /* breathing room so nothing clips, mirrored away */
  padding: 10px 0 14px;
  margin: -10px 0 -14px;
}
.ag__scroller::-webkit-scrollbar { display: none; }
/* mouse-drag support (main.js adds the class when it wires the handlers) */
/* mandatory snap re-snaps on every frame of a drag (the stutter) — off while
   the pointer is down; main.js settles on a card before restoring it */
.ag__scroller.is-dragging { scroll-snap-type: none; }
.ag__scroller--draggable { cursor: grab; }
.ag__scroller--draggable:active { cursor: grabbing; }
.ag__scroller--draggable .ag-tile { user-select: none; }
.ag__scroller--draggable .ag-tile img { -webkit-user-drag: none; }

.ag__set {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: var(--ag-card-w);
  gap: 0 var(--ag-gap);
  width: fit-content;
  padding-inline: var(--ag-inset);
  margin: 0;
}

.ag-tile { display: flex; flex-direction: column; scroll-snap-align: start; }
.ag-tile:last-child { scroll-snap-align: end; }

/* the media card — Figma 2x exports; gray shows only while loading */
.ag-tile__card {
  order: 1;
  height: var(--ag-card-h);
  border-radius: 28px;
  background: #e3e3e8;
  overflow: hidden;
}
.ag-tile__card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}

/* caption under the card: title on top, copy below, flush with the card
   edge (user 2026-07-30 — no side inset) */
.ag-tile__caption {
  order: 2;
  padding: 28px 0 0;
  font-size: 17px;
  line-height: 1.2353;
  font-weight: 600;
  letter-spacing: -.022em;
  color: var(--ink-l2);
}
.ag-tile__caption h3 { display: block; font: inherit; color: var(--ink-l1); }
.ag-tile__caption p  { display: block; margin: .35em 0 0; }

.ag__paddles {
  display: flex;
  gap: 12px;
  flex-shrink: 0;
  margin-bottom: 6px; /* optical alignment with the lede baseline */
}

@media (max-width: 1068px) {
  .ag { --ag-card-w: 344px; --ag-card-h: 416px; }
}
@media (max-width: 734px) {
  /* mobile: stacked, one tile under the other, full image (no crop);
     4vw padding = the .container gutter, so tiles align with the header */
  .ag__scroller { overflow-x: visible; padding: 0 4vw; margin: 0; }
  .ag__set {
    grid-auto-flow: row;
    grid-auto-columns: unset;
    gap: 40px 0;
    width: auto;
  }
  .ag-tile__card { width: 100%; height: auto; }
  .ag-tile__card img { height: auto; }
  /* Text md/Semibold (16/24) — the caption reads too small at 14 on a phone */
  .ag-tile__caption { padding-top: 24px; font-size: 16px; line-height: 1.5; letter-spacing: -.016em; }
  .ag__paddles { display: none; }
}

/* Desktop scroll-driven mode: main.js adds .analysis--pin (≥735, motion OK),
   sizes --ag-extra to the row's overflow, and scrubs scrollLeft against the
   pin. Without JS or with reduced motion the class never lands and the
   carousel stays free-scrolling. */
.analysis--pin {
  /* the user's "3×" air lives OUTSIDE the pinned frame, as white entry/exit
     stretches around it — cramming it INSIDE the 100svh pin shrank the tiles
     until the whole row fit and the carousel had nothing to scroll */
  --ag-air-t: clamp(120px, 15svh, 210px);
  --ag-air-b: clamp(96px, 12svh, 170px);
  padding: 0;
  height: calc(100svh + var(--ag-extra, 820px) + var(--ag-air-t) + var(--ag-air-b));
  /* BFC: without this the pin's --ag-air margins collapse through the
     section edge and render as a BLACK band over <main> between the realism
     video and this white section (the "video cortado" bug) */
  display: flow-root;
}
.analysis--pin .analysis__pin {
  position: sticky;
  top: 0;
  height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* compact breathing room inside the pinned frame */
  padding: max(5svh, 40px) 0 max(4svh, 32px);
  margin-top: var(--ag-air-t);
  margin-bottom: var(--ag-air-b);
}
.analysis--pin .ag__scroller { scroll-snap-type: none; }

/* pinned mode fits ONE viewport: the tile scales with svh (same 372:450
   ratio as the 744×900 exports, so cover never crops) and the vertical
   chrome compresses on short screens */
.analysis--pin .ag {
  --ag-card-h: min(450px, 54svh);
  --ag-card-w: calc(min(450px, 54svh) * .82667);
  margin-top: clamp(16px, 3.5svh, 56px);
}
/* pinned: the paddles stay visible in the head (user 2026-08-03, reversing
   2026-07-30) — clicking one scrolls the PAGE a card's worth, which the scrub
   translates into row travel, so arrows and scroll stay in lockstep */
.analysis--pin .ag-tile__caption {
  padding-top: clamp(14px, 2.6svh, 28px);
  /* type follows the tile so long captions stop forcing tiny cards:
     17px at the full 372 width, floor 13.5px */
  font-size: clamp(13.5px, calc(var(--ag-card-w) / 22), 17px);
}
/* (short-viewport residue is trimmed by the JS fit pass in sizePin —
   it measures real overflow instead of guessing with svh tiers) */

.analysis .footnote { margin-top: clamp(44px, 6vh, 64px); margin-inline: auto; text-align: center; }

/* --- LEGACY BENTO (markup now lives only in analysis-lab.html as reference;
   these rules stay so the lab keeps rendering the old SVG mocks) --- */

.bento {
  margin-top: clamp(40px, 6vh, 70px);
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 18px;
}

.bento-card {
  background: var(--white);
  border-radius: var(--r-card);
  padding: clamp(24px, 2.6vw, 34px);
  display: flex;
  flex-direction: column;
  gap: 22px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, .05);
}
.bento-card--7 { grid-column: span 6; }
.bento-card--5 { grid-column: span 6; }
.bento-card--4 { grid-column: span 4; }

.bento-card h3 {
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: -.017em;
  margin-bottom: .45rem;
}
.bento-card header p { color: var(--ink-l2); font-size: .99rem; max-width: 46ch; }

.panel {
  background: #0d0d10;
  border-radius: var(--r-panel);
  padding: 18px;
  margin-top: auto;
  color: var(--ink-d1);
}
.panel--center { display: flex; flex-direction: column; align-items: center; gap: 10px; }

/* Data tiles */
.tiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
}
.tile {
  background: #17171b;
  border-radius: 12px;
  padding: 12px 12px 10px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.tile span {
  font-size: .56rem;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--ink-d3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tile b {
  font-family: var(--mono);
  font-size: clamp(.95rem, 1.6vw, 1.3rem);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.tile i { font-style: normal; font-size: .6rem; color: var(--ink-d2); text-transform: uppercase; letter-spacing: .08em; }

/* Dispersion + face */
.disp, .face { width: 100%; height: auto; }
.legend {
  display: flex;
  gap: 18px;
  font-family: var(--mono);
  font-size: .66rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-d2);
}
.legend span { display: inline-flex; align-items: center; gap: 7px; }
.legend--chips { flex-wrap: wrap; gap: 8px; }
.chip-sm {
  padding: 5px 10px;
  border-radius: 8px;
  background: #1b1b20;
  border: 1px solid rgba(255, 255, 255, .1);
  font-size: .62rem;
  white-space: nowrap;
}

/* Tags */
.tagcloud { display: flex; flex-wrap: wrap; gap: 9px; margin-top: auto; }
.tag {
  padding: 8px 15px;
  border-radius: 980px;
  border: 1px solid var(--line-l);
  font-size: .86rem;
  font-weight: 600;
  color: var(--ink-l2);
  background: var(--white);
}
.tag--on {
  background: #041030;
  border-color: #041030;
  color: #7fb2ff;
}
.tag--ghost { border-style: dashed; color: rgba(29, 29, 31, .45); }

/* MyBag */
.bag { display: flex; flex-direction: column; gap: 9px; }
.bag__row {
  display: grid;
  grid-template-columns: 30px 1fr 40px;
  align-items: center;
  gap: 12px;
  font-family: var(--mono);
  font-size: .78rem;
}
.bag__row span { color: var(--ink-d3); text-transform: uppercase; }
.bag__row b { font-variant-numeric: tabular-nums; text-align: right; font-weight: 600; }
.bag__bar { height: 7px; border-radius: 4px; background: #202026; overflow: hidden; }
.bag__bar i {
  display: block;
  height: 100%;
  width: var(--w);
  border-radius: 4px;
  background: linear-gradient(90deg, #1e6dff, var(--blue-light));
}
.bag__gap {
  justify-self: start;
  margin-left: 42px;
  padding: 3px 9px;
  border-radius: 6px;
  border: 1px dashed rgba(255, 214, 10, .5);
  color: #ffd60a;
  font-family: var(--mono);
  font-size: .6rem;
  letter-spacing: .09em;
  text-transform: uppercase;
}

.analysis .footnote { margin-top: 26px; text-align: center; }

/* ==========================================================================
   8 · FORESIGHT+
   ========================================================================== */

/* bottom −32.5% total (user 2026-07-31: −25%, then −10% more): the deck→Tiers
   gap is this padding + the stack's own padding-bottom + .tiers' padding-top,
   so all three are cut by the same factor to shrink the total evenly at every
   viewport instead of collapsing at one breakpoint */
.plus { padding: clamp(96px, 14vh, 170px) 0 clamp(65px, 9.45vh, 115px); }
.plus__head { text-align: center; }
.plus__head .lede { margin-inline: auto; }

/* Stacked cards: Mobile slides up over Premier on scroll (sticky deck).
   Geometry per Figma 11712:27951 — 1204×520 cards, radius 26. */
.stack {
  width: min(1204px, 94vw);
  display: flex;
  flex-direction: column;
  gap: 40px;
  margin-top: clamp(80px, 12vh, 140px);
  padding-bottom: 10.8vh; /* was 16vh — same −32.5% pass as .plus/.tiers */
}

.stack-card {
  position: sticky;
  overflow: hidden;
  border-radius: 26px;
  background: #080507;
  height: clamp(420px, 36.12vw, 520px);
  isolation: isolate;
  will-change: transform;
}
.stack-card--sim {
  top: calc(var(--nav-h) + 24px);
  transform-origin: top center;
  background: #151515;
}
.stack-card--sim::before {
  width: 8.5%;
  background: linear-gradient(90deg, rgba(21, 21, 21, 0), #151515);
}
.stack-card--sim::after {
  height: 26%;
  background: linear-gradient(180deg, rgba(21, 21, 21, 0), #151515);
}
.stack-card--mobile { top: calc(var(--nav-h) + 69px); }

/* oversized plate so the photo can drift with the scroll (see --par-over) */
.stack-card__par {
  position: absolute;
  inset: calc(var(--par-over) * -1) 0;
  z-index: 0;
  transform: translate3d(0, var(--par, 0px), 0);
  will-change: transform;
}
.stack-card__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

.stack-card__art {
  position: absolute;
  left: 46.7%;
  top: -43%;
  width: 63%;
  /* parallax rides in front of the pose — the rotation must stay last */
  transform: translate3d(0, var(--par, 0px), 0) rotate(-24.47deg);
  z-index: 0;
  will-change: transform;
}
.stack-card__art img { width: 100%; height: auto; }

/* right + bottom fades per Figma */
.stack-card::before,
.stack-card::after {
  content: "";
  position: absolute;
  z-index: 1;
  pointer-events: none;
}
.stack-card::before {
  /* right fade hangs partly off-card per Figma (visible ≈ 5.6%) */
  top: 0; bottom: 0;
  right: -2.9%;
  width: 8.5%;
  background: linear-gradient(90deg, rgba(0, 0, 0, 0), #000);
}
.stack-card::after {
  /* bottom fade: right-anchored under the art, darkest point clipped below
     the card edge, left flank feathered so no seam shows */
  right: -3%; bottom: -8%;
  width: 48%;
  height: 34%;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0), #000);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 38%);
  mask-image: linear-gradient(90deg, transparent, #000 38%);
}

.stack-card__copy {
  position: absolute;
  z-index: 2;
  top: clamp(24px, 3vw, 43px);
  left: clamp(22px, 2.8vw, 40px);
  max-width: min(525px, 86%);
}
.stack-card__tag {
  /* Mono md/Regular — 16/24 */
  font-family: var(--mono);
  font-size: clamp(.85rem, 1.11vw, 1rem);
  line-height: 1.5;
  text-transform: uppercase;
  color: #fff;
}
.stack-card__copy h3 {
  /* Display lg/Semibold — 48/60, −2% */
  margin-top: 2px;
  font-size: clamp(1.8rem, 3.33vw, 3rem);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -.02em;
  color: #fff;
}
.stack-card__copy p:last-child {
  /* Text lg/Medium — 18/28, 60% white */
  margin-top: 16px;
  font-size: clamp(1rem, 1.25vw, 1.125rem);
  font-weight: 500;
  line-height: 1.556;
  letter-spacing: 0;
  color: rgba(250, 250, 250, .6); /* Alpha/Text/Tertiary */
  max-width: min(393px, 90%);
}

/* ==========================================================================
   9 · TIERS
   ========================================================================== */

.tiers { padding: clamp(65px, 9.45vh, 115px) 0 clamp(96px, 14vh, 170px); background: var(--black); } /* top −32.5%, see .plus */
.tiers > .container:first-of-type { text-align: center; }
.tiers > .container:first-of-type .lede { margin-inline: auto; }

.tiers__grid {
  margin-top: clamp(40px, 6vh, 64px);
  width: min(1204px, 94vw); /* 3×388 + 2×20 per Figma */
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  align-items: stretch;
}

/* Glass tier cards (per Figma): radial glow from top center + tinted border.
   The glow lives on ::before so it can breathe (slow pulse + drift). */
.tier-card {
  position: relative;
  overflow: hidden;
  border-radius: 28px;
  padding: clamp(28px, 2.8vw, 40px);
  display: flex;
  flex-direction: column;
  gap: 14px;
  color: var(--ink-d1);
  border: none; /* the line is the gradient ring layer per Figma */
  background: rgba(0, 0, 0, .2);
}
.tier-card--gold { gap: 16px; }
/* the CTA sits on the card foot instead of trailing its own list, so the three
   buttons line up across the row — Gold has the longest list and already sat
   there, so it sets the line. Same rule foresight-plus/plus.css already had;
   this landing was the only one missing it (user 2026-08-18). */
.tier-card__cta { margin-top: auto; }
.tier-card > * { position: relative; z-index: 2; }
.tier-card {
  transition: opacity .9s var(--ease) var(--d, 0s), transform .5s var(--ease);
}
.tier-card:hover { transform: scale(1.02); }
.tier-card::before {
  content: "";
  position: absolute;
  inset: -12% -10%;
  z-index: 1;
  background: radial-gradient(70% 42% at 50% 6%, rgba(76, 76, 76, .24), rgba(76, 76, 76, .05) 42%, transparent 70%);
  animation: glow-breathe 7s ease-in-out infinite alternate;
  pointer-events: none;
}

/* rotating border line (Silver/Gold): luminous arc merging into black; Free stays static */
.tier-card .tier-card__ring {
  position: absolute;
  inset: 0; /* must stay inside the padding box — .tier-card's overflow:hidden clips there */
  border-radius: 27px;
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
  /* Figma stroke: diagonal #E8ECF3→#B9C0CC→#8A94A5 at 10% */
  background: linear-gradient(108deg,
    rgba(232, 236, 243, .1), rgba(185, 192, 204, .1) 55%, rgba(138, 148, 165, .1));
}
.tier-card--silver .tier-card__ring {
  /* same ramp, born transparent, at 20% */
  background: linear-gradient(108deg,
    rgba(232, 236, 243, 0), rgba(185, 192, 204, .2) 55%, rgba(138, 148, 165, .2));
}
.tier-card--gold .tier-card__ring {
  /* Figma stroke: transparent → #F5C260 45% → #C88A1F, full opacity */
  background: linear-gradient(108deg,
    rgba(255, 233, 168, 0), #f5c260 45%, #c88a1f);
}
.tier-card--free .tier-card__ring i { display: none; }
.tier-card__ring i {
  /* Silver & Gold: identical geometry — same from-angle, same stops (0/55/110°),
     same duration, zero delay. Only the color differs. */
  position: absolute;
  inset: -60%;
  background: conic-gradient(from 0deg,
    transparent 0deg, rgba(201, 210, 223, .85) 55deg, transparent 110deg);
  animation: line-rotate 6s linear infinite;
}
.tier-card--gold .tier-card__ring i {
  background: conic-gradient(from 0deg,
    transparent 0deg, rgba(255, 223, 154, .95) 55deg, transparent 110deg);
}
.tier-card__ring b {
  position: absolute;
  inset: 1px;
  border-radius: 26px;
  background: #000; /* matches the page black so Silver/Gold read like Free's fill */
}
@keyframes line-rotate { to { transform: rotate(360deg); } }
.tier-card--silver::before {
  background: radial-gradient(70% 42% at 50% 6%, rgba(185, 192, 204, .24), rgba(138, 148, 165, .05) 42%, transparent 70%);
}
.tier-card--gold::before {
  background: radial-gradient(70% 42% at 50% 6%, rgba(245, 194, 96, .3), rgba(200, 138, 31, .09) 42%, transparent 70%);
}

@keyframes glow-breathe {
  from { opacity: .6; transform: translateY(0) scale(1); }
  to   { opacity: 1;  transform: translateY(3.5%) scale(1.07); }
}

.tier-card__head {
  display: flex;
  align-items: center;
  gap: 20px;
  padding-bottom: 14px;
  border-bottom: 1px solid rgba(255, 255, 255, .1);
  min-height: 86px; /* 72 head + 14 divider gap per Figma */
}
/* Figma clips the 72px medal into a 59.143px badge box, trimming its transparent
   margin. Negative insets give the same layout width without cropping the art. */
.tier-card__medal { width: 72px; height: 72px; flex: none; margin-inline: -6.43px; }
/* Video medals (Silver/Gold): the mp4 is cropped to the medal's edge — no
   transparent margin like the PNGs — so it renders at 69px (tiers-lab tuning)
   while keeping the PNG's 59.14px layout footprint. mix-blend-mode: screen
   drops the video's black background over the dark card. */
.tier-card__medal--video {
  width: 69px;
  height: 69px;
  margin-inline: -4.93px; /* (59.14 − 69) / 2 */
  object-fit: cover;
  mix-blend-mode: screen;
  pointer-events: none;
}
.tier-card__medal--silver { filter: grayscale(1) brightness(.91) contrast(1.01); }

.tier-card__name {
  font-size: 1.125rem; /* Text lg/Semibold — 18/28, wght 584 per the library style */
  font-weight: 584;
  line-height: 1.556;
  color: rgba(250, 250, 250, .6); /* Alpha/Text/Tertiary */
}
.tier-card h3 {
  /* Display sm/Bold — 30/38 */
  font-size: clamp(1.5rem, 2.1vw, 1.875rem);
  font-weight: 700;
  letter-spacing: 0;
  line-height: 1.267;
  color: #f5f5f6;
}
.tier-card__kicker {
  /* Text lg/Semibold — 18/28, wght 584 */
  font-size: 1.125rem;
  font-weight: 584;
  line-height: 1.556;
  color: rgba(250, 250, 250, .8); /* Alpha/Text/Secondary */
}

.tier-card ul {
  /* Text md/Regular — 16/24, rows gap 8, kicker→list 20 per Figma */
  margin-top: 6px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: clamp(.95rem, 1.15vw, 1rem);
  color: rgba(250, 250, 250, .8); /* Alpha/Text/Secondary */
}
.tier-card li {
  position: relative;
  padding-left: 30.755px; /* 18.755 check + 12 gap per Figma */
  line-height: 1.5;
}
.tier-card li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 2px;
  width: 18.755px;
  height: 18.755px;
  border-radius: 50%;
  background: #8a919c;
  -webkit-mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10 1a9 9 0 100 18 9 9 0 000-18zm4.2 6.4l-4.9 5.4a.9.9 0 01-1.32.03L5.7 10.5a.9.9 0 111.27-1.27l1.6 1.6 4.28-4.7a.9.9 0 111.34 1.2z"/></svg>') center / contain no-repeat;
  mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10 1a9 9 0 100 18 9 9 0 000-18zm4.2 6.4l-4.9 5.4a.9.9 0 01-1.32.03L5.7 10.5a.9.9 0 111.27-1.27l1.6 1.6 4.28-4.7a.9.9 0 111.34 1.2z"/></svg>') center / contain no-repeat;
}
.tier-card--silver li::before { background: #c3cbd6; }
.tier-card--gold li::before { background: #e5b34c; }

/* the Compare Plans trigger rides the footer's own 14px flex gap */
.tiers__foot .link-arrow--lg {
  margin-top: 0;
  margin-bottom: 0;
}

.tiers__foot {
  margin-top: clamp(40px, 4.6vh, 56px);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 14px;
}
.tiers__foot .footnote { max-width: 58ch; }

/* ==========================================================================
   10 · COURSE LIBRARY
   ========================================================================== */

.library { padding: clamp(96px, 14vh, 170px) 0; }
.library > .container:first-of-type { text-align: center; }
.library > .container:first-of-type .lede { margin-inline: auto; }

/* Course fan (per Figma 11720:28361) — Figma's own isolated card renders
   (get_screenshot contentsOnly:true; flat exports bake the #F5F5F5 canvas),
   layered so the fan can open slightly. Shown at 70% of the design's 402×268.
   Offsets are the design's geometry in % of the 402×268 box; the centre one
   was cross-checked against the flat render by pixel correlation (<1px off).
   The downward dissolve is the flat render's own alpha ramp, measured:
   opaque to 64/268, gone by 175/268. */
.fan {
  --w: min(281px, 62vw); /* 402 × 0.7 */
  position: relative;
  width: var(--w);
  aspect-ratio: 402 / 268;
  /* the dissolve leaves the box's bottom empty — close the gap */
  margin: 0 auto calc(var(--w) * -.14);
}
/* Downward dissolve as a black overlay instead of a mask: any mask-image
   clips every overflowing card to the border box, which cut the fan's
   open/hover swing at the box edges. Painting black over the black section
   is visually identical and clips nothing. Stops are the flat render's
   measured ramp in box space (opaque to 64/268, gone by 175/268); the
   overlay reaches past the sides to cover the swung-out cards' bottoms. */
.fan::after {
  content: "";
  position: absolute;
  inset: 0 -20%;
  z-index: 1;
  background: linear-gradient(180deg, transparent 23.9%, #000 65.4%);
  pointer-events: none;
}
/* the heading overlaps the fan's dissolved bottom (negative margin), so it
   must paint above the ::after black — the mask never painted, this does */
.library .h2,
.library .lede { position: relative; z-index: 2; }

/* The fan opens as it scrolls through the viewport, then opens a touch further
   on hover. Both feed ONE transform per card, composed from two inherited
   numbers so they never fight each other:
     --o  scroll openness 0→1 (scrubbed in main.js off viewport progress)
     --h  hover boost 0→1 (CSS :hover)
   At --o:0 the cards sit closed (the old pre-reveal pose); at 1 they rest in
   the Figma pose; --h adds the extra spread on top. */
.fan { --o: 0; --h: 0; }
.fan:hover { --h: 1; }

.fan__card {
  position: absolute;
  height: auto;
  /* short, so the scroll scrub still tracks the wheel but hover eases */
  transition: transform .3s var(--ease);
  will-change: transform;
}
.fan__card--l {
  left: -.97%; top: 1.53%; width: 55.72%; transform-origin: 75% 90%;
  transform: rotate(calc(3.5deg * (1 - var(--o)) + -1.5deg * var(--h)))
             translate(calc(3.5% * (1 - var(--o)) + -1.8% * var(--h)),
                       calc(1%   * (1 - var(--o)) + -0.7% * var(--h)));
}
.fan__card--r {
  left: 45.25%; top: 1.53%; width: 55.72%; transform-origin: 25% 90%;
  transform: rotate(calc(-3.5deg * (1 - var(--o)) + 1.5deg * var(--h)))
             translate(calc(-3.5% * (1 - var(--o)) + 1.8% * var(--h)),
                       calc(1%    * (1 - var(--o)) + -0.7% * var(--h)));
}
.fan__card--c {
  left: 26.62%; top: -.15%; width: 46.77%; transform-origin: 50% 100%;
  transform: translateY(calc(2% * (1 - var(--o)) + -1% * var(--h)));
}

.passes {
  margin-top: clamp(40px, 6vh, 64px);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}

/* Pass cards mirror the tier cards exactly (fondo/glow/line), mapped
   Free → Course Pass (static neutral line) and Gold → Pro (celeste). */
.pass-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-card);
  border: none; /* the line is the ring layer, like the tiers */
  background: rgba(0, 0, 0, .2);
  padding: clamp(28px, 3vw, 42px);
  display: flex;
  flex-direction: column;
  transition: opacity .9s var(--ease) var(--d, 0s), transform .5s var(--ease);
}
.pass-card:hover { transform: scale(1.02); }
.pass-card > * { position: relative; z-index: 2; }

/* breathing top glow — Pro breathes celeste the way Gold breathes gold */
.pass-card::before {
  content: "";
  position: absolute;
  inset: -12% -10%;
  z-index: 1;
  background: radial-gradient(70% 42% at 50% 6%, rgba(76, 76, 76, .24), rgba(76, 76, 76, .05) 42%, transparent 70%);
  animation: glow-breathe 7s ease-in-out infinite alternate;
  pointer-events: none;
}
.pass-card--pro::before {
  background: radial-gradient(70% 42% at 50% 6%, rgba(59, 165, 255, .3), rgba(30, 109, 255, .09) 42%, transparent 70%);
}

/* border line = ring layer. overflow:hidden clips at the padding box, so the
   ring sits at inset 0 (not -1px) — same gotcha as .tier-card__ring. */
.pass-card .pass-card__ring {
  position: absolute;
  inset: 0;
  border-radius: calc(var(--r-card) - 1px);
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
  /* Free's static edge: gray ramp at 10% */
  background: linear-gradient(108deg,
    rgba(232, 236, 243, .1), rgba(185, 192, 204, .1) 55%, rgba(138, 148, 165, .1));
}
.pass-card--pro .pass-card__ring {
  /* gold's edge in celeste: transparent → celeste → blue */
  background: linear-gradient(108deg,
    rgba(143, 216, 255, 0), #3ba5ff 45%, #1e6dff);
}
.pass-card:not(.pass-card--pro) .pass-card__ring i { display: none; }
.pass-card__ring i {
  position: absolute;
  inset: -60%;
  background: conic-gradient(from 0deg,
    transparent 0deg, rgba(143, 216, 255, .95) 55deg, transparent 110deg);
  animation: line-rotate 6s linear infinite;
}
.pass-card__ring b {
  position: absolute;
  inset: 1px;
  border-radius: calc(var(--r-card) - 2px);
  background: #000; /* page black, same trick as the tiers */
}

/* Dot field on hover (js/dot-field.js), per Figma 12040:6161 — the same
   halftone grid the hero and the closer carry, at the same 1:1 scale, but
   living inside the card and only while the pointer is on it.
   The design tops it with a black diagonal (30.638deg) so the dots survive
   only in the upper-right corner; over a black card a mask is identical to
   that overlay and keeps the stacking simple.
   The mask is painted at 240% and slides into place, so the field sweeps in
   from outside the corner instead of just fading — mask-position animates,
   while interpolating the gradient's own stops would need @property. */
.pass-card > .pass-card__shader {
  position: absolute;
  inset: 1px;              /* the 1px canto belongs to .pass-card__ring */
  z-index: 1;              /* over the breathing glow, under the copy (z2) */
  width: calc(100% - 2px);
  height: calc(100% - 2px);
  display: block;
  pointer-events: none;
  opacity: 0;
  -webkit-mask-image: linear-gradient(30.638deg, transparent 42%, #000 78%);
          mask-image: linear-gradient(30.638deg, transparent 42%, #000 78%);
  -webkit-mask-size: 240% 240%;
          mask-size: 240% 240%;
  -webkit-mask-position: 0% 100%;
          mask-position: 0% 100%;
  transition: opacity .6s var(--ease),
              -webkit-mask-position .9s var(--ease),
                      mask-position .9s var(--ease);
}
/* the class is toggled by dot-field.js, not :hover — the canvas has to keep
   drawing through the fade-out, so JS owns both ends of the state */
.pass-card.is-dots > .pass-card__shader {
  opacity: 1;
  -webkit-mask-position: 62% 38%;
          mask-position: 62% 38%;
}
/* no pointer, no field — and JS skips the WebGL context entirely there */
@media (hover: none) {
  .pass-card > .pass-card__shader { display: none; }
}

.pass-card__name {
  /* Text lg/Semibold — same ramp as .tier-card__name (Silver/Gold) */
  font-size: 1.125rem;
  font-weight: 584;
  line-height: 1.556;
  color: rgba(250, 250, 250, .6); /* Alpha/Text/Tertiary */
}
/* PRO chip (Figma 11890:3610). It is a PARALLELOGRAM, not a rect — the vector
   path runs 3.09px of lean over 18.56px of height, i.e. exactly 1:6 → 9.46°.
   The 40.21 in Figma is the slanted bounding box; the face itself is 37.11
   wide, so that is the CSS width and the skew re-creates the overhang.
   Skewing the box (rather than a backdrop layer) also makes `overflow: hidden`
   clip the shine to the slanted edges; only the label counter-skews. */
.pass-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  isolation: isolate;
  margin-left: 5px;
  /* `vertical-align: middle` lands on the x-height midpoint, not the line-box
     one, which left it 1.8px low against the 18/28 name — Figma has it dead
     centre, so the nudge puts it back */
  vertical-align: middle;
  top: -2px;
  width: 37.1px;
  height: 18.6px;
  border-radius: 1.5px;
  transform: skewX(-9.46deg);
  background: linear-gradient(180deg, rgba(255, 255, 255, .5), rgba(255, 255, 255, .1));
  -webkit-backdrop-filter: blur(6.2px);
  backdrop-filter: blur(6.2px);
  box-shadow: 0 0 43px rgba(255, 180, 60, .18);
  font-size: 12.4px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
  color: #fff;
}
.pass-badge i { font-style: normal; transform: skewX(9.46deg); } /* label stays upright */
.pass-badge::after {
  content: "";
  position: absolute;
  inset: -1px;
  background: linear-gradient(105deg,
    transparent 38%, rgba(255, 255, 255, .9) 50%, transparent 62%);
  translate: -160% 0;
  animation: pass-badge-shine 5.2s var(--ease) infinite;
  pointer-events: none;
}
/* the sweep runs in the last ~28% of the cycle; the rest is the rest */
@keyframes pass-badge-shine {
  0%, 72% { translate: -160% 0; }
  100%    { translate: 160% 0; }
}

.pass-card__price {
  margin-top: 1rem;
  font-size: clamp(2.6rem, 4.6vw, 3.6rem);
  font-weight: 700;
  letter-spacing: -.025em;
  line-height: 1;
}
.pass-card__price span { font-size: .38em; font-weight: 600; color: var(--ink-d2); letter-spacing: 0; }
.pass-card__req {
  /* plain quiet caps — the capsule chrome was removed (user 2026-07-30) */
  margin-top: .8rem;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--ink-d2);
}
.pass-card__body { margin-top: 1.2rem; color: var(--ink-d2); max-width: 40ch; }

/* Course belt — replaces the placeholder discs with real course chips that
   loop sideways inside the card. Geometry is Figma 12027:4095: 40px disc,
   8px to the label, name 12 / location 10 at 30%, and a 1×39 divider that
   fades out at both ends. The strip is DESIGNED to overflow — in Figma it is
   794 wide inside 489 of usable card — so the card crops it and the loop
   just keeps it moving. */
.courses {
  /* Pinned to the card's foot rather than the flow: both cards are the same
     height (the grid stretches them) but the body runs one line on Course Pass
     and two on Pro, which left the belts at different heights. The auto margin
     eats the slack so both sit on the card's bottom padding; the minimum air
     comes from the body's margin-bottom below. */
  margin-top: auto;
  height: 40px;
  overflow: hidden;
  /* the label fades out instead of being sliced at the crop line */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.pass-card__body { margin-bottom: 32px; } /* = Figma's 32 sp before the strip */

.courses__track {
  display: flex;
  width: max-content;
  height: 100%;
  align-items: center;
  will-change: transform;
}
/* main.js clones the set; the loop shifts by exactly one set's width, so
   -50% of the two-set track lands on the seam. Without JS the strip simply
   sits still — which is the Figma mock.
   Speed is 18px/s — slow enough to read a course name as it goes by. main.js
   sets --courses-dur per belt from its own measured set width, because the two
   cards carry different line-ups and a shared duration would run them at
   different speeds. The value here is only the no-JS fallback. */
.courses { --courses-dur: 45s; }
.courses--run .courses__track { animation: courses-run var(--courses-dur) linear infinite; }
@keyframes courses-run { to { transform: translateX(-50%); } }

/* 24 each side of the 1px divider = 49px chip to chip. Figma has 20 (41px);
   the user opened it up for air. Widening this lengthens the set, but the
   duration is measured, so the belt keeps its 18px/s. */
.courses__set { display: flex; align-items: center; gap: 24px; padding-right: 24px; }

.course-chip { display: flex; align-items: center; gap: 8px; flex: none; }
.course-chip__crest {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  /* most crests sit on white; a few carry the club's own colour and set
     --crest-bg inline (Gearhart gray, Glen Abbey navy, Olympic Club red) */
  background: var(--crest-bg, #fff);
  display: grid;
  place-items: center;
  flex: none;
  overflow: hidden;
}
.course-chip__crest img { display: block; }

.course-chip__txt { display: flex; flex-direction: column; line-height: 1; }
.course-chip__txt b {
  font-size: 12px;
  font-weight: 584;
  letter-spacing: -.01em;
  color: #fff;
  white-space: nowrap;
}
.course-chip__txt i {
  font-style: normal;
  font-size: 10px;
  font-weight: 584;
  color: rgba(255, 255, 255, .3);
  margin-top: 1px;
  white-space: nowrap;
}

.course-chip__sep {
  flex: none;
  width: 1px;
  height: 39px;
  background: linear-gradient(180deg, rgba(3, 72, 217, 0), var(--blue) 50%, rgba(3, 72, 217, 0));
}
/* ==========================================================================
   12 · VENUES
   ========================================================================== */

.venues {
  position: relative;   /* containing block for the backdrop plate */
  background: #000;     /* what shows around the rounded window while it opens */
  padding: clamp(110px, 16vh, 190px) 0;
}
.venues__inner { position: relative; z-index: 1; text-align: center; }
.venues__inner .lede { max-width: 56ch; margin-inline: auto; }

/* Backdrop per Figma 12053:11782: a photo carrying a 100px layer blur, sized
   1778×787 over a 1440×660 frame and cropped by it. The blur is baked into the
   asset rather than run as a CSS filter — a 100px blur on a full-bleed layer
   is an expensive paint, and it would bleed the plate's edges into nothing.
   Because the source has no detail left, 960px wide is plenty (5 KB).
   The plate overhangs the section by --par-over and takes the shared [data-par]
   drift, exactly like the showcase frames and the Foresight+ cards. */
.venues__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;   /* the window the oversized plate drifts inside */
  pointer-events: none;
  /* the section's gradients live on the plate, not on .venues: the expanding
     mask clips this element, so they open with it and the section keeps flat
     black outside the window. They are also the fallback if the image fails. */
  background:
    radial-gradient(85% 120% at 15% 0%, rgba(3, 72, 217, .16), transparent 55%),
    linear-gradient(180deg, #050b16 0%, #02040984 60%, #000 100%),
    #000;
}
.venues__par {
  position: absolute;
  inset: calc(var(--par-over) * -1) 0;
  transform: translate3d(0, var(--par, 0px), 0);
  will-change: transform;
}
.venues__plate {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ==========================================================================
   13 · FAQ
   ========================================================================== */

.faq { padding: clamp(96px, 14vh, 170px) 0; }

.faq__grid {
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: clamp(36px, 5vw, 90px);
  align-items: start;
}
.faq__head { position: sticky; top: calc(var(--nav-h) + 48px); }
.faq__head .h2 {
  /* Display lg/Bold — 48/60, −2%: keeps the title on two lines */
  font-size: clamp(2rem, 3.33vw, 3rem);
  line-height: 1.25;
}
.faq__list { margin-top: 6px; }

.qa { border-top: 1px solid var(--line-l); }
.qa:last-child { border-bottom: 1px solid var(--line-l); }

.qa summary {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 26px 2px;
  font-size: clamp(1.1rem, 1.39vw, 1.25rem); /* Text xl/Semibold */
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: -.01em;
  cursor: pointer;
  transition: color .3s;
}
.qa summary::-webkit-details-marker { display: none; }
.qa summary:hover { color: var(--blue); }

.qa__icon {
  position: relative;
  flex: none;
  width: 22px;
  height: 22px;
}
.qa__icon::before, .qa__icon::after {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  width: 15px;
  height: 1.8px;
  background: var(--ink-l2);
  transform: translate(-50%, -50%);
  transition: transform .4s var(--ease);
}
.qa__icon::after { transform: translate(-50%, -50%) rotate(90deg); }
.qa[open] .qa__icon::after { transform: translate(-50%, -50%) rotate(0deg); }
.qa[open] .qa__icon::before { transform: translate(-50%, -50%) rotate(0deg); }

.qa__panel { overflow: hidden; }
.qa__panel p {
  padding: 0 40px 28px 2px;
  font-size: clamp(1rem, 1.25vw, 1.125rem); /* Text lg/Medium */
  font-weight: 500;
  line-height: 1.556;
  color: var(--ink-l2);
  max-width: 60ch;
}

/* ==========================================================================
   14 · CLOSER
   ========================================================================== */

.closer {
  /* top matched to bottom (user 2026-07-31) — it was clamp(168px, 26vh, 300px),
     roughly 36% taller than the foot, which read as a lopsided module */
  padding: clamp(124px, 19vh, 220px) 0;
  text-align: center;
  position: relative;
  overflow: clip;
}
.closer::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -320px;
  width: 1100px;
  height: 640px;
  transform: translateX(-50%);
  background: radial-gradient(closest-side, rgba(3, 72, 217, .2), transparent 70%);
  pointer-events: none;
  z-index: 1; /* glow above the dot field */
}

.closer__inner { position: relative; z-index: 2; }
/* midway between .h2's 1.25 and the band headline's .875 (user 2026-08-19),
   mirrored from Premier's closer. */
.closer__inner .h2--xl { line-height: 1.06; }
@media (max-width: 1024px) {
  .closer__inner .h2--xl { line-height: 1.135; }
}

/* Ambient dot-field canvas (js/dot-field.js) — behind the glow and content.
   Without the explicit z-indexes the canvas paints OVER ::before (pseudo is
   first in paint order). Mask fades the grid at the section's edges. */
.closer__shader {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
}

/* 3D glass app icon (Figma 11802:93118) — replaces the eyebrow tag here.
   JS scrubs its entrance + float/tilt; static and fully visible without it. */
.closer__icon {
  display: block;
  width: clamp(133px, 11.9vw, 171px);
  height: auto;
  aspect-ratio: 1; /* <video> won't infer it from the attrs before metadata lands */
  margin: 0 auto 1.05rem;
  will-change: transform, opacity;
}
.closer .lede { margin-inline: auto; }
.closer .cta-row { justify-content: center; }

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

.footer {
  background: var(--black);
  border-top: 1px solid rgba(255, 255, 255, .1); /* alpha-white-10 */
  padding: 34px 0 46px;
  text-align: left;
  font-size: .74rem;
  line-height: 1.7;
  color: var(--ink-d3);
}
.footer p + p { margin-top: .5rem; }
.footer__legal { padding-top: 1rem; color: rgba(255, 255, 255, .4); font-weight: 500; } /* alpha-white-40 */

/* ==========================================================================
   FLOATING COMPATIBILITY PILL
   Capsule geometry (360×42, 4px inset, 34px round button) wearing the
   localnav's glass. The scroll choreography runs in two beats and reverses:
   leaving — the capsule furls toward its button cap (clip-path), then the cap
   slides off past the right edge; returning — it slides back in first, then
   unfurls leftward. Driven from main.js: visible near the top and again near
   the end of the page, furled away in between. The × dismisses it for good.
   ========================================================================== */

.pill {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 90; /* under the nav (100), over everything else */
  display: flex;
  align-items: center;
  gap: 4px;
  width: min(360px, calc(100vw - 32px));
  height: 42px;
  padding: 4px 4px 4px 0;
  border-radius: 980px;
  border: 1px solid rgba(255, 255, 255, .08);
  background: rgba(8, 8, 10, .4);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  box-shadow: 0 4px 24px rgba(0, 0, 0, .35);
  clip-path: inset(0 0 0 0 round 980px);
  translate: 0 0;
  opacity: 1;
  /* return beat: slide in, then unfurl */
  transition:
    translate .5s var(--ease),
    clip-path .55s var(--ease) .22s,
    opacity .25s var(--ease);
}
.pill--away {
  /* leaving beat: furl to the cap, then slip off the edge */
  clip-path: inset(0 0 0 calc(100% - 42px) round 980px);
  translate: calc(100% + 32px) 0;
  opacity: 0;
  pointer-events: none;
  transition:
    clip-path .5s var(--ease),
    translate .55s var(--ease) .2s,
    opacity .3s var(--ease) .45s;
}

.pill__text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  /* dissolve only where the text exits, toward the button */
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 75%, transparent 100%);
  mask-image: linear-gradient(90deg, #000 0, #000 75%, transparent 100%);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
}

.pill__track {
  display: flex;
  width: max-content;
  animation: pill-ticker 30s linear infinite;
}
@keyframes pill-ticker { to { transform: translateX(-50%); } }
.pill:hover .pill__track { animation-play-state: paused; }

.pill__item {
  padding-left: 18px; /* clears the capsule curve */
  padding-right: 2em;
  font-size: 15px;
  line-height: 24px;
  font-weight: 492;
  white-space: nowrap;
  color: rgba(250, 250, 250, .8); /* Alpha/Text/Secondary */
}

.pill__btn {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, .08);
  color: #fff;
  cursor: pointer;
  transition: background .25s ease;
}
.pill__btn:hover { background: rgba(255, 255, 255, .16); }
.pill__btn:active { background: rgba(255, 255, 255, .22); }
.pill__btn svg { width: 16px; height: 16px; transition: transform .4s var(--ease); }
.pill__btn:hover svg { transform: rotate(90deg); }

/* ==========================================================================
   BREAKPOINTS
   ========================================================================== */

@media (max-width: 1068px) {
  .why__grid { grid-template-columns: 1fr; max-width: 560px; margin-inline: auto; }
  .faq__grid { grid-template-columns: 1fr; }
  .faq__head { position: static; }
  .bento-card--7, .bento-card--5 { grid-column: span 12; }
  .bento-card--4 { grid-column: span 6; }
  .tiers__grid { grid-template-columns: 1fr; max-width: 640px; margin-inline: auto; }
}

@media (max-width: 820px) {
  .localnav__links { display: none; }
  .localnav__burger { display: flex; }

  .passes { grid-template-columns: 1fr; }
  .why-cap { padding-top: 24px; font-size: 16px; line-height: 1.5; letter-spacing: -.016em; } /* = ag stacked caption, Text md/Semibold */
  .stack-card__art { width: 82%; left: auto; right: -24%; top: 4%; }
  .bento-card--4 { grid-column: span 12; }

  /* +20% module height on mobile (user 2026-07-30): 14vh → 20.3vh padding
     measured to land total height at 1.2× (513 → 616 at 375×812) */
  .realism { min-height: 0; padding: clamp(130px, 20.3vh, 240px) 0; }
  /* the expanding mask is off at this breakpoint — a 6.25vw inset is a
     sliver here, and the rounded window just eats the frame */
  [data-clip] { --clip: 0 !important; clip-path: none; }

  /* el calado en pantallas angostas: el slice del viewBox recorta los lados,
     así que la máscara baja de cuerpo para que las tres líneas entren */
  /* (the old `font-size: 88px` override lived here — it fitted the width but
     left the fixed line gaps at 1.42× the font instead of .89×, which is what
     read as "muy separado". --ms replaces it.) */

  /* Composition on mobile (user 2026-07-31). The panel is exactly one screen
     and the calado+sub group only fills ~half, so ~434px of air has to live
     somewhere — top and bottom trade against each other and can't both shrink.
     Two passes, measured at 390×844:
       1. gap 166 → 83 (halved) by dropping the mask block, bottom 125 → 90;
       2. top 344 → 258 (−25%, same move as the module margins) by lifting the
          whole group 86px — which is why the bottom lands back at ~176.
     The mask translate is in viewBox units so it rides the slice scale and
     holds the same proportion at every width; the sub stays a %. */
  .vt__masktext { transform: translateY(30.5px) scale(var(--ms, 1)); }
  .vt__sub { top: 68.5%; }

  .gallery { gap: 14px; }
  .gallery-paddles { display: none; }
}

@media (max-width: 480px) {
  /* One gutter for the whole phone layout. The containers are percentage-based
     (92vw / 94vw), so they drifted apart on a narrow screen — 15px for .container
     against 11px for .container-wide. A flat 16px lines every section up on the
     same edge; --ag-inset carries it into the stacked analysis tiles, and the
     showcase still measures its 104vw bleed from that same margin. */
  .container, .container-wide, .container-narrow,
  .showcase__inner { width: calc(100% - 32px); } /* .showcase__inner repeats the 94vw formula as its own class */
  .ag { --ag-inset: 16px; }
  .hero__title { font-size: clamp(2.9rem, 15vw, 3.9rem); }
  .btn { width: 100%; }
  .cta-row { flex-direction: column; align-items: stretch; }
  .hero .cta-row, .closer .cta-row { align-items: center; }
  .hero .btn, .closer .btn { width: auto; min-width: 230px; }
  .fsx__cta { width: auto; } /* the marquee CTA hugs its label, per Figma */
  .pill { right: 16px; bottom: 16px; }
  .tiles { grid-template-columns: repeat(2, 1fr); }
}

/* ==========================================================================
   REDUCED MOTION
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  /* the scrub job never registers here — land on the open state, not the window */
  [data-clip] { --clip: 0; clip-path: none; }
  .showcase__img { animation: none; }
  .tier-card::before { animation: none; }
  .tier-card__ring i { animation: none; }
  .pass-card::before { animation: none; }
  .pass-card > .pass-card__shader { display: none; } /* pure decoration, hover-only */
  .pass-badge::after { animation: none; translate: -160% 0; }
  .pass-card__ring i { animation: none; }
  .tier-card:hover { transform: none; }
  .pass-card:hover { transform: none; }
  .paddle, .btn { transition: none; }
  .btn .roll b, .paddle svg, .pill__btn svg { transition: none; }
  /* roll frozen: first copy always in place, second always parked below */
  .btn .roll b:first-child { transform: none !important; }
  .btn .roll b:nth-child(2) { transform: translateY(115%) !important; }
  .link-arrow::after { animation: none; }
  .fsx__viewport { overflow-x: auto; } /* JS belt is off; row scrolls by hand */
  .pill, .pill--away { transition: none; }
  .pill__track { animation: none; }
  .playhero__layer, .playhero__back, .playhero__front { transform: none !important; opacity: 1 !important; }
  .playhero__layer { transform: translateX(-50%) !important; }
  /* vt: sin transiciones ni drift; la composición calada queda estática
     (el sub visible, el frame a su tamaño de reposo — min-height cubre el hold) */
  .vt__frame { transition: none; transform: none !important; border-radius: 28px !important; }
  .vt__video { animation: none; }
  .vt__maskart { transition: none; transform: none; }
  .vt__sub { transition: none; opacity: 1; transform: translate(-50%, 0); }
  .vt__veil { transition: none; opacity: 1; } /* the is-rising job never runs here */
  .fan__card { transition: none !important; transform: none !important; }
}

/* ============================================================
   9 · COMPARE PLANS — full-bleed overlay + slide-up module
   Figma: 12228:3770 (Table-1), 12228:4220 (Table module),
          12228:4743 (scroll behaviour).

   The panel is a FIXED 833-tall window (the Figma module height,
   clamped to the viewport). Everything inside scrolls — including
   the close button and title — except the Free/Silver/Gold header,
   which sticks to the top of its own table container. Rows blur
   away behind that header and fade at both edges of the window.
   ============================================================ */

/* plate = alpha-white-5 composited over --elev; used by the sticky
   header and the fade ramps so rows dissolve into the same value. */
:root { --cmp-plate: #191919; }

.compare {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
  visibility: hidden;
  pointer-events: none;
  /* hold visibility until the exit transition has played out */
  transition: visibility 0s linear .5s;
}
.compare.is-open {
  visibility: visible;
  pointer-events: auto;
  transition-delay: 0s;
}

.compare__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .72);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity .42s var(--ease);
  cursor: pointer;
}
.compare.is-open .compare__scrim { opacity: 1; }

/* The module — eases up on open, back down and out on close. */
.compare__panel {
  position: relative;
  width: 633px;
  max-width: 100%;
  height: min(833px, calc(100vh - 48px));
  background: var(--elev);
  border: 1px solid rgba(255, 255, 255, .05);
  border-radius: var(--r-card);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* the close button keeps the panel's own 24px; the headline and the
     table plate sit a further 40px in, for the 64px gutter in Figma */
  --cmp-inset: 40px;
  padding: 24px 24px 24px;
  opacity: 0;
  transform: translateY(56px);
  transition: opacity .42s var(--ease), transform .52s var(--ease);
}
.compare.is-open .compare__panel { opacity: 1; transform: none; }


.compare__close {
  flex: none;
  align-self: flex-start;
  width: 44px;
  height: 44px;
  padding: 0;
  position: relative;
  border: 1px solid var(--line-d);
  border-radius: 50%;
  background: rgba(255, 255, 255, .06);
  cursor: pointer;
  transition: background .2s var(--ease), border-color .2s var(--ease);
}
.compare__close:hover { background: rgba(255, 255, 255, .12); border-color: rgba(255, 255, 255, .22); }
.compare__close:focus-visible { outline: 2px solid var(--blue-light); outline-offset: 3px; }
.compare__close::before,
.compare__close::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 14px;
  height: 1.5px;
  margin: -.75px 0 0 -7px;
  background: var(--ink-d1);
  border-radius: 2px;
}
.compare__close::before { transform: rotate(45deg); }
.compare__close::after  { transform: rotate(-45deg); }

.compare__head {
  flex: none;
  text-align: center;
  padding: 10px 0 20px;
  margin: 0 var(--cmp-inset);
}
.compare__head .eyebrow { margin-bottom: 8px; }
.compare__title {
  margin: 0;
  font-family: var(--font);
  font-weight: 600;                /* Demibold */
  font-size: 36px;                 /* display-md */
  line-height: 44px;
  letter-spacing: -.72px;
  color: var(--ink-d1);
}

/* ---- table plate: alpha-white-5, radius-2xl, 24 padding ---- */
.compare__table {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: rgba(255, 255, 255, .05);
  border-radius: 16px;
  padding: 0 24px;
  margin: 0 var(--cmp-inset);
}

.compare__cols,
.compare__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) repeat(3, 80px);
  gap: 4px;
  align-items: center;
}

/* Free / Silver / Gold — fixed with the rest of the header block.
   Bottom-aligned so FREE sits on the same line as the Silver/Gold
   prices rather than floating between name and price. */
.compare__cols {
  flex: none;
  align-items: end;
  padding: 24px 0 8px;
}

/* The only scrolling region. Rows dissolve into both edges of the
   window via a mask, so nothing hard-clips at the plate bounds. */
.compare__rows {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  /* top pad + the group's own 20px lands the first heading at 28px,
     just clear of the 22px ramp */
  padding: 8px 0 20px;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 22px, #000 calc(100% - 38px), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 22px, #000 calc(100% - 38px), transparent 100%);
}
.compare__rows::-webkit-scrollbar { width: 0; height: 0; }

.compare__plan { text-align: center; }
.compare__plan-name {
  display: block;
  font-size: 16px;
  font-weight: 500;
  color: var(--ink-d1);
  letter-spacing: -.01em;
}
.compare__plan-price {
  display: block;
  margin-top: 1px;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 16px;
  color: var(--ink-d2);
}
.compare__plan--free .compare__plan-name {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-d2);
}

.compare__group {
  padding: 20px 0 8px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--ink-d1);
}

.compare__row {
  padding: 9px 0;
  border-top: 1px solid rgba(255, 255, 255, .07);
}
.compare__label {
  font-size: 14px;          /* text-sm */
  line-height: 20px;
  font-weight: 500;         /* Medium */
  color: var(--ink-d1);
}
.compare__cell {
  text-align: center;
  font-size: 14px;
  color: var(--ink-d1);
}

/* included — the circle-check mask the tier cards use */
.compare__yes {
  display: inline-block;
  width: 16px;
  height: 16px;
  vertical-align: -2px;
  background: var(--ink-d1);
  -webkit-mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10 1a9 9 0 100 18 9 9 0 000-18zm4.2 6.4l-4.9 5.4a.9.9 0 01-1.32.03L5.7 10.5a.9.9 0 111.27-1.27l1.6 1.6 4.28-4.7a.9.9 0 111.34 1.2z"/></svg>') center / contain no-repeat;
  mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10 1a9 9 0 100 18 9 9 0 000-18zm4.2 6.4l-4.9 5.4a.9.9 0 01-1.32.03L5.7 10.5a.9.9 0 111.27-1.27l1.6 1.6 4.28-4.7a.9.9 0 111.34 1.2z"/></svg>') center / contain no-repeat;
}
.compare__no { font-weight: 600; color: rgba(255, 255, 255, .28); }

/* ---- Scroll indicator -------------------------------------------
   Sits in the panel's right gutter, outside the table plate, and
   spans exactly the plate's height (Figma 12237:5221). Hidden until
   you hover the module, and flashes briefly while scrolling. */

.compare__bar {
  position: absolute;
  top: 4px;                 /* iOS insets the indicator from the scroll bounds */
  bottom: 4px;
  right: calc(-1 * var(--cmp-inset) - 9px);   /* 15px in from the panel edge */
  width: 6px;
  border-radius: 999px;
  opacity: 0;
  pointer-events: none;
  /* out: lingers, then eases away. in: effectively instant. */
  transition: opacity .4s var(--ease) .45s;
}
.compare__panel:hover .compare__bar,
.compare__bar.is-active {
  opacity: 1;
  pointer-events: auto;
  transition: opacity .05s linear;
}
.compare__thumb {
  display: block;
  width: 100%;
  border-radius: 999px;
  background: rgba(255, 255, 255, .4);
  transition: background .15s var(--ease);
  cursor: grab;
}
.compare__bar:hover .compare__thumb { background: rgba(255, 255, 255, .52); }
.compare__bar.is-dragging .compare__thumb { background: rgba(255, 255, 255, .62); cursor: grabbing; }



@media (max-width: 560px) {
  .compare { padding: 16px 12px; }
  .compare__panel { height: calc(100vh - 32px); border-radius: var(--r-panel); --cmp-inset: 0px; }
  .compare__panel { padding: 20px 16px; }
  .compare__table { padding: 0 16px; }
  .compare__cols { padding: 16px 0 12px; }
  .compare__cols,
  .compare__row { grid-template-columns: minmax(0, 1fr) repeat(3, 56px); gap: 4px; }
  .compare__title { font-size: 27px; line-height: 34px; }
  .compare__label { font-size: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .compare__scrim,
  .compare__panel { transition: opacity .2s linear; }
  .compare__panel { transform: none; }
}

/* ---- Course list variant (Figma 12237:5221) ---------------------
   Same shell as Compare plans, but two 80px plan columns, brighter
   row labels, and rows separated by a 12px gap instead of rules. */

.compare--courses .compare__cols,
.compare--courses .compare__row {
  grid-template-columns: minmax(0, 1fr) 80px 80px;
}
.compare--courses .compare__cols {
  align-items: start;
  padding: 24px 0 8px;
}
.compare--courses .compare__plan-name {
  font-size: 16px;
  line-height: 18px;
}
.compare--courses .compare__rows {
  display: flex;
  flex-direction: column;
  gap: 19px;              /* 21px row + 19px = 40px pitch, same as Compare plans */
  padding: 20px 0 20px;
}
.compare--courses .compare__row {
  padding: 0;
  border-top: 0;
}

@media (max-width: 560px) {
  .compare--courses .compare__cols,
  .compare--courses .compare__row { grid-template-columns: minmax(0, 1fr) 68px 68px; }
  .compare--courses .compare__plan-name { font-size: 14px; }
}


.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;
}


.plusband__cta {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: clamp(20px, 2.4vw, 36px);
  margin-top: clamp(30px, 4.5vh, 50px);
}

/* --- COMPATIBILITY (user comp 2026-08-14): metallic gradient headline over
   the device-lineup jpeg; the image tucks up behind the head so the copy
   floats in its dark upper region like the comp --- */
.compat {
  position: relative;
  overflow: hidden;
  background: #000;
  padding-top: clamp(84px, 12vh, 160px);
  text-align: center;
}
.compat__head {
  position: relative;
  z-index: 1;
  /* the shared .container caps at 1080px, but the headline's first line needs
     ~1130px at the top of its clamp — so it broke to three lines and stranded
     the "and". This band gets its own wider measure (user 2026-08-21). */
  width: min(1420px, 94vw);
  max-width: none;
  margin-inline: auto;
}
.compat__title {
  /* The hard <br> broke after "and", but at the top of the clamp that first
     line is wider than the container — so it wrapped again and orphaned the
     "and" on its own line (user 2026-08-21). Giving the heading a measure in
     em means the break scales with the type instead of fighting it, and
     balance keeps the two lines even. */
  max-width: min(15em, 94vw);
  margin: 0 auto;
  text-wrap: balance;
  font-size: clamp(38px, 5.2vw, 92px);
  font-weight: 640;
  line-height: 1.04;
  letter-spacing: -.03em;
  background: linear-gradient(180deg, #fff 8%, #d9dbdf 46%, #55585e 96%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* BUTTON — travelling ring (user 2026-08-14, made shared 2026-08-21).
   Keeps the .btn geometry but wears an animated stroke instead of a fill or a
   ghost border. Now a modifier so any button can take it, rather than living
   on the compat CTA alone. */
.btn--ring {
  position: relative;
  border: 0;
  border-radius: 999px;
  background: #000; /* stays black on hover too — only the stroke reacts */
  --cta-ring: .28;
  color: #fff;
  transition: --cta-ring .28s var(--ease);
}
.btn--ring:hover { transform: none; background: #000; filter: none; }
/* device-list lede under the headline (user 2026-09-04): the shared .lede
   type, centred like the other section ledes; 56ch (not the base 46) so the
   eight device names hold to two lines at desktop widths. */
.compat .lede { margin-inline: auto; max-width: 56ch; }
.compat .compat__cta { margin-top: clamp(26px, 3.5vh, 44px); }
/* the icon rides beside the label, inside the button's flex row */
.btn__ico {
  flex: none;
  width: 1.05em;
  height: 1.05em;
  margin-right: .55em;
  margin-left: -.15em;
}
/* the travelling stroke: a conic gradient rotated by an animated angle, laid
   over a constant dim ring so the outline never disappears. Both layers are
   punched hollow by the same mask, leaving only the 1px edge.
   @property is what makes the angle animatable at all — without it the conic
   simply sits still, which is a clean static fallback. */
@property --cta-angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}
/* inherits so ::before picks it up from the button; registered as a number so
   it can be transitioned — hover lifts the whole ring to solid white */
@property --cta-ring {
  syntax: "<number>";
  initial-value: 0.28;
  inherits: true;
}
.btn--ring::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background:
    conic-gradient(from var(--cta-angle) at 50% 50%,
      rgba(255, 255, 255, var(--cta-ring)) 0deg,
      rgba(255, 255, 255, 1) 48deg,
      rgba(255, 255, 255, var(--cta-ring)) 96deg,
      rgba(255, 255, 255, var(--cta-ring)) 360deg),
    linear-gradient(rgba(255, 255, 255, var(--cta-ring)) 0 0);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
  animation: cta-stroke 3.6s linear infinite;
}
@keyframes cta-stroke { to { --cta-angle: 360deg; } }
/* hover: the whole pill outlines in solid white, body untouched */
.btn--ring:hover { --cta-ring: 1; }
@media (prefers-reduced-motion: reduce) {
  .compat__cta { transition: none; }
  .btn--ring::before { animation: none; }
}
/* --- the comp's frame, rebuilt as layers (user 2026-08-18) ---------------
   Everything is positioned as a % of Figma's 1440x1129 frame, so the whole
   stage scales with the column. Measured against the flat export, this
   reconstruction lands at a mean error of 1.3/255 — the comp's four blurred
   #0A0A0A blobs turned out to be invisible over the near-black base and were
   dropped rather than carried as four more nodes.
   NOTE: Figma stretches the lineup into its 1090x529 box (the source plate is
   1024x795), so both axes are pinned here on purpose — do not "fix" the
   aspect, it would no longer match the comp. */
.compat__stage {
  position: relative;
  overflow: hidden;              /* clips every plate's overhang */
  width: min(1720px, 100%);
  aspect-ratio: 1440 / 1129;
  /* less negative than before (user 2026-08-21): the lineup grew 20%, so the
     stage was riding up into the headline and CTA — pulling it down is what
     lifts the copy clear */
  margin: clamp(-330px, -19vw, -80px) auto 0;
  /* melts the stage's top edge into the section black, as the flat plate did */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 30%);
          mask-image: linear-gradient(180deg, transparent 0, #000 30%);
}
.compat__stage > * { position: absolute; max-width: none; }
.compat__sky {
  inset: 0;
  background: linear-gradient(90deg, #050404, #1e2128);
}
/* the rocks: the comp's plate scaled up 6% about its centre purely to buy
   bottom overhang — its bottom row is fully opaque, so the edge must never
   climb into frame when the layer drifts up */
.compat__mtn {
  left: -57.842%;
  top: 28.722%;
  width: 223.115%;
  height: 74.266%;
  transform: translate3d(0, calc(var(--par, 0px) * .35), 0);
}
/* the comp's bottom fade, between the rocks and the lineup */
.compat__haze {
  left: 0;
  right: 0;
  bottom: 0;
  height: 30.115%;
  background: linear-gradient(0deg, #0a0a0a, rgba(10, 10, 10, 0));
}
/* the lineup, 20% smaller than the comp (user 2026-08-18). It keeps the comp's
   TOP edge rather than its base — shrinking off the base dropped the visual
   centre 53px and left the air lopsided (119px above, 21px below).
   Travels ~5x the rocks — that gap IS the parallax. The plate's own bottom row
   is still mid-shadow (alpha 72), so the last 9% is faded out: the solid bodies
   end at 90.7% of the source, so this only tapers the shadow and the cut can
   never read as an edge wherever the drift leaves it. */
.compat__rig {
  /* The old plate was a stretched export, so both axes were pinned. The new
     lineup render (user 2026-08-19) is a true 2048x1590 and must keep its own
     aspect — height is free, and the box is anchored by its FEET so the extra
     height grows upward into the frame's air rather than pushing the units
     down through the fade. */
  left: 21.7%;              /* re-centred for the narrower box */
  bottom: 1.2%;             /* sits lower in the frame (user 2026-08-21) */
  width: 56.6%;
  height: auto;
  /* belt and braces: if a height ever lands on this box again, contain keeps
     the render from stretching — the old rule squashed it 1.6x wide */
  aspect-ratio: 2048 / 1590;
  object-fit: contain;
  /* grows as it rides up the page: --parp is the driver's raw 0→1 pass, and
     the origin sits at the feet so the lineup scales up out of the ground
     rather than swelling from its middle (user 2026-08-21) */
  /* starts 20% up on where it used to sit and still grows on the way past
     (user 2026-08-21) */
  transform: translate3d(0, calc(var(--par, 0px) * 1.8), 0)
             scale(calc(1.2 + var(--parp, 0) * 0.14));
  transform-origin: 50% 100%;
  will-change: transform;
  -webkit-mask-image: linear-gradient(180deg, #000 91%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 91%, transparent 100%);
}
/* The comp's frame bottoms out at #090909, not black, so it met the next
   section's #000 as a visible seam. This pins the fade to the SECTION (not the
   stage) so it stays put while the layers drift. */
.compat::after {
  content: "";
  position: absolute;
  inset: auto 0 0;
  height: clamp(90px, 13vh, 170px);
  background: linear-gradient(180deg, rgba(0, 0, 0, 0), #000 78%);
  pointer-events: none;
}
@media (max-width: 640px) {
  /* the 1440-wide comp scales down to nothing on a phone — blow the stage up
     and re-centre it so the lineup still reads, and pull the head tighter.
     The global `img { max-width: 100% }` (line 83) capped the old 135% here,
     so the plate never grew and the negative margin just shoved it off centre. */
  .compat { padding-top: clamp(64px, 9vh, 92px); }
  .compat__stage {
    max-width: none;
    width: 178%;
    margin-left: -39%;   /* (100 - 178) / 2 — keeps the lineup centred */
    margin-top: clamp(-360px, -55vw, -150px);
  }
  .compat::after { height: clamp(70px, 10vh, 110px); }
}
/* the drift is a desktop nicety; on a phone it costs a scroll-linked write per
   frame for a few px of travel, so the plates sit still */
@media (max-width: 640px), (prefers-reduced-motion: reduce) {
  .compat__mtn, .compat__rig { transform: none; will-change: auto; }
}
/* WIDE WINDOWS (user 2026-09-21). The stage stops growing at 1720px, and past
   that it sat as a box in the section's black: the rocks and the lit side of
   the sky both ended in a hard vertical edge. The rocks plate is 2.23 frames
   wide, so there was always more of it to show. From here up the STAGE runs
   edge to edge and only the FRAME keeps the cap: every horizontal measure is
   the same fraction as above, taken off --frame and hung from the centre, so
   the crop inside the frame does not move and the window just opens onto more
   plate. Heights still read the stage, which the strut holds at the frame's
   own height, so nothing vertical changes either. */
@media (min-width: 1721px) {
  .compat__stage {
    /* a %, so it resolves against whatever reads it: the stage's width in the
       plates' left/width, the sky's own box in its gradient. The min() is for
       a classic scrollbar, which leaves the section just under the cap while
       the window is already over it — there this all equals the rules above. */
    --frame: min(1720px, 100%);
    width: 100%;
    aspect-ratio: auto;
  }
  .compat__stage::before {
    content: "";
    display: block;
    width: var(--frame);
    aspect-ratio: 1440 / 1129;
  }
  /* the same ramp across the frame, holding its end colours outward. The lit
     side falls back to the dark one over the stretch where the rocks fade out
     (92%-100% of the plate, see below), so a window wider than the plate ends
     dark on both sides rather than in a bare column of sky; up to ~3350px that
     stretch is still off screen. */
  .compat__sky {
    background: linear-gradient(90deg,
      #050404 calc(50% - var(--frame) / 2),
      #1e2128 calc(50% + var(--frame) / 2),
      #1e2128 calc(50% + var(--frame) * .97424),
      #050404 calc(50% + var(--frame) * 1.15273));
  }
  .compat__mtn {
    left: calc(50% - var(--frame) * 1.07842);   /* -57.842% of the frame */
    width: calc(var(--frame) * 2.23115);
  }
  .compat__rig {
    left: calc(50% - var(--frame) * .283);      /* 21.7% of the frame */
    width: calc(var(--frame) * .566);
  }
}
/* The plate runs out at ~3700px of window on the left and ~3970px on the right.
   Its ends are faded from 3000px, while both are still off screen, so they melt
   into the sky when they arrive instead of cutting — and nothing pops on the
   way there. */
@media (min-width: 3000px) {
  .compat__mtn {
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
            mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  }
}
/* DITHER (user 2026-09-21) — the sky climbs some 30 levels across the whole
   frame and is then multiplied by the stage's top fade, so every 1/255 step is
   a patch dozens of pixels across and the two ramps draw a grid of blocks; the
   wider the window, the easier it is to see. Same remedy as the noise tile over
   the Premier screenshots' veil: scatter each step boundary over a few pixels
   and the eye integrates a smooth ramp. It lives INSIDE the stage, so the top
   fade tapers it with everything else and the section's black stays pure, and
   UNDER the lineup, which has no ramp to hide. */
.compat__stage::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: .05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}
.compat__rig { z-index: 1; }

/* ==========================================================================
   FORESIGHT+ ECOSYSTEM BAND — ported from the Foresight+ page (user
   2026-08-18) so the Premier page shows the offering as the wired hub
   instead of a bullet row. Same component, Premier's own CTAs beneath.
   ========================================================================== */

.thisis {
  position: relative;
  /* doubled top air (user 2026-08-14): the band needs room off the hero */
  /* the Foresight+ page tucks the course marquee under this band, so its
     bottom padding was tiny. Here the venues band follows straight after,
     so the CTA needs real air beneath it (user 2026-08-18). */
  padding: clamp(48px, 7vh, 96px) 0 clamp(120px, 16vh, 210px); /* tight under compat (user 2026-08-18) */
  background: var(--black);
  overflow: clip;
  text-align: center;
}
.thisis__head { position: relative; z-index: 2; }
.thisis__head .h2--xl { line-height: .875; } /* the mock's tight two-line leading */
/* the break is a desktop-only device: tablet and phone read it on one line */
@media (max-width: 1024px) {
  .thisis__br { display: none; }
  .thisis__head .h2--xl { line-height: 1.02; }
}
.thisis__lede {
  position: relative;
  z-index: 2;
  /* Text xl/Semibold — 20/30, 60% on-brand white, 586px box (3 lines) */
  max-width: min(760px, 92vw); /* wider, single-flow copy (user 2026-08-14) */
  margin: clamp(1.75rem, 2.78vw, 2.5rem) auto 0;
  padding-inline: 24px;
  font-size: clamp(1.05rem, 1.39vw, 1.25rem);
  font-weight: 584;
  line-height: 1.5;
  color: rgba(245, 245, 246, .6);
}

/* ---- the band: two halves of the frame, hub overlapping between them ---- */
.thisis__stage {
  --wp: 0;
  --msc: .8;          /* module scale, picked in the lab */
  --lw: .5px;         /* wire hairline */
  --ch: 5px;      /* comet head height */
  --cblur: 1.09px;
  position: relative;
  z-index: 2;
  width: min(1440px, 96vw);
  margin: clamp(48px, 6.5vw, 93px) auto 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 0;
  align-items: center;
}
.thisis__col { display: contents; }
.thisis__col--premier > * { grid-column: 1; }
.thisis__col--app > * { grid-column: 2; }

/* ---- wire ---- */
.thisis__wire {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: min(1, calc(var(--wp) * 5)); /* first pixels ease in, not pop */
}
.thisis__trace {
  fill: none;
  /* pathLength=1 normalises both sides, and the offset comes from ONE custom
     property so the driver writes a single value instead of six inline styles */
  stroke-dasharray: 1;
  stroke-dashoffset: var(--dash, 1);
}
.thisis__trace--core  { stroke: rgba(255, 255, 255, .92); stroke-width: var(--lw); }
/* the glow is drawn as real strokes under the core, not left to a blur, so
   it stays glued to the line at every width */
.thisis__trace--glow  { stroke: #fff; stroke-width: calc(var(--lw) * 3.6); opacity: .16; }
.thisis__trace--glow2 { stroke: #fff; stroke-width: calc(var(--lw) * 8.8); opacity: .06; }
.thisis__hot { /* short bright segment of the rail under the hub */
  fill: none;
  stroke: url(#thisis-hotgrad);
  stroke-width: 1.5;
  opacity: calc(var(--wp) * .9);
}
/* the comet: a soft blob riding the drawn tip (Figma 12692:3689 — a quick
   ramp to a peak just behind the tip, then a long smooth fade) */
.thisis__comet {
  pointer-events: none;
  filter: blur(var(--cblur));
  /* Rasterise the blur ONCE and then only move the layer. Without this the
     browser re-runs the filter every frame; `mix-blend-mode: screen` used to
     sit here too and forced a backdrop read-back per frame — on black a white
     shape screens to itself, so dropping it costs nothing visually. */
  will-change: transform;
  /* NO css opacity here: it would beat the SVG presentation attribute the
     driver animates, and the comet would never show */
}

/* ---- hub ---- */
.thisis__cube {
  position: relative;
  z-index: 3;
  grid-column: 1 / -1;
  grid-row: 1;
  justify-self: center;
  margin: 0 0 0 2vw; /* the file sits the hub just right of centre */
  width: calc(clamp(285px, 30.9vw, 444px) * var(--msc)); /* +50% (user 2026-08-14) */
}
.thisis__cube img {
  position: relative;
  width: 100%;
  height: auto;
  will-change: transform;
  animation: thisis-float 6.5s ease-in-out infinite;
}
@keyframes thisis-float {
  0%, 100% { transform: translateY(-4px) rotate(-1.1deg); }
  50%      { transform: translateY(4px) rotate(1.1deg); }
}
.thisis__cube::before { /* junction glow blooms as the wire completes */
  content: "";
  position: absolute;
  inset: -40%;
  z-index: -1;
  background: radial-gradient(closest-side, rgba(82, 148, 255, .34), rgba(59, 165, 255, .12) 46%, transparent 72%);
  opacity: calc((.25 + var(--wp) * .75) * .2);
}

/* ---- products: both renders carry their own alpha, so the wire passes
   behind them without a black plate cutting it ---- */
.thisis__scene {
  grid-row: 1;
  position: relative;
  z-index: 2;
  justify-self: center;
  align-self: center;
}
.thisis__scene img { width: 100%; height: auto; }
/* the vertical offsets are set by thisis-wire.js, which aligns each product
   to the measured rail (40.3% / 45.1% of its box, per the file) */
.thisis__scene--bay   { width: calc(clamp(240px, 25.7vw, 370px) * var(--msc)); }
.thisis__scene--phone { width: calc(clamp(180px, 18.78vw, 271px) * var(--msc)); }

/* ---- captions on shared rows across both columns ---- */
.thisis__prodlogo {
  grid-row: 2;
  justify-self: center;
  align-self: center;
  height: calc(clamp(18px, 1.65vw, 23.7px) * var(--msc));
  width: auto;
  margin-top: clamp(14px, 1.6vw, 22px);
  position: relative;
  z-index: 2;
}
.thisis__prodname {
  grid-row: 2;
  justify-self: center;
  align-self: center;
  margin-top: clamp(14px, 1.6vw, 22px);
  font-size: calc(clamp(1.5rem, 2.22vw, 2rem) * var(--msc));
  font-weight: 640;
  letter-spacing: -.02em;
  line-height: 1;
  color: #fff;
  position: relative;
  z-index: 2;
}
.thisis__col p {
  grid-row: 3;
  justify-self: center;
  max-width: calc(min(424px, 92%) * var(--msc));
  margin-top: clamp(10px, .97vw, 14px);
  font-size: calc(clamp(1rem, 1.25vw, 1.2rem) * var(--msc));
  line-height: 1.6;
  color: rgba(255, 255, 255, .82);
  position: relative;
  z-index: 2;
}
.thisis__col .link-arrow {
  grid-row: 4;
  justify-self: center;
  margin-top: clamp(9px, .85vw, 12px);
  position: relative;
  z-index: 2;
}
/* scene → caption gap halved on desktop/tablet (user 2026-08-19), mirrored
   from Premier's band. Identical pull-up on both captions: they share grid
   row 2, so anything asymmetric knocks the logo and the label out of line.
   Phones keep the stacked spacing below. */
@media (min-width: 701px) {
  .thisis__prodlogo,
  .thisis__prodname { margin-top: calc(clamp(14px, 1.6vw, 22px) - min(2.8vw, 40px)); }
}

/* ---- dot field: the footer's halftone shader pooled around the hub ---- */
.thisis__dots {
  position: absolute;
  z-index: 0;
  pointer-events: none;
}

/* ---- entrance: the driver owns these, not the global reveal observer,
   so the hub can land before the wire and the columns ---- */
/* the reveal rides `translate`, NOT `transform` — the scenes carry their
   rail alignment in `transform`, and a shared property would cancel it */
.thisis__rv {
  opacity: 0;
  translate: 0 28px;
  /* promoted BEFORE the entrance runs: letting the browser create these
     layers on the first animated frame is what makes the reveal stutter */
  will-change: opacity, translate;
  transition: opacity .9s var(--ease) var(--d, 0s),
              translate .9s var(--ease) var(--d, 0s);
}
.thisis__rv.in { opacity: 1; translate: none; }
/* …and released once it has landed, so the layers are not kept forever */
.thisis__stage.is-settled .thisis__rv,
.thisis__stage.is-settled .thisis__cube img { will-change: auto; }


/* one CTA under the band now (user 2026-08-18): the column links and the
   trial footnote came off, so this is just the single button, centred */
.thisis__tail { position: relative; z-index: 2; margin-top: clamp(48px, 7vh, 84px); }
.thisis__tail .plusband__cta { margin-top: 0; display: flex; justify-content: center; }


/* stacked band on phones (user 2026-08-18): cube -> Premier -> App run
   vertically, the wire chaining down between them */
@media (max-width: 700px) {
  .thisis__stage {
    display: flex;
    flex-direction: column;
    align-items: center;
    row-gap: clamp(40px, 11vw, 64px);
  }
  .thisis__cube { order: 1; margin: 0; width: clamp(200px, 56vw, 270px); }
  .thisis__col { display: flex; flex-direction: column; align-items: center; }
  .thisis__col--premier { order: 2; }
  .thisis__col--app { order: 3; margin-top: clamp(24px, 8vw, 48px); }
  .thisis__col--premier .thisis__prodlogo { margin-top: clamp(-34px, -8vw, -20px); } /* hugs the bay render */
  .thisis__scene--bay { width: min(72vw, 330px); }
  .thisis__scene--phone { width: min(56vw, 255px); }
  .thisis__col p { max-width: 30ch; }
}

/* sub header appears on scroll only, on tablet/phone (user 2026-08-18) */
@media (max-width: 1024px) {
  .localnav {
    transform: translateY(-14px);
    opacity: 0;
    pointer-events: none;
    transition: opacity .35s var(--ease), transform .35s var(--ease);
  }
  .localnav.scrolled {
    transform: none;
    opacity: 1;
    pointer-events: auto;
  }
}

/* return-scroll global header on tablet/phone (user 2026-08-18) */
@media (max-width: 1024px) {
  .gnav--fixed {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 70;
    transform: translateY(-100%);
    transition: transform .35s var(--ease);
  }
  .gnav--fixed.gnav--reveal { transform: translateY(0); }
}
