.hero {
  /* Everything typographic in the hero is sized as a multiple of this,
     so the whole block retunes from one number instead of eight. The
     rest of the page keeps the base scale. */
  --hero-scale: 1.1;

  /* Lifted out of .hero__inner's `gap` shorthand so the photo can claim
     that same gutter back with a negative margin. It is the one strip of
     the hero guaranteed to hold no text at any width, which is what makes
     it safe for the image to grow into. */
  --hero-gap: clamp(2.5rem, 1rem + 6vw, 5.5rem);

  /* Distance from the right edge of the container's content box out to the
     right edge of the viewport: the gutter, plus whatever margin is left
     over once the container has hit its 76rem cap. The photo bleeds by
     exactly this, so it reaches the screen edge and stops. */
  --hero-bleed: calc(var(--gutter) + max(0px, (100vw - var(--container)) / 2));

  /* How much larger the picture runs than the space that is strictly its
     own. At 1 it fills its grid column plus the gap plus the bleed; above
     that it reaches further left, into the dead space at the end of the
     copy column. The copy is capped at 42rem and its column is wider than
     that at every desktop width, so there is real room there — and what
     the picture extends across it is its feathered edge, which carries
     almost no ink. See the note on the feather below. */
  --photo-grow: 1.3;

  /* See .hero__copy for what this is and why it eases back on narrow
     viewports. It lives up here because the frame below needs it to work
     out where the copy ends. */
  --copy-pull: min(4.375rem, max(0px, (100vw - 76rem) / 2 + var(--gutter) - 0.5rem));

  /* Where the copy's ink stops, which is what stops the photograph from
     growing into the text.

     This was 36rem when the headline was "The next chapter of you starts
     here." and its longest line measured 562px, well short of the copy's
     42rem cap. It is the full 42rem now, and not as a guess: the headline
     is sized in cqi against this same column, so by construction its
     longest line is 98% of the column and the copy is filled edge to
     edge. There is no dead space left at the end of the line to lend the
     picture any more.

     That costs the photograph real width — see --photo-grow. A shorter
     headline would give it straight back. */
  --copy-left: calc(max(0px, (100vw - var(--container)) / 2) + var(--gutter) - var(--copy-pull));
  --copy-ink-right: calc(var(--copy-left) + 42rem);

  position: relative;
  overflow: hidden;
  /* Fill the first screen exactly. The announcement bar and nav sit above
     the hero in normal flow, so their heights come off the top. dvh so
     mobile browser chrome collapsing doesn't leave a gap; vh first as a
     fallback. min-height rather than height, so the stacked mobile layout
     is free to grow past it. */
  min-height: calc(100vh - var(--nav-h) - var(--bar-h));
  min-height: calc(100dvh - var(--nav-h) - var(--bar-h));
  display: flex;
  align-items: center;
  padding-block: clamp(3.5rem, 2rem + 6vw, 7rem) clamp(4rem, 2.5rem + 6vw, 7.5rem);
  /* Dark now. The photograph is a night-toned double exposure, so the hero
     inverts: light copy on deep navy instead of dark copy on white. */
  background: var(--a-800);
  color: var(--text-invert);
}

/* ---------- Photographic frame ----------
   The photograph used to be a full-bleed background sitting behind the
   whole hero, with a heavy scrim over it doing the work of keeping white
   copy legible on top. It sits in the right-hand column now and never
   crosses the text, so the scrim's job changes completely: it is no
   longer there for contrast, only to dissolve the picture's own edges
   into the navy.

   .hero__visual is the frame — where the picture sits and how wide it
   gets. .hero__photo is the picture — how it is cropped, dimmed and
   feathered. Keeping those apart means the crop maths below does not
   have to know anything about the page's margins. */

