/* ==========================================================================
   Revenue Jump — variant C: $1 opt-in page
   Capri (Studio Zash / Shopify Dawn) design system, Heartcore brand.

   From Capri: 1600px width, pill buttons (radius 40), cards at radius 40 with
   1px borders, zero shadows anywhere, flush colour bands, heading scale 140 /
   body 110, blob image masks, marquee band, reveal-on-scroll.

   From the Heartcore brand guide: Navy + Gold + Teal only, F37 Bergman /
   F37 Moon / Work Sans, left-aligned.

   DELIBERATE EXCEPTION: the "AS FEATURED IN" bar is centred, per Shanda's
   art direction (kicker centred, logos evenly spaced, Forbes and Inc
   anchoring the outer positions). Everything else stays left-aligned.
   ========================================================================== */

/* ---------- Fonts ---------- */

@font-face {
  font-family: "F37 Bergman";
  src: url("../fonts/F37Bergman-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "F37 Moon";
  src: url("../fonts/F37Moon-ExtraBold.woff2") format("woff2");
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

/* ---------- Tokens ---------- */

:root {
  --navy: #003a4a;
  --gold: #fcd271;
  --teal: #afe1da;
  --teal-deep: #0094aa;

  --cream: #f7f2ec;
  --cream-deep: #efe7dd;
  --white: #ffffff;

  --navy-80: rgba(0, 58, 74, 0.8);
  --navy-55: rgba(0, 58, 74, 0.55);
  --navy-15: rgba(0, 58, 74, 0.15);
  --white-72: rgba(255, 255, 255, 0.72);
  --white-24: rgba(255, 255, 255, 0.24);

  --display: "F37 Bergman", Georgia, serif;
  --subhead: "F37 Moon", "Work Sans", system-ui, sans-serif;
  --body: "Work Sans", system-ui, -apple-system, "Segoe UI", sans-serif;

  --maxw: 1600px;
  --readable: 1000px;
  --gutter: clamp(1.25rem, 4vw, 5rem);
  --section: clamp(3.5rem, 6vw, 6.5rem);
  --pill: 999px;
  --card-radius: 40px;
}

/* ---------- Base ---------- */

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--cream);
  color: var(--navy);
  font-family: var(--body);
  font-size: clamp(1.0625rem, 1rem + 0.25vw, 1.1875rem);
  line-height: 1.6;
  text-align: left;
  -webkit-font-smoothing: antialiased;
}

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

a {
  color: inherit;
}

button {
  font: inherit;
  color: inherit;
}

:focus-visible {
  outline: 3px solid var(--teal-deep);
  outline-offset: 3px;
}

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

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.wrap--readable {
  max-width: var(--readable);
}

.band {
  padding-block: var(--section);
  scroll-margin-top: 72px;
}

.band--tight {
  padding-block: clamp(2.25rem, 4vw, 3.5rem);
}

.band--cream {
  background: var(--cream);
}

.band--cream-deep {
  background: var(--cream-deep);
}

.band--navy {
  background: var(--navy);
  color: var(--white);
}

.band--navy p {
  color: var(--white-72);
}

.band--gold {
  background: var(--gold);
  color: var(--navy);
}

/* ---------- Typography ---------- */

h1,
h2,
h3 {
  margin: 0;
  font-weight: 400;
  text-wrap: balance;
}

.display-xl,
.display-l,
.display-m {
  font-family: var(--display);
  line-height: 1.04;
  letter-spacing: -0.02em;
}

.display-xl {
  font-size: clamp(2.5rem, 1.5rem + 4.4vw, 5.25rem);
}

.display-l {
  font-size: clamp(2.125rem, 1.45rem + 3vw, 4rem);
}

.display-m {
  font-size: clamp(1.75rem, 1.35rem + 1.9vw, 2.75rem);
}

.subhead {
  font-family: var(--subhead);
  font-weight: 800;
  line-height: 1.3;
  font-size: clamp(1.0625rem, 0.98rem + 0.5vw, 1.375rem);
}

.eyebrow {
  font-family: var(--body);
  font-weight: 600;
  font-size: 0.75rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  margin: 0 0 1.25rem;
  color: var(--teal-deep);
}

.eyebrow--light {
  color: var(--teal);
}

.lead {
  font-size: clamp(1.125rem, 1.05rem + 0.4vw, 1.375rem);
  line-height: 1.5;
}

p {
  margin: 0 0 1.25rem;
  max-width: 68ch;
}

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

strong {
  font-weight: 600;
}

em {
  font-style: italic;
}

.measure {
  max-width: 68ch;
}

.mt {
  margin-top: clamp(1.75rem, 3.5vw, 2.75rem);
}

/* ---------- Buttons — Capri pill, 1px border, flat ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  padding: 1.25rem 2.75rem;
  border: 1px solid var(--teal);
  border-radius: var(--pill);
  background: var(--teal);
  color: var(--navy);
  font-family: var(--body);
  font-weight: 700;
  font-size: clamp(0.8125rem, 0.78rem + 0.2vw, 0.9375rem);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  text-align: center;
  cursor: pointer;
  transition: background-color 0.25s ease, color 0.25s ease,
    border-color 0.25s ease;
}

.btn:hover {
  background: var(--teal-deep);
  border-color: var(--teal-deep);
  color: var(--white);
}

.btn--outline {
  background: transparent;
  border-color: var(--navy);
  color: var(--navy);
}

.btn--outline:hover {
  background: var(--navy);
  border-color: var(--navy);
  color: var(--white);
}

.btn--sm {
  padding: 0.7rem 1.5rem;
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
}

/* The primary offer button is deliberately large — it is the only thing on
   the page being asked for. */
.btn--offer {
  font-size: clamp(0.875rem, 0.8rem + 0.4vw, 1.0625rem);
  padding: 1.4rem 3rem;
}

@media (max-width: 560px) {
  .btn--offer {
    width: 100%;
  }
}

/* The offer button is the whole point of the page, so it gets real breathing
   room rather than sitting hard against the block above and below it. This
   owns the spacing - do not also put .mt on a .cta-block. */
.cta-block {
  margin-block: clamp(2.75rem, 5.5vw, 4.5rem);
}

/* v4: both lines under the button were "so tiny" in Shanda's review, so they
   step up from 15px/14px to roughly 20px/18px and both carry real weight.
   Full navy rather than the 80%/55% tints — at this size the faded greys read
   as disabled text. They still sit below the button in the hierarchy because
   the button is 40px+ display type. */
.cta-note {
  margin: 1.5rem 0 0;
  font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.25rem);
  font-weight: 700;
  line-height: 1.4;
  color: var(--navy);
}

.band--navy .cta-note {
  color: var(--white);
}

.cta-urgency {
  margin: 0.65rem 0 0;
  font-size: clamp(0.9375rem, 0.9rem + 0.3vw, 1.125rem);
  font-weight: 600;
  line-height: 1.45;
  color: var(--navy-80);
}

.band--navy .cta-urgency {
  color: var(--white-72);
}

/* ---------- Nav ---------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--cream);
  border-bottom: 1px solid var(--navy-15);
}

.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  min-height: 68px;
  padding-block: 0.7rem;
}

.nav__brand {
  font-family: var(--subhead);
  font-weight: 800;
  font-size: 0.8125rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--navy);
}

.nav__brand span {
  color: var(--teal-deep);
}

/* ---------- Authority bar ----------
   Centred by explicit art direction. Logos evenly spaced across the full
   width so the outer positions (Forbes, Inc) carry the most weight. */

/* v4: squished. Shanda's note was "one tight strip, no breathing room above
   or below... currently there's too much white space around it." Outer padding
   roughly halves, the kicker and trust line pull in close, and .band--hero
   below cuts the gap between this strip and the video/offer block. */
.authority {
  background: var(--white);
  border-bottom: 1px solid var(--navy-15);
  padding-block: clamp(0.9rem, 1.6vw, 1.25rem);
  text-align: center;
}

.authority__kicker {
  margin: 0 0 0.6rem;
  font-family: var(--body);
  font-weight: 600;
  font-size: 0.6875rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--navy-55);
  max-width: none;
}

.authority__logos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: clamp(1.25rem, 4vw, 3rem);
  margin: 0 auto;
  padding: 0;
  list-style: none;
  max-width: 1100px;
}

.authority__logos li {
  flex: 1 1 auto;
  display: flex;
  justify-content: center;
}

/* Grayscale, editorial weight — swap these for official logo SVGs. */
.authority__logo {
  font-family: var(--display);
  font-size: clamp(1.25rem, 0.9rem + 1.5vw, 2.125rem);
  line-height: 1;
  letter-spacing: 0.02em;
  color: #6f6f6f;
  filter: grayscale(1);
  opacity: 0.85;
}

.authority__trust {
  margin: 0.7rem auto 0;
  max-width: 62ch;
  font-size: 0.875rem;
  line-height: 1.45;
  color: var(--navy-55);
}

/* ---------- Video section ---------- */

/* v4: the offer has to sit closer to the top. The default --section padding
   put ~6.5rem of cream between the credibility strip and the headline; this
   cuts the top side only, so the rhythm below the section is untouched. */
.band--hero {
  padding-block-start: clamp(1.75rem, 3vw, 2.75rem);
}

/* Heading and the vertical clip sit side by side on desktop; the video drops
   below the copy on narrow screens, which is the natural DOM order. */
/* v4: top-aligned, not centred. The 9:16 player is ~700px tall while the copy
   column got shorter when the long headline was replaced — centring dropped
   the h1 a couple of hundred pixels down the page and put dead cream above it,
   which is the white space Shanda was objecting to. Top alignment starts the
   headline level with the top of the video.

   Row gap is 0 by design: the support and micro paragraphs carry their own
   margin-top, and a grid row-gap on top of that would double the spacing. All
   vertical rhythm in here comes from the elements themselves. */
.video-split {
  display: grid;
  column-gap: clamp(2rem, 4vw, 4.5rem);
  row-gap: 0;
  align-items: start;
}

/* Stacked, the player needs the breathing room the old row gap used to give
   it. Reset on desktop, where it sits in its own column. */
.video-split__media {
  margin-top: clamp(2rem, 4vw, 4.5rem); /* same value the old row gap used */
}

/* Desktop: copy stacks down column 1 — headline, subhead, callout, then the
   support and micro paragraphs — while the player spans all three rows of
   column 2 beside it.

   Before this, .video-support and .video-micro were siblings BELOW the grid,
   so they cleared the full height of the ~700px player and left a large dead
   gap under the callout. The two columns now run to roughly equal height.

   The span-3 assumes three rows in column 1. If a paragraph is ever removed
   from this block, drop the span to match or column 2 gains an empty row.

   Row 4 is the first CTA, which sits inside this grid for the sake of the
   mobile stacking order - see the .cta-block rule at the end of this block
   before renumbering anything. */
@media (min-width: 950px) {
  .video-split {
    grid-template-columns: 1.1fr 0.9fr;
  }

  .video-split__copy {
    grid-column: 1;
    grid-row: 1;
  }

  .video-split__media {
    grid-column: 2;
    grid-row: 1 / span 3;
    margin-top: 0;
  }

  .video-split .video-support {
    grid-column: 1;
    grid-row: 2;
  }

  .video-split .video-micro {
    grid-column: 1;
    grid-row: 3;
  }

  /* The first CTA lives INSIDE this grid purely so that mobile stacks it
     directly under the player, which is what Shanda asked for: stacked, the DOM
     order is the visual order, so no mobile CSS is needed at all.

     Desktop has to undo that nesting - it belongs full width beneath both
     columns, exactly where it sat when it was a sibling of the grid. Row 4 is
     below the three column-1 rows and clear of the player, which still spans
     rows 1-3 of column 2.

     Spacing comes from .cta-block's own margin-block; the grid sets
     row-gap: 0 so elements own their rhythm. */
  .video-split .cta-block {
    grid-column: 1 / -1;
    grid-row: 4;
  }
}

.video-head {
  max-width: 20ch;
}

.video-sub {
  margin: 1.5rem 0 0;
  max-width: 46ch;
  color: var(--navy-80);
}