/* The frame starts from three strips of space the copy never occupies —
   its own grid column, the gap to its left, and the bleed out to the
   right edge of the screen — and then multiplies that by --photo-grow.

   Pinned by its right edge and sized by width, so the growth all happens
   leftward: the right edge stays welded to the screen edge instead of
   drifting off it. The percentage resolves against the grid area, which
   is column two, so the sum in here is the frame's natural width and the
   multiplier is exact.

   Column one's track is never touched, so the headline, lead, buttons and
   stats sit exactly where they did before. */
.hero__visual {
  /* The three strips that are unambiguously the picture's: its own grid
     column (the percentage, which resolves against the grid area), the
     gap to its left, and the bleed out to the screen edge. */
  --photo-natural: calc(100% + var(--hero-gap) + var(--hero-bleed));

  /* How far the frame is allowed to hang off the right of the screen.

     The radial mask is an 82%-wide ellipse centred at 52%, so it has
     already erased everything past about 93% of the frame — and what sits
     out there in the picture is empty backdrop to the right of the
     woman's head, not subject. Pushing a sliver of that off-screen costs
     nothing visible and buys the same sliver of clearance back on the
     left, which is where it is scarce. Kept small deliberately: at much
     more than this the cut starts landing on pixels the mask has not
     finished fading. */
  --photo-overhang: 0.04;

  /* Everything between the copy's last pixel of ink and the screen edge,
     divided by the fraction of the frame that stays on screen. The growth
     is allowed to eat into this and no further. */
  --photo-room: calc((100vw - var(--copy-ink-right)) / (1 - var(--photo-overhang)));

  /* Grown by --photo-grow, but never past the copy, and never smaller
     than it was: the outer max() matters around 1000px, where the natural
     frame already reaches further left than the ink boundary and the
     middle term would otherwise shrink it.

     From roughly 1100px to 1920px the full 30% fits. Above that the
     container stops widening while the copy keeps drifting inward with
     it, so the cap takes over and the growth eases off — about 23% at
     2560. That is the right place to give way: those are the widths where
     the frame is already largest in absolute terms. */
  --photo-width: max(
    var(--photo-natural),
    min(calc(var(--photo-natural) * var(--photo-grow)), var(--photo-room))
  );

  position: relative;
  justify-self: end;
  width: var(--photo-width);
  margin-right: calc(-1 * (var(--hero-bleed) + var(--photo-width) * var(--photo-overhang)));
}

/* The crop, which falls out of one number.

   The file is 3157x1600. Only the right 2400px of it is photograph; the
   left 757px is generated navy padding — a stretched, blurred copy of the
   picture's leftmost column, added back when this was a full-bleed
   background that had to dissolve across the entire left margin. It is
   banded and obviously artificial, so showing it is not an option.

   Rather than crop the asset, the box is given the aspect ratio of the
   photograph alone (2400/1600 = 1.5) and the image is scaled to the box
   height and pinned to its right edge. The arithmetic then cancels
   exactly: at height H the scaled file is 1.973H wide against a 1.5H box,
   so 0.473H hangs off the left — and the padding, at 757/1600 of the
   height, is 0.473H. The generated strip lands precisely outside the box
   at every size, with no magic offset to keep in sync. */