/* v4: the bandwidth / stop-convincing message, moved up under the subhead and
   bolded. Body face rather than the F37 subhead face so it reads as Shanda
   talking rather than as a second headline competing with the h1. The teal
   rule is the spotlight colour the brand guide reserves for action items,
   used here as a marker rather than as fill. */
.video-callout {
  margin: 1.5rem 0 0;
  max-width: 44ch;
  padding-left: clamp(0.9rem, 1.5vw, 1.15rem);
  border-left: 4px solid var(--teal);
  font-family: var(--body);
  font-weight: 700;
  font-size: clamp(1.0625rem, 0.98rem + 0.5vw, 1.3125rem);
  line-height: 1.4;
  color: var(--navy);
}

/* Left-aligned like everything else, but capped so the player never gets
   absurdly wide on a 1600px container. */
.video {
  position: relative;
  margin: clamp(2.25rem, 4vw, 3.25rem) 0 0;
  max-width: var(--readable);
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--card-radius);
  border: 1px solid var(--navy-15);
  background: var(--navy);
}

/* The West Palm Beach clip is shot vertical, so the player is 9:16 and
   width-capped rather than filling the column. Swap this class for the
   default 16:9 .video if a landscape cut is ever used instead. */
.video--vertical {
  aspect-ratio: 9 / 16;
  width: min(400px, 100%);
  max-width: none;
  margin: 0;
}

.video > iframe,
.video > video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  /* object-fit only affects a <video>; the Vimeo iframe letterboxes itself.
     contain, not cover, so a source that is not exactly 9:16 shows letterbox
     bars against the navy rather than cropping into the speaker. */
  object-fit: contain;
  background: var(--navy);
}

.video-support {
  margin: clamp(1.75rem, 3vw, 2.5rem) 0 0;
  max-width: 62ch;
}

.video-micro {
  margin: 1.25rem 0 0;
  max-width: 68ch;
  color: var(--navy-80);
}

/* ---------- Steps ---------- */

.steps {
  display: grid;
  gap: clamp(1.25rem, 2.5vw, 2rem);
  margin: clamp(2.5rem, 5vw, 3.5rem) 0 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 900px) {
  .steps {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.step {
  padding: clamp(1.75rem, 3vw, 2.5rem);
  border: 1px solid var(--navy-15);
  border-radius: var(--card-radius);
}

.band--navy .step {
  border-color: var(--white-24);
}

.step__num {
  display: block;
  margin-bottom: 0.85rem;
  font-family: var(--body);
  font-weight: 700;
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--teal-deep);
}

.band--navy .step__num {
  color: var(--teal);
}

.step h3 {
  margin: 0 0 0.9rem;
  font-family: var(--subhead);
  font-weight: 800;
  font-size: clamp(1.125rem, 1.05rem + 0.4vw, 1.375rem);
  line-height: 1.25;
}

.step p {
  margin: 0;
  font-size: 0.9875rem;
  line-height: 1.6;
  color: var(--navy-80);
}

.band--navy .step p {
  color: var(--white-72);
}

/* The "PLUS" callout — paired with a blob-masked photo */
.plus {
  display: grid;
  gap: clamp(2rem, 4vw, 4rem);
  align-items: center;
  margin: clamp(2rem, 4vw, 3rem) 0 0;
}

@media (min-width: 900px) {
  .plus {
    grid-template-columns: 0.85fr 1.15fr;
  }
}

.plus__tag {
  display: inline-block;
  margin: 0 0 1rem;
  padding: 0.4rem 1rem;
  border-radius: var(--pill);
  background: var(--gold);
  color: var(--navy);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

/* ---------- Blob-masked media ---------- */

.blob {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--blob-image, none) center / cover no-repeat var(--teal);
  clip-path: polygon(var(--blob-1));
}

.blob--3 {
  clip-path: polygon(var(--blob-3));
}

.blob--5 {
  clip-path: polygon(var(--blob-5));
}

.ph {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  padding: 0.45rem 0.9rem;
  border-radius: var(--pill);
  background: var(--gold);
  color: var(--navy);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* ---------- Confirmation page ---------- */

/* Enrolment progress. Gold rather than teal on purpose: teal is the brand's
   action colour and is reserved for buttons, and a half-full gold bar reads as
   "unfinished" instead of as something to click. */
.progress {
  max-width: var(--readable);
}

.progress__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.7rem;
}

.progress__label {
  margin: 0;
  font-family: var(--subhead);
  font-weight: 800;
  font-size: clamp(1rem, 0.95rem + 0.35vw, 1.25rem);
  color: var(--navy);
}

.progress__pct {
  margin: 0;
  font-family: var(--body);
  font-weight: 700;
  font-size: 0.9375rem;
  color: var(--navy-55);
  font-variant-numeric: tabular-nums;
}

.progress__track {
  height: 12px;
  border-radius: var(--pill);
  background: var(--navy-15);
  overflow: hidden;
}

/* The bar is driven by --progress so the width, the printed percentage and
   aria-valuenow can never drift apart: set the custom property once on
   .progress and the number next to it in the markup. 50% is the default
   because /confirmation/ is the page that has always used it.

   The funnel reads: 25% after paying (variant B's step 2), 50% on the
   confirmation page, 100% once they are in the WhatsApp group - which we
   cannot detect, so nothing on the site ever shows it. */
.progress__fill {
  display: block;
  width: var(--progress, 50%);
  height: 100%;
  border-radius: var(--pill);
  background: var(--gold);
}

.progress__hint {
  margin: 0.7rem 0 0;
  font-size: 0.9375rem;
  color: var(--navy-55);
}

/* Copy beside the QR on desktop, stacked below it on narrow screens. */
.confirm-split {
  display: grid;
  column-gap: clamp(2rem, 4vw, 4.5rem);
  row-gap: clamp(2rem, 4vw, 3rem);
  align-items: start;
}

@media (min-width: 950px) {
  .confirm-split {
    grid-template-columns: 1.25fr 0.75fr;
  }
}

.confirm-sub {
  margin: 1.5rem 0 0;
  max-width: 46ch;
  color: var(--navy-80);
}

.confirm-lead {
  margin: 1.25rem 0 0;
  max-width: 58ch;
}

.checklist {
  margin: clamp(1.5rem, 3vw, 2rem) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0.85rem;
  max-width: 58ch;
}

.checklist li {
  position: relative;
  padding-left: 2.1rem;
  line-height: 1.5;
  color: var(--navy);
}

/* Drawn rather than an emoji: a green check would be a fourth colour, and
   emoji rendering varies wildly across platforms. */
.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.15em;
  width: 1.4rem;
  height: 1.4rem;
  border-radius: var(--pill);
  background: var(--teal);
  /* tick */
  background-image: linear-gradient(45deg, transparent 45%, var(--navy) 45%, var(--navy) 55%, transparent 55%),
                    linear-gradient(-45deg, transparent 45%, var(--navy) 45%, var(--navy) 55%, transparent 55%);
  background-size: 0.42rem 0.7rem, 0.62rem 0.7rem;
  background-position: 0.36rem 0.42rem, 0.5rem 0.3rem;
  background-repeat: no-repeat;
}