.hero__photo {
  position: relative;
  aspect-ratio: 2400 / 1600;
  background-image: url('../assets/hero-dna.jpg');
  background-repeat: no-repeat;
  background-size: auto 100%;
  background-position: right center;

  /* Brightness first, then contrast to put back the separation that
     dimming costs. The picture's own backdrop is near-white while the
     hero is near-black navy — opposite ends of the scale — so without
     this the frame reads as a lit panel stuck on a dark page no matter
     how far the edges are feathered. At 0.68 the backdrop lands close
     enough to the navy that the feather has only a short distance left
     to travel. Saturation comes down with it: colour that survives heavy
     dimming looks lurid. */
  filter: brightness(0.68) contrast(1.12) saturate(0.85);

  /* Three masks intersected, because one shape cannot do this job.

     The radial does the real work — an ellipse a little larger than the
     box, so the corners (furthest from the centre) are gone entirely
     while the couple in the middle stays at full strength. The two
     linears then guarantee the four straight edges reach zero, which a
     radial alone leaves at a low but non-zero value along the mid-edges.

     Every stop is eased rather than evenly spaced. A linear ramp puts a
     visible crease at the point it begins; stacking the stops toward the
     transparent end hides where the fade starts. */
  --feather:
    radial-gradient(
      ellipse 82% 84% at 52% 48%,
      #000 30%,
      rgb(0 0 0 / 0.86) 50%,
      rgb(0 0 0 / 0.52) 70%,
      rgb(0 0 0 / 0.18) 86%,
      transparent 99%
    ),
    linear-gradient(
      to right,
      transparent 0%,
      rgb(0 0 0 / 0.35) 9%,
      rgb(0 0 0 / 0.85) 20%,
      #000 34%,
      #000 74%,
      rgb(0 0 0 / 0.7) 89%,
      transparent 100%
    ),
    linear-gradient(
      to bottom,
      transparent 0%,
      rgb(0 0 0 / 0.4) 8%,
      rgb(0 0 0 / 0.88) 19%,
      #000 32%,
      #000 70%,
      rgb(0 0 0 / 0.72) 87%,
      transparent 100%
    );

  -webkit-mask-image: var(--feather);
  mask-image: var(--feather);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

/* The tint pass, on the frame rather than the picture, and deliberately
   so: the filter and mask above would both apply to a pseudo-element of
   .hero__photo, which would dim this navy away from the page's navy and
   then rub out the very rim it is meant to be painting.

   Unmasked, its outer edge is solid --a-800 sitting on an --a-800 hero,
   so the rectangle it draws is invisible. What it actually buys is the
   middle band, where the picture is still largely opaque: the mask makes
   the edges thinner, this makes them navier, and the two together read as
   the photo dissolving into the page rather than fading out on top of it.

   The second wash is bottom-weighted because the hero's lower edge meets
   the white section below and has to arrive on a flat colour. */
.hero__visual::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(
      ellipse 80% 82% at 52% 48%,
      transparent 26%,
      color-mix(in oklab, var(--a-800) 22%, transparent) 52%,
      color-mix(in oklab, var(--a-800) 62%, transparent) 74%,
      color-mix(in oklab, var(--a-800) 88%, transparent) 92%,
      var(--a-800) 100%
    ),
    linear-gradient(
      to bottom,
      transparent 58%,
      color-mix(in oklab, var(--a-800) 34%, transparent) 82%,
      color-mix(in oklab, var(--a-800) 72%, transparent) 100%
    );
  pointer-events: none;
}

/* Mobile serves a 1200px cut — a quarter the bytes for a screen that
   cannot resolve the difference. */
@media (max-width: 48rem) {
  .hero__photo {
    background-image: url('../assets/hero-dna-1200.jpg');
  }
}

/* The copy column takes the larger share so the headline breaks over two
   lines rather than three. Measured against Inter Tight at the shipped
   size: the longest line ("The next chapter of") needs 659px, so the
   column has to clear that at every width down to the 58rem stack. */
.hero__inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.36fr) minmax(0, 0.64fr);
  align-items: center;
  gap: var(--hero-gap);
}

/* ---------- Copy ---------- */

/* Every font-size below multiplies the shared token rather than
   hardcoding a new value, so the hero tracks any change to the global
   type scale while staying a step larger than it. */

/* Wide enough for the two-line headline. The lead and stats are capped
   narrower below, so only the headline uses the full measure. */
.hero__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  max-width: 42rem;
  /* Pulled 70px left of the container's gutter — but only where there is
     room for it. A flat -70px clips the copy off the left edge below about
     1290px, because once the viewport is narrower than the 76rem container
     there is no side margin left to borrow from and only the gutter remains.

     So the pull is the smaller of 70px and whatever space actually exists,
     less 8px of safety. Above ~1300px that resolves to the full 70px; below
     it eases back automatically instead of clipping.

     A negative margin rather than a transform, so it does not create a
     containing block for the child scroll-reveals.

     The value itself lives on .hero, because the photographic frame needs
     it too — it cannot work out where the copy's right edge falls without
     knowing how far left the copy was pulled. */
  margin-left: calc(var(--copy-pull) * -1);

  /* Makes this column the reference the headline's cqi sizing measures
     against. Safe here because the width does not come from the contents:
     as a grid item it stretches to its track and is then capped by
     max-width above, so containing the inline axis changes no layout. */
  container-type: inline-size;
}