.qr {
  border: 1px solid var(--navy-15);
  border-radius: var(--card-radius);
  padding: clamp(1.1rem, 2.2vw, 1.5rem);
  background: var(--white);
  max-width: 400px;
}

.qr__kicker {
  margin: 0 0 0.75rem;
  font-family: var(--body);
  font-weight: 600;
  font-size: 0.75rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--teal-deep);
}

/* The source is a whole phone screenshot — mostly green padding with the QR
   card in the middle. This crops to the card instead of showing the screen:
   the box is landscape-ish, the image is 0.46:1, so cover fills the width and
   object-position picks the vertical slice holding the card. Swap in a tighter
   export and both properties can go. */
/* The group QR export (1170x1172) is already cropped to the card, so it is
   shown whole. This replaced a block of zoom/offset maths that existed purely
   because the previous asset was a full-length phone screenshot, mostly
   padding, which shrank the QR to the point of being awkward to scan. The new
   one renders the code roughly a third larger in the same space.

   If a future QR arrives as an untrimmed screenshot again, crop the file
   rather than reinstating CSS to hide the padding. */
.qr__shot {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 20px;
}

.qr__note {
  margin: 0.85rem 0 0;
  font-size: 0.875rem;
  line-height: 1.45;
  color: var(--navy-55);
}

/* Inline placeholder badge — used on the confirmation page so unfinished copy
   cannot ship unnoticed. Same gold as .ph, but flows inline with text. */
.ph-note {
  display: inline-block;
  margin-right: 0.6rem;
  padding: 0.2rem 0.6rem;
  border-radius: var(--pill);
  background: var(--gold);
  color: var(--navy);
  font-family: var(--body);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  vertical-align: middle;
}

/* ---------- Whop checkout dialog ----------
   Whop's embedded checkout only mounts inline, so the pop-up is ours. Native
   <dialog> gives focus trapping, Esc, and the backdrop for free.

   The dialog element itself is the full-viewport layer — it stays transparent
   and does the centring, and .checkout__panel is the visible card. That split
   is what makes the click-outside-to-close test in main.js work: a click on
   the dialog is a click on the backdrop, a click on the panel is not. */

.checkout {
  width: 100%;
  max-width: none;
  max-height: none;
  height: 100%;
  margin: 0;
  padding: clamp(1rem, 3vw, 2rem);
  border: 0;
  background: transparent;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.checkout::backdrop {
  background: rgba(0, 58, 74, 0.72); /* navy, not black — stays on brand */
}

.checkout[open] {
  display: flex;
  align-items: flex-start;
  justify-content: center;
}

.checkout__panel {
  width: 100%;
  max-width: 480px;
  margin: auto; /* centres vertically when short, top-aligns when tall */
  border-radius: var(--card-radius);
  border: 1px solid var(--navy-15);
  background: var(--white);
  overflow: hidden; /* keeps the iframe inside the 40px corners */
}

.checkout__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: clamp(1.1rem, 2.2vw, 1.5rem) clamp(1.25rem, 2.5vw, 1.75rem);
  border-bottom: 1px solid var(--navy-15);
}

.checkout__title {
  margin: 0;
  font-family: var(--subhead);
  font-weight: 800;
  font-size: clamp(1.0625rem, 1rem + 0.3vw, 1.25rem);
  line-height: 1.25;
  color: var(--navy);
}

.checkout__close {
  flex: 0 0 auto;
  width: 2.25rem;
  height: 2.25rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--navy-15);
  border-radius: var(--pill);
  background: transparent;
  color: var(--navy);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.checkout__close:hover,
.checkout__close:focus-visible {
  background: var(--cream);
}

/* Order bump. Gold rather than teal: teal is reserved for the primary action
   (the pay button inside the iframe), and gold is the brand's attention colour
   without competing with it. The whole block is a <label>, so the copy is as
   clickable as the box. */
.bump {
  display: flex;
  gap: 0.9rem;
  align-items: flex-start;
  margin: clamp(1.1rem, 2.2vw, 1.5rem) clamp(1.25rem, 2.5vw, 1.75rem) 0;
  padding: clamp(0.9rem, 1.8vw, 1.15rem);
  border: 2px dashed var(--gold);
  border-radius: 20px;
  background: rgba(252, 210, 113, 0.12);
  cursor: pointer;
}

.bump:hover,
.bump:focus-within {
  background: rgba(252, 210, 113, 0.22);
}

.bump__box {
  flex: 0 0 auto;
  width: 1.35rem;
  height: 1.35rem;
  margin: 0.15rem 0 0;
  accent-color: var(--navy);
  cursor: pointer;
}

.bump__body {
  display: block;
}

.bump__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}

.bump__title {
  font-family: var(--subhead);
  font-weight: 800;
  font-size: clamp(0.9375rem, 0.9rem + 0.25vw, 1.0625rem);
  line-height: 1.3;
  color: var(--navy);
}

.bump__price {
  flex: 0 0 auto;
  font-family: var(--body);
  font-weight: 700;
  font-size: 1.0625rem;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}

.bump__copy {
  display: block;
  margin-top: 0.4rem;
  font-size: 0.875rem;
  line-height: 1.45;
  color: var(--navy-80);
}

/* ---------- VIP Room page (/vip/) ----------
   Copy left, checkout right on desktop. Stacked, the copy comes FIRST and the
   buy box sits at the bottom — this is a warm audience being pitched an
   upgrade they already declined once, so they need the argument before the
   form. That is just DOM order, so there is no `order` override here; an
   earlier version put the buy box first on mobile and it was wrong. */

.vip-split {
  display: grid;
  column-gap: clamp(2rem, 4vw, 4.5rem);
  row-gap: clamp(2rem, 4vw, 3rem);
  align-items: start;
}

@media (min-width: 950px) {
  .vip-split {
    grid-template-columns: 1.05fr 0.95fr;
  }
  .vip-split__copy {
    grid-column: 1;
    grid-row: 1;
  }
  .buybox {
    grid-column: 2;
    grid-row: 1;
  }
}

.vip-lead {
  margin: 1.5rem 0 0;
  max-width: 58ch;
}

.vip-body {
  margin: 1.25rem 0 0;
  max-width: 58ch;
  color: var(--navy-80);
}

.vip-list {
  margin-top: clamp(1.5rem, 3vw, 2rem);
}

/* The buy box. Same card language as the checkout dialog panel so the two
   routes to this product feel like one product. */
.buybox {
  border: 1px solid var(--navy-15);
  border-radius: var(--card-radius);
  background: var(--white);
  overflow: hidden;
}

.buybox__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: clamp(1.1rem, 2.2vw, 1.5rem) clamp(1.25rem, 2.5vw, 1.75rem);
  border-bottom: 1px solid var(--navy-15);
}

.buybox__title {
  margin: 0;
  font-family: var(--subhead);
  font-weight: 800;
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.1875rem);
  color: var(--navy);
}

.buybox__price {
  margin: 0;
  font-family: var(--body);
  font-weight: 700;
  font-size: 1.375rem;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}

/* Matches .checkout__mount — stops the card collapsing to a sliver before the
   Whop iframe reports its height. */
.buybox__mount {
  min-height: 420px;
}

.buybox__mount iframe {
  display: block;
  width: 100%;
  border: 0;
}

/* Order total. The only total on screen — Whop's own row is suppressed with
   hide-price, because it cannot know about the bump. */
.ordertotal {
  margin: clamp(1rem, 2vw, 1.25rem) clamp(1.25rem, 2.5vw, 1.75rem) 0;
  font-family: var(--body);
  font-size: 0.9375rem;
  color: var(--navy-80);
}

.ordertotal__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.3rem 0;
}

.ordertotal__row[hidden] {
  display: none; /* [hidden] loses to display:flex without this */
}

.ordertotal__amt {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.ordertotal__row--sum {
  margin-top: 0.35rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--navy-15);
  font-weight: 700;
  color: var(--navy);
}

.ordertotal__row--sum .ordertotal__amt {
  font-size: 1.25rem;
}

.ordertotal__note {
  margin: 0.6rem 0 0;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--navy-55);
}

/* Whop injects its iframe in here. Min-height stops the panel collapsing to a
   sliver in the moment between the dialog opening and the iframe reporting
   its height. */
.checkout__mount {
  min-height: 420px;
}

.checkout__mount iframe {
  display: block;
  width: 100%;
  border: 0;
}

/* ---------- OPEN BUG: the phone-number country picker on touch --------------
   Do not "fix" this by constraining the iframe height. That was tried on
   2026-08-17 and is reverted below the explanation.

   THE BUG. Whop's country picker is a Radix Select, and Radix sizes its dropdown
   against the viewport of the document it lives in - which here is the iframe,
   not the screen. Whop's embed script measures its own content and writes an
   inline height onto the iframe (1093px in a 911px-tall window, proportionally
   worse on a phone), so the iframe is taller than the screen and OUR dialog is
   what scrolls. On a phone the country list is therefore laid out ~1300px tall
   inside a viewport the visitor can only see ~600px of, and the part of it that
   scrolls sits off-screen. A mouse wheel still reaches it, which is why this
   looks fine on desktop. A finger does not: a cross-origin iframe will not chain
   a touch scroll out to the parent page.

   WHAT WAS TRIED AND FAILED. Capping .checkout__panel at the dialog height and
   forcing the iframe to height:100% so the IFRAME scrolls instead of the dialog.
   The geometry is correct and it measures clean in desktop Chrome - iframe drops
   from 1093px to 760px, fully on screen, with its own scrollbar. On real iOS
   Safari it is worse than the bug it fixes: Whop's form is clipped partway down
   (nothing below "Address line 1" renders) and the frame stops scrolling early,
   because forcing a height after Whop has already laid out leaves the frame's
   scroll extent stale and iOS does not recompute it. Verified on an iPhone.

   Any real fix has to stop the country list being sized against a viewport the
   visitor cannot see, WITHOUT us resizing Whop's iframe underneath it. The most
   promising route is Whop's hosted checkout on touch devices, where the document
   viewport IS the screen - but that moves rjCheckoutComplete's work (upsell arm,
   Purchase event, receipt id) onto the return leg, so it is a revenue-path
   change, not a stylesheet one. */

/* ---------- Post-payment state ----------
   Replaces the entire checkout panel the moment Whop reports completion, so
   the card fields cannot be mistaken for still needing input. Built in JS
   (see showCheckoutSuccess in main.js) because it takes over the panel. */
.checkout__done {
  padding: 2.5rem 2rem;
  text-align: center;
}

.checkout__done-title {
  margin: 0 0 0.5rem;
  font-family: var(--subhead);
  font-weight: 800;
  font-size: clamp(1.375rem, 1.2rem + 0.8vw, 1.75rem);
  line-height: 1.25;
  color: var(--navy);
}

.checkout__done-copy {
  margin: 0 0 1.75rem;
  color: var(--navy-55);
  line-height: 1.5;
}

.checkout__done-btn {
  width: 100%;
}

/* ---------- Marquee ---------- */

.marquee {
  overflow: hidden;
  padding-block: clamp(0.9rem, 1.6vw, 1.35rem);
  border-block: 1px solid var(--navy-15);
  background: var(--gold);
  color: var(--navy);
}