/* e-200, not --detail. The page teal (e-600) is tuned for a light
   background and vanishes on navy; e-300 measured only 3.7:1 here. */
.hero .eyebrow {
  font-size: calc(var(--t-xs) * var(--hero-scale));
  color: var(--e-200);
}

/* The leading dash scales with the label it belongs to. */
.hero .eyebrow::before {
  width: calc(1.25rem * var(--hero-scale));
}

/* ---------- Kicker ----------
   Where the eyebrow used to sit, but not an eyebrow. The other eyebrows
   on the site label a section in two or three uppercase words; this is a
   whole sentence, and a sentence set in 13px caps reads as a caption —
   it also flattens the serif "you", which is the only place on the site
   that typeface appears.

   So: sentence case, lead size, and the emphasis carried by the serif
   rather than by tracking. It is the softer, human line above a headline
   that is deliberately clinical, which is the whole reason both exist. */
.hero__kicker {
  max-width: 32rem;
  font-family: var(--font-display);
  font-size: calc(var(--t-lead) * var(--hero-scale));
  font-weight: 450;
  line-height: 1.45;
  letter-spacing: -0.012em;
  /* Not full white. The headline underneath is --n-0, and two elements at
     the same value with no size gap between them compete; stepping this
     back lets the headline stay the first thing read. */
  color: color-mix(in oklab, var(--n-0) 76%, transparent);
}

.hero__kicker em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  /* The serif runs optically smaller than Inter Tight at the same size,
     and it is the one word meant to be noticed. */
  font-size: 1.14em;
  letter-spacing: -0.01em;
  color: var(--e-200);
  padding-inline-end: 0.04em;
}

/* The headline is sized off the copy column rather than the viewport.

   "Pharmacist compounded." is the constraint. Measured in this face at
   this weight and tracking it runs 10.53x its own font-size, so the
   largest size that keeps it on one line is the column width over 10.53
   — which is 9.50cqi. 9.3cqi is that with 2% of slack, and cqi is a
   percentage of .hero__copy's inline size, so the fit holds at every
   width without a breakpoint. That is why .hero__copy declares
   container-type below.

   Do not replace this with a vw clamp. The column is min(grid track,
   42rem), and the track is a fraction of a container that stops growing
   at 76rem — so above about 1300px the column is flat while vw keeps
   climbing, and any clamp tuned to look right at 1440 overshoots the
   measure somewhere else and breaks the line.

   The floor matters on phones. Below roughly 430px 9.3cqi would fall
   under 36px, which is small for the one heading on the screen; at that
   point letting the claims wrap is the better trade, so the max() holds
   the size and .hero__beat gives up its nowrap. The first declaration is
   the fallback for browsers without container query units. */
.hero__copy h1 {
  margin-top: var(--s-5);
  font-size: calc(clamp(2.25rem, 1.2rem + 2.9vw, 3.55rem) * var(--hero-scale));
  font-size: max(2.25rem, 9.3cqi);
  color: var(--n-0);
}

/* Each claim on its own line as a matter of structure, not of wrapping.
   nowrap so a claim is never split across two lines above the point where
   the size floor takes over; at that point it has to be allowed to wrap
   or it would simply overflow the column. */
.hero__beat {
  display: block;
  white-space: nowrap;
}

@media (max-width: 27rem) {
  .hero__beat {
    white-space: normal;
  }
}

/* The one serif word on the entire site. Reserving a typeface for a
   single moment is what makes that moment land. */