.marquee--navy {
  background: var(--navy);
  color: var(--white);
  border-color: var(--white-24);
}

.marquee__track {
  display: flex;
  width: max-content;
  animation: marquee-scroll 32s linear infinite;
}

.marquee:hover .marquee__track {
  animation-play-state: paused;
}

.marquee__item {
  display: inline-flex;
  align-items: center;
  gap: clamp(1.5rem, 3vw, 3rem);
  padding-inline: clamp(0.75rem, 1.5vw, 1.5rem);
  font-family: var(--subhead);
  font-weight: 800;
  font-size: clamp(0.875rem, 0.8rem + 0.45vw, 1.25rem);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
}

.marquee__item::after {
  content: "\25C6";
  font-size: 0.6em;
  opacity: 0.6;
}

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

/* ---------- "As heard on" strip ---------- */

.heard {
  background: var(--cream-deep);
  border-block: 1px solid var(--navy-15);
  padding-block: clamp(1.5rem, 3vw, 2.25rem);
}

.heard__kicker {
  margin: 0 0 0.9rem;
  font-family: var(--body);
  font-weight: 700;
  font-size: 0.6875rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--teal-deep);
  max-width: none;
}

.heard__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: clamp(0.875rem, 0.84rem + 0.2vw, 1rem);
  color: var(--navy-80);
}

.heard__list li {
  display: inline-flex;
  align-items: center;
  gap: 1.25rem;
}

.heard__list li:not(:last-child)::after {
  content: "\2022";
  color: var(--navy-15);
}

/* ---------- Testimonials — screenshot mosaic ----------
   Seven real customer screenshots at wildly different aspect ratios (5.89:1
   down to 0.46:1). Multi-column rather than a grid of equal tiles, because
   equal tiles would mean cropping or letterboxing every one of them; here each
   screenshot keeps its native proportions and the columns simply flow around
   the differences.

   Column width is the whole design constraint. The breakpoints below are the
   same ones the old .quotes grid used, so the section keeps the full wrap
   width it had before and lands at ~466px columns on a wide screen — near
   enough identical to the old cards. Every screenshot is 574px native or
   wider, so at that column width they all downscale and stay sharp.

   The one exception is lisa-taylor.png (1306px native) inside a full-width
   strip: past about a 1500px viewport the strip outgrows it and it upscales by
   under 10%, which is not visible. Everything else downscales at every size. */

.shots {
  columns: 1;
  column-gap: clamp(1rem, 1.8vw, 1.35rem);
  margin: clamp(2.5rem, 5vw, 3.5rem) 0 0;
}

@media (min-width: 680px) {
  .shots {
    columns: 2;
  }
}

@media (min-width: 1150px) {
  .shots {
    columns: 3;
  }
}

.shot {
  /* Multi-column will happily slice an element across a column break; this
     keeps each screenshot whole. */
  break-inside: avoid;
  margin: 0 0 clamp(0.9rem, 1.8vw, 1.35rem);
  border-radius: 24px;
  overflow: hidden;
  border: 1px solid var(--white-24);
  /* The screenshots carry their own light and dark backgrounds — they are real
     captures, not artwork. No card padding behind them: a white mat would fight
     the dark ones and vice versa. Border + radius is enough to read them as
     deliberate tiles against the navy. */
  background: var(--navy);
  line-height: 0;
}

.shot img {
  display: block;
  width: 100%;
  height: auto;
}

/* The two ultra-wide strips (5.89:1 and 4.11:1). At half width their text
   would land around 65px tall and be illegible, so they break the full width
   of the mosaic instead. column-span works in every current browser; where it
   does not, they simply stay in-column and the layout still holds. */
.shot--full {
  column-span: all;
}

/* ---------- Testimonials — original text version ----------
   Unused while the screenshot mosaic is live: the markup is parked in a
   <template> in index.html. Kept so the styling still exists if Shanda wants
   the typeset quotes back. */

.quotes {
  display: grid;
  gap: clamp(1rem, 1.8vw, 1.35rem);
  margin: clamp(2.25rem, 4vw, 3rem) 0 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 680px) {
  .quotes {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1150px) {
  .quotes {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.quote {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  padding: clamp(1.4rem, 2.2vw, 1.9rem);
  border: 1px solid var(--white-24);
  border-radius: var(--card-radius);
}

.quote__text {
  margin: 0;
  font-size: 1.0625rem;
  line-height: 1.5;
  color: var(--white);
}

.quote__who {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin-top: auto;
}

.quote__avatar {
  flex: none;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--teal);
  color: var(--navy);
  display: grid;
  place-items: center;
  font-family: var(--subhead);
  font-weight: 800;
  font-size: 0.875rem;
  background-size: cover;
  background-position: center;
}

.quote__name {
  margin: 0;
  font-family: var(--subhead);
  font-weight: 800;
  font-size: 0.9375rem;
  line-height: 1.2;
  color: var(--white);
}

.quote__role {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--white-72);
}

/* Placeholder treatment — must be obvious so it cannot ship by accident */
.quote--placeholder {
  border-style: dashed;
}

.quote__flag {
  align-self: flex-start;
  padding: 0.25rem 0.7rem;
  border-radius: var(--pill);
  background: var(--gold);
  color: var(--navy);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.quote--empty {
  align-items: flex-start;
  justify-content: center;
  min-height: 190px;
  color: var(--white-72);
  font-style: italic;
}

/* ---------- Live purchase proof widget ----------
   Hidden until real purchase data is supplied — see js/main.js. */

.proof {
  position: fixed;
  left: clamp(0.75rem, 2vw, 1.5rem);
  bottom: clamp(0.75rem, 2vw, 1.5rem);
  z-index: 70;
  width: min(340px, calc(100vw - 1.5rem));
  padding: 1rem 1.1rem;
  border: 1px solid var(--navy-15);
  border-radius: 24px;
  background: var(--white);
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.4s ease, transform 0.4s ease;
  pointer-events: none;
}

.proof[data-shown="true"] {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.proof[hidden] {
  display: none;
}

.proof__head {
  margin: 0 0 0.5rem;
  padding-right: 1.5rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--teal-deep);
  max-width: none;
}

.proof__row {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  font-size: 0.875rem;
  line-height: 1.4;
}

.proof__dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--teal-deep);
  transform: translateY(-1px);
}

.proof__text {
  margin: 0;
  max-width: none;
}

.proof__time {
  display: block;
  font-size: 0.75rem;
  color: var(--navy-55);
}

.proof__close {
  position: absolute;
  top: 0.5rem;
  right: 0.55rem;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--navy-55);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

.proof__close:hover {
  color: var(--navy);
}

/* ---------- Footer ---------- */

.footer {
  background: var(--navy);
  color: var(--white-72);
  border-top: 1px solid var(--white-24);
  padding-block: 2.5rem;
  font-size: 0.8125rem;
}

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  align-items: center;
  justify-content: space-between;
}

.footer a {
  color: var(--teal);
  text-decoration: none;
}

.footer a:hover {
  text-decoration: underline;
}

.footer p {
  margin: 0;
  max-width: none;
}

/* ---------- Reveal on scroll ----------
   Visible by default; only hidden once JS confirms it can reveal it again. */

.js-anim .reveal {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.7s ease, transform 0.7s ease;
}

.js-anim .reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* ---------- Utilities ---------- */

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.85rem 1.25rem;
  background: var(--teal);
  color: var(--navy);
  font-weight: 700;
  text-decoration: none;
}

.skip-link:focus {
  left: 0;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .js-anim .reveal {
    opacity: 1;
    transform: none;
  }
}

/* ---------- One-click upsell (/step-2-k2r3/) ----------
   The decline link is a real, readable link, not hidden micro-text. A one-time
   offer people cannot decline produces refunds and chargebacks, not revenue. */
.upsell__decline {
  margin: 1.5rem 0 0;
  font-size: 1rem;
}

.upsell__decline a {
  color: var(--navy-55);
  text-decoration: underline;
}

.upsell__decline a:hover {
  color: var(--navy);
}

.upsell__error {
  margin: 1rem 0 0;
  padding: 0.85rem 1rem;
  border-radius: 16px;
  background: rgba(252, 210, 113, 0.35);
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--navy);
}

.upsell__error[hidden] {
  display: none;
}

/* On /step-2-k2r3/ the progress bar is immediately followed by the offer copy and
   the two collide. /confirmation/ puts its bar in its own band, so this is
   scoped to the adjacency rather than added to .progress itself. */
.progress + .eyebrow {
  margin-top: clamp(2rem, 4vw, 3rem);
}

/* ---------- Upsell confirmation box ----------
   Reuses .checkout / .checkout__panel so it inherits the dialog framing,
   backdrop and radius already used by the sales-page checkout. */
.confirmbox__panel {
  padding: 2.25rem 2rem;
  text-align: center;
  max-width: 30rem;
}

.confirmbox__title {
  margin: 0 0 0.75rem;
  font-family: var(--subhead);
  font-weight: 800;
  font-size: clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);
  line-height: 1.25;
  color: var(--navy);
}

.confirmbox__copy {
  margin: 0 auto 1.75rem;
  max-width: 26rem;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--navy-80);
}

.confirmbox__yes {
  width: 100%;
}

/* Quiet, but a real button at a real size - a cancel people cannot find is
   the thing that turns an upsell into a complaint. */
.confirmbox__cancel {
  display: block;
  width: 100%;
  margin-top: 0.85rem;
  padding: 0.6rem;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.9375rem;
  color: var(--navy-55);
  text-decoration: underline;
  cursor: pointer;
}

.confirmbox__cancel:hover {
  color: var(--navy);
}

/* ---------- Upsell variant U2 (/step-2-w7q5/) ----------
   Image-led alternative to the written control at /step-2-k2r3/. Everything
   here is namespaced .ualt and scoped under body.upsell-alt so it cannot leak
   into the cream pages; the two arms share styles.css and must not drift.

   THE WHOLE DESIGN RESTS ON ONE COLOUR. The three artwork files are exported
   on #003a4a, which is exactly var(--navy). Because the page sits on the same
   value, the images have no visible edge and read as full-bleed bands. Change
   var(--navy) and this page develops seams. */
body.upsell-alt {
  background: var(--navy);
  color: var(--cream);
}

.ualt {
  padding-block: clamp(1.25rem, 3vw, 2.5rem) clamp(2.5rem, 5vw, 4rem);
}

/* Matches the artwork's own 1080px export width, so the images render at or
   below native size and never soften on a large screen. */
.ualt__wrap {
  width: min(100% - 2rem, 1080px);
  margin-inline: auto;
}

/* ---- Enrolment progress on navy ----
   .progress is built for the cream pages: navy ink on a navy-15 track. Both
   vanish here, so the three colours are re-stated rather than the component
   being forked. The gold fill is already correct on either ground. */
.ualt__progress {
  max-width: none;
  margin-bottom: clamp(1.5rem, 3vw, 2.25rem);
}

.ualt__progress .progress__label {
  color: var(--cream);
}

.ualt__progress .progress__pct,
.ualt__progress .progress__hint {
  color: var(--white-72);
}

.ualt__progress .progress__track {
  background: rgba(255, 255, 255, 0.14);
}

/* The images carry their own internal padding, so they butt straight up
   against the page with no card, border or shadow of ours. */
.ualt__art {
  width: 100%;
  height: auto;
  border-radius: 18px;
}

/* ---- Countdown ----
   hidden until main.js has read the stored deadline, so the markup's static
   10:00 never flashes to a buyer who is actually 40 seconds from expiry. */
.ualt__timer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  margin-block: clamp(1rem, 2.5vw, 1.5rem);
  padding: 0.9rem 1.25rem;
  border: 1px solid var(--white-24);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.06);
  font-size: clamp(0.875rem, 0.84rem + 0.2vw, 1rem);
  color: var(--cream);
  text-align: center;
}

.ualt__timer[hidden] {
  display: none;
}

.ualt__timer strong {
  font-variant-numeric: tabular-nums; /* stops the row twitching each second */
  font-size: 1.125em;
  color: var(--gold);
}

.ualt__timer--foot {
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
}