.hero__copy h1 em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.015em;
  color: var(--e-200);
  padding-inline-end: 0.04em;
}

/* Held to 34rem while the headline runs to 42rem. Letting the paragraph
   fill the wider column would push it past 80 characters a line. */
.hero__lead {
  margin-top: var(--s-5);
  font-size: calc(var(--t-lead) * var(--hero-scale));
  line-height: var(--lh-loose);
  color: color-mix(in oklab, var(--n-0) 78%, transparent);
  max-width: 34rem;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-top: var(--s-8);
}

/* Label and padding both scale, so the buttons grow as objects rather
   than just gaining bigger text in the same box. The :has() rule is
   restated because base.css sets padding-right at equal specificity. */
.hero .btn--lg {
  padding: calc(0.9375rem * var(--hero-scale)) calc(var(--s-8) * var(--hero-scale));
  font-size: calc(1rem * var(--hero-scale));
}

.hero .btn--lg:has(.btn__arrow) {
  padding-right: calc((var(--s-8) - 0.3125rem) * var(--hero-scale));
}

/* On a navy hero the navy primary button would disappear into its own
   background, so the pair inverts: white fill for the primary, hairline
   outline for the secondary. Markup is untouched. */
.hero .btn--primary {
  --btn-fg: var(--a-800);
  background: linear-gradient(180deg, var(--n-0) 0%, var(--n-100) 100%);
  box-shadow:
    inset 0 1px 0 var(--n-0),
    inset 0 0 0 1px color-mix(in oklab, var(--a-800) 12%, transparent),
    0 1px 2px rgb(0 0 0 / 0.3),
    0 10px 26px rgb(0 0 0 / 0.34);
}

.hero .btn--primary:hover {
  background: linear-gradient(180deg, var(--n-0) 0%, var(--n-0) 100%);
  box-shadow:
    inset 0 1px 0 var(--n-0),
    inset 0 0 0 1px color-mix(in oklab, var(--a-800) 14%, transparent),
    0 1px 2px rgb(0 0 0 / 0.32),
    0 16px 38px rgb(0 0 0 / 0.4);
}

.hero .btn--primary:active {
  background: linear-gradient(180deg, var(--n-100) 0%, var(--n-200) 100%);
  box-shadow:
    inset 0 2px 5px color-mix(in oklab, var(--a-800) 22%, transparent),
    0 1px 2px rgb(0 0 0 / 0.3);
}

.hero .btn--secondary {
  --btn-fg: var(--n-0);
  --btn-bd: color-mix(in oklab, var(--n-0) 30%, transparent);
  background: color-mix(in oklab, var(--n-0) 7%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  box-shadow: inset 0 1px 0 color-mix(in oklab, var(--n-0) 22%, transparent);
}

.hero .btn--secondary:hover {
  --btn-bd: color-mix(in oklab, var(--n-0) 58%, transparent);
  background: color-mix(in oklab, var(--n-0) 14%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in oklab, var(--n-0) 30%, transparent);
}

.hero .btn--secondary:active {
  background: color-mix(in oklab, var(--n-0) 5%, transparent);
  box-shadow: inset 0 2px 5px rgb(0 0 0 / 0.25);
}

/* Matches the lead's measure so the hairline aligns with the paragraph
   above it instead of running out to the headline's width. */
.hero__stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-10);
  margin-top: var(--s-12);
  padding-top: var(--s-8);
  border-top: 1px solid color-mix(in oklab, var(--n-0) 16%, transparent);
  width: 100%;
  max-width: 34rem;
}

.hero__stat dt {
  font-family: var(--font-display);
  font-size: calc(clamp(1.5rem, 1.3rem + 0.9vw, 1.9rem) * var(--hero-scale));
  font-weight: 600;
  letter-spacing: -0.035em;
  line-height: 1.1;
  color: var(--n-0);
  font-variant-numeric: tabular-nums;
}