/* ---- Offer block ---- */
.ualt__offer {
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  align-items: start;
  margin-top: clamp(1rem, 2.5vw, 1.5rem);
}

@media (min-width: 800px) {
  .ualt__offer {
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  }
}

.ualt__tile {
  width: 100%;
  height: auto;
  border-radius: 18px;
}

.ualt__badge {
  display: inline-block;
  margin: 0 0 0.9rem;
  padding: 0.35rem 0.85rem;
  border: 1px solid rgba(252, 210, 113, 0.5);
  border-radius: var(--pill);
  background: rgba(252, 210, 113, 0.14);
  font-family: var(--subhead);
  font-weight: 800;
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold);
}

.ualt__title {
  margin: 0 0 0.35rem;
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.75rem, 1.4rem + 1.6vw, 2.5rem);
  line-height: 1.1;
  color: var(--white);
}

.ualt__price {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin: 0 0 1rem;
  font-family: var(--subhead);
  font-weight: 800;
  font-size: 1.375rem;
  color: var(--gold);
}

/* The was-price is deliberately quieter and smaller than the live one: it is
   context, not the number being asked for. Muted white rather than gold so the
   eye lands on $27 first - two gold numbers side by side compete. */
.ualt__price-was {
  font-size: 0.8em;
  font-weight: 400;
  color: var(--white-72);
  /* line-through, not text-decoration on <s> alone: browsers vary the stroke
     position, and at this weight the default sits low enough to read as an
     underline on some Android builds. */
  text-decoration: line-through;
  text-decoration-thickness: 2px;
}

.ualt__price-now {
  color: var(--gold);
}

.ualt__desc {
  margin: 0 0 1.5rem;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--white-72);
}

/* ---- Order lines ---- */
.ualt__lines {
  margin: 0 0 1.75rem;
}

.ualt__line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 0.7rem;
  border-bottom: 1px solid var(--white-24);
}

.ualt__line dt,
.ualt__line dd {
  margin: 0;
  font-size: 0.9375rem;
  color: var(--white-72);
}

.ualt__line--total {
  border-bottom: 0;
}

.ualt__line--total dt {
  font-family: var(--subhead);
  font-weight: 800;
  color: var(--cream);
}

.ualt__line--total dd {
  font-family: var(--subhead);
  font-weight: 800;
  font-size: 1.5rem;
  color: var(--white);
}

.ualt__cur {
  margin-right: 0.3rem;
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--white-72);
}

.ualt__cta {
  width: 100%;
}

/* The early exit that sits under the "what's included" artwork, roughly 860px
   further up the page than the decline below the button. See the comment in the
   markup for the numbers that prompted it.

   A GHOST PILL, not a bare text link. Every other element on this page is a
   contained shape - pill buttons, the bordered timer, the artwork cards - so an
   underlined link floating on the navy read as something half-finished rather
   than as a deliberate control. It borrows the button radius and font to sit
   inside the system, and stays subordinate to the gold CTA by being outline-only
   and sentence case rather than by being smaller or fainter. Contrast is NOT
   dialled down to keep it quiet: this is the way out of a flow the buyer has
   already paid into, and it has to stay legible. */
.ualt__exit {
  margin: clamp(1.25rem, 3vw, 1.75rem) 0 0;
  text-align: center;
}

.ualt__exit a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44px floor: this is a touch target on the phones where it matters most. */
  min-height: 44px;
  padding: 0.7rem 1.6rem;
  border: 1px solid var(--white-24);
  border-radius: var(--pill);
  background: rgba(255, 255, 255, 0.04);
  color: var(--white-72);
  font-family: var(--body);
  font-size: 0.875rem;
  text-decoration: none;
  transition: background-color 0.25s ease, color 0.25s ease,
    border-color 0.25s ease;
}

.ualt__exit a:hover,
.ualt__exit a:focus-visible {
  border-color: var(--white-72);
  background: rgba(255, 255, 255, 0.1);
  color: var(--white);
}

/* Belt and braces for the hidden attribute. The UA rule would cover a <p>,
   which sets no display of its own - but the same assumption about .btn is what
   left an expired notice sitting under a live "Place order now" button on this
   very page. Cheap insurance, and it keeps the temporary hide reliable. */
.ualt__exit[hidden] {
  display: none;
}

/* Decline is a full-width, readable control rather than micro-text. A one-time
   offer people struggle to refuse produces refunds, not revenue - same rule as
   the control page. */
.ualt__decline {
  margin: 0.9rem 0 0;
  text-align: center;
  font-size: 0.9375rem;
}

.ualt__decline a {
  color: var(--white-72);
  text-decoration: underline;
}

.ualt__decline a:hover {
  color: var(--white);
}

/* Error text sits on navy here, so the control page's amber-on-cream would be
   unreadable. Same element, same data attribute, different ground. */
body.upsell-alt .upsell__error {
  background: rgba(252, 210, 113, 0.16);
  border: 1px solid rgba(252, 210, 113, 0.45);
  color: var(--cream);
}

/* .btn sets display:inline-flex, and an author rule outranks the UA
   stylesheet's [hidden]{display:none} - so setting .hidden on a button does
   nothing at all. This bit during review: the "offer has expired" notice
   rendered directly underneath a still-clickable "Place order now". Any
   hidden button inside this page must actually disappear. */
body.upsell-alt .btn[hidden] {
  display: none;
}

/* ---- Expired state ---- */
.ualt__expired {
  margin-top: 1.5rem;
  padding: 1.5rem;
  border: 1px solid var(--white-24);
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.06);
  text-align: center;
}

.ualt__expired[hidden] {
  display: none;
}

.ualt__expired-title {
  margin: 0 0 0.4rem;
  font-family: var(--subhead);
  font-weight: 800;
  font-size: 1.0625rem;
  color: var(--cream);
}

.ualt__expired-copy {
  margin: 0 0 1.25rem;
  font-size: 0.9375rem;
  color: var(--white-72);
}

/* The footer is built for cream pages; on navy its default border and ink
   would both disappear. */
.ualt__footer {
  border-top: 1px solid var(--white-24);
  background: transparent;
  color: var(--white-72);
}