.hero__stat dd {
  margin-top: var(--s-1);
  font-size: calc(var(--t-sm) * var(--hero-scale));
  color: color-mix(in oklab, var(--n-0) 70%, transparent);
}

/* ---------- Visual ---------- */

/* Every dimension inside the card multiplies this, so the whole mock
   retunes from one number. The width shrinks too — scaling only the type
   would leave a full-width card with small text in it.

   The card is disabled (its markup is commented out in index.html) and
   .hero__visual is the photographic frame now — see the top of this file
   for where its box is set. Only the card's own scale variables live
   here; putting the width back would fight the frame. */
.hero__visual {
  --card-scale: 0.81;
  --card-shift: 1rem;
}

/* ---------- Liquid glass ----------
   Not a frosted pane. The centre stays comparatively clear and the work
   happens at the rim:

     1. a light blur + saturation boost   — colour bleeds through
     2. edge refraction (Chromium)        — the backdrop physically bends
                                            through the bevel, see the
                                            feDisplacementMap filters in
                                            index.html
     3. a bright specular rim             — two-tone, brightest top-left,
                                            picking up again bottom-right
     4. an inner shade under the top edge — reads as slab thickness
     5. a 62% fill                        — low enough to look like glass,
                                            high enough that 9.7px text
                                            still clears 4.8:1

   blur(10px) is deliberately light. It is not there to hide the photo,
   only to keep the backdrop from swinging 8x in luminance under the text. */

.plan,
.float {
  /* Dark glass. A white fill over a night photograph would read as a
     lightbox, not a pane — on a dark backdrop the tint has to darken.

     52%: with the scrim back over a full-bleed image the cards' backdrop is
     dark again, so the tint no longer has to carry the contrast on its own.
     46% is the measured floor; 52% leaves margin. */
  --glass: 52%;
  background: color-mix(in oklab, var(--a-800) var(--glass), transparent);
  -webkit-backdrop-filter: blur(10px) saturate(170%);
  backdrop-filter: blur(10px) saturate(170%);
  border: 0;
}

/* The refraction. Gated on the CSS Paint API, which is a reliable proxy
   for Blink: Chromium renders SVG filter references inside backdrop-filter,
   WebKit and Gecko do not. Everywhere else keeps the plain glass above
   rather than losing the backdrop-filter entirely. */
@supports (background: paint(id)) {
  .plan {
    backdrop-filter: url(#lg-card) blur(10px) saturate(170%);
  }
  .float {
    backdrop-filter: url(#lg-badge) blur(8px) saturate(170%);
  }
}

/* The rim, carved out of a gradient with mask-composite because a
   border-color cannot hold one. Brighter and wider than a frosted card
   would use — on real glass the edge is where light collects. */
.plan::before,
.float::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  padding: 1.5px;
  background: linear-gradient(
    145deg,
    color-mix(in oklab, var(--n-0) 62%, transparent) 0%,
    color-mix(in oklab, var(--n-0) 20%, transparent) 30%,
    color-mix(in oklab, var(--n-0) 8%, transparent) 55%,
    color-mix(in oklab, var(--n-0) 42%, transparent) 100%
  );
  -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;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .plan,
  .float {
    background: color-mix(in oklab, var(--a-800) 88%, var(--a-600));
    border: 1px solid color-mix(in oklab, var(--n-0) 16%, transparent);
  }
  .plan::before,
  .float::before {
    display: none;
  }
}

.plan {
  position: relative;
  padding: calc(var(--s-6) * var(--card-scale));
  border-radius: var(--r-2xl);
  box-shadow:
    inset 0 1px 1px color-mix(in oklab, var(--n-0) 42%, transparent),
    inset 0 -16px 26px -16px rgb(0 0 0 / 0.5),
    0 1px 3px rgb(0 0 0 / 0.3),
    0 30px 70px rgb(0 0 0 / 0.45);
}

.plan__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}

.plan__chip {
  padding: calc(0.3125rem * var(--card-scale)) calc(var(--s-3) * var(--card-scale));
  border-radius: var(--r-pill);
  background: color-mix(in oklab, var(--e-400) 26%, transparent);
  color: var(--e-200);
  font-size: calc(var(--t-xs) * var(--card-scale));
  font-weight: 600;
  letter-spacing: 0.01em;
}

.plan__status {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-size: calc(var(--t-xs) * var(--card-scale));
  font-weight: 500;
  color: color-mix(in oklab, var(--ok-500) 55%, var(--n-0));
}

.plan__pulse {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 0 currentColor;
  animation: pulse 2.4s var(--ease-inout) infinite;
}

@keyframes pulse {
  0% {
    box-shadow: 0 0 0 0 color-mix(in oklab, var(--ok-500) 45%, transparent);
  }
  70%,
  100% {
    box-shadow: 0 0 0 7px transparent;
  }
}

.plan__title {
  margin-top: calc(var(--s-5) * var(--card-scale));
  padding-bottom: calc(var(--s-5) * var(--card-scale));
  border-bottom: 1px solid color-mix(in oklab, var(--n-0) 14%, transparent);
}

.plan__title h3 {
  color: var(--n-0);
  font-size: calc(1.375rem * var(--card-scale));
  font-weight: 600;
  letter-spacing: -0.028em;
}

.plan__title p {
  margin-top: var(--s-1);
  font-size: calc(var(--t-sm) * var(--card-scale));
  color: color-mix(in oklab, var(--n-0) 68%, transparent);
}

.plan__timeline {
  display: flex;
  flex-direction: column;
  padding-block: calc(var(--s-5) * var(--card-scale));
}

.plan__step {
  position: relative;
  display: grid;
  grid-template-columns: calc(1.25rem * var(--card-scale)) 1fr auto;
  align-items: center;
  gap: calc(var(--s-3) * var(--card-scale));
  padding-block: calc(var(--s-2) * var(--card-scale));
  font-size: calc(var(--t-base) * var(--card-scale));
}

/* Connector line between dots, drawn on every step but the last. */
.plan__step:not(:last-child)::before {
  content: '';
  position: absolute;
  left: calc(0.59rem * var(--card-scale));
  top: calc(50% + 0.6rem * var(--card-scale));
  width: 1.5px;
  height: calc(100% - 1.2rem * var(--card-scale));
  background: color-mix(in oklab, var(--n-0) 22%, transparent);
}

.plan__dot {
  display: grid;
  place-items: center;
  width: calc(1.25rem * var(--card-scale));
  height: calc(1.25rem * var(--card-scale));
  border-radius: 50%;
  border: 1.5px solid color-mix(in oklab, var(--n-0) 34%, transparent);
  background: color-mix(in oklab, var(--a-800) 60%, transparent);
  color: var(--n-0);
  z-index: 1;
}

.plan__step--done .plan__dot {
  background: var(--e-400);
  border-color: var(--e-400);
}

.plan__step--active .plan__dot {
  border-color: var(--e-200);
  border-style: dashed;
}

.plan__step--done .plan__step-label {
  color: color-mix(in oklab, var(--n-0) 70%, transparent);
}

.plan__step--active .plan__step-label {
  color: var(--n-0);
  font-weight: 550;
}

.plan__step-meta {
  font-size: calc(var(--t-xs) * var(--card-scale));
  color: color-mix(in oklab, var(--n-0) 70%, transparent);
  font-variant-numeric: tabular-nums;
}

.plan__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--s-3) * var(--card-scale));
  padding-top: calc(var(--s-5) * var(--card-scale));
  border-top: 1px solid color-mix(in oklab, var(--n-0) 14%, transparent);
}

.plan__price {
  color: var(--n-0);
  font-family: var(--font-display);
  font-size: calc(1.5rem * var(--card-scale));
  font-weight: 600;
  letter-spacing: -0.035em;
}

.plan__per {
  margin-left: var(--s-1);
  font-size: calc(var(--t-sm) * var(--card-scale));
  color: color-mix(in oklab, var(--n-0) 70%, transparent);
}

.plan__manage {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-size: calc(var(--t-base) * var(--card-scale));
  font-weight: 550;
  /* e-200 on dark glass; the light-background teals are far too dark here. */
  color: var(--e-200);
}

/* ---------- Floating badges ---------- */

.float {
  position: absolute;
  display: flex;
  align-items: center;
  gap: calc(var(--s-3) * var(--card-scale));
  padding: calc(var(--s-3) * var(--card-scale)) calc(var(--s-4) * var(--card-scale));
  border-radius: var(--r-lg);
  box-shadow:
    inset 0 1px 1px color-mix(in oklab, var(--n-0) 42%, transparent),
    inset 0 -10px 16px -10px rgb(0 0 0 / 0.45),
    0 1px 3px rgb(0 0 0 / 0.32),
    0 18px 44px rgb(0 0 0 / 0.48);
  white-space: nowrap;
}

.float strong {
  display: block;
  color: var(--n-0);
  font-family: var(--font-display);
  font-size: calc(var(--t-base) * var(--card-scale));
  font-weight: 600;
  letter-spacing: -0.012em;
}

.float span:not(.float__icon) {
  display: block;
  font-size: calc(var(--t-xs) * var(--card-scale));
  color: color-mix(in oklab, var(--n-0) 70%, transparent);
  margin-top: 1px;
}

.float__icon {
  display: grid;
  place-items: center;
  width: calc(2.125rem * var(--card-scale));
  height: calc(2.125rem * var(--card-scale));
  border-radius: var(--r-md);
  flex-shrink: 0;
}

.float__icon--accent {
  background: color-mix(in oklab, var(--e-400) 28%, transparent);
  color: var(--e-200);
}

.float__icon--ok {
  background: color-mix(in oklab, var(--ok-500) 26%, transparent);
  color: color-mix(in oklab, var(--ok-500) 45%, var(--n-0));
}

.float--provider {
  top: calc(-1.5rem * var(--card-scale));
  left: calc(-3rem * var(--card-scale));
}

.float--ship {
  bottom: calc(-1.75rem * var(--card-scale));
  right: calc(-2.5rem * var(--card-scale));
}

/* Two badges drifting on offset cycles. 7px of travel over 6s — you
   register it as "alive", not as "moving". */
@media (prefers-reduced-motion: no-preference) {
  .float--provider {
    animation: drift 6s var(--ease-inout) infinite;
  }
  .float--ship {
    animation: drift 6s var(--ease-inout) -3s infinite;
  }
}

@keyframes drift {
  50% {
    transform: translateY(-7px);
  }
}

/* ---------- Responsive ---------- */

@media (max-width: 68rem) {
  .float--provider {
    left: -1rem;
  }
  .float--ship {
    right: -0.5rem;
  }
}

@media (max-width: 58rem) {
  .hero__inner {
    grid-template-columns: 1fr;
    gap: var(--s-16);
  }
  .hero__copy {
    max-width: 40rem;
    margin-left: 0;
  }
  /* Single column now: the picture sits under the copy rather than beside
     it, so the leftward reach into the gap is wrong — that gap no longer
     separates two columns. It keeps the bleed on both sides instead, which
     is what lets it stay large on a narrow screen. */
  .hero__visual {
    justify-self: stretch;
    margin-inline: calc(var(--hero-bleed) * -1);
  }
  .hero__stats {
    gap: var(--s-8);
  }

  /* Nothing sits on top of the photo at any width now, so the stacked
     layout needs no special crop and no separate scrim — the frame and
     feather defined at the top of this file carry over unchanged. */
}

@media (max-width: 30rem) {
  .float {
    padding: var(--s-2) var(--s-3);
  }
  .float--provider {
    left: -0.5rem;
    top: -1.25rem;
  }
  .float--ship {
    right: -0.5rem;
    bottom: -1.25rem;
  }
  .hero__stats {
    gap: var(--s-6) var(--s-8);
  }
}
