/* ==========================================================================
   Apex Cognition — Section layouts
   ========================================================================== */

/* --------------------------------------------------------------------------
   Header
   -------------------------------------------------------------------------- */

.site-header {
  position: absolute;
  inset-inline: 0;
  top: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  padding: var(--space-5) var(--gutter);
  max-width: var(--content-wide);
  margin-inline: auto;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--text);
  text-decoration: none;
}

.brand__mark { width: 26px; height: 26px; color: var(--accent); flex: none; }

.brand__word {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: -0.02em;
}

.brand__word span { color: var(--text-dim); font-weight: 500; margin-left: 0.28em; }

@media (max-width: 34rem) {
  .site-header__cta { display: none; }   /* the sticky CTA covers this */
}

/* --------------------------------------------------------------------------
   Brain stage — fixed behind hero + value prop
   -------------------------------------------------------------------------- */

#brain-stage {
  position: fixed;
  inset: 0;
  z-index: var(--z-canvas);
  pointer-events: none;
  transition: opacity var(--dur-mid) linear;
}

#brain-canvas,
#burst-canvas,
.brain-placeholder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Static stand-in: paints on the first frame, so the hero is never blank.
   Deliberately soft and out of focus — the same read as the blurred WebGL
   brain it hands over to. */
.brain-placeholder {
  background:
    radial-gradient(38% 30% at 50% 42%, rgba(124, 111, 240, 0.42), transparent 70%),
    radial-gradient(22% 18% at 41% 36%, rgba(94, 234, 212, 0.26), transparent 70%),
    radial-gradient(26% 20% at 60% 47%, rgba(124, 111, 240, 0.30), transparent 72%),
    radial-gradient(16% 13% at 52% 58%, rgba(255, 122, 69, 0.14), transparent 70%);
  filter: blur(28px);
  opacity: 0.9;
  transition: opacity var(--dur-xslow) var(--ease);
}

/* Once WebGL has drawn a real frame, hero-brain.js takes over this element's
   opacity, keeping it as a soft bloom bed through the hero and clearing it as
   the brain sharpens. Until then the CSS value above is what's on screen. */

#brain-canvas {
  opacity: 0;
  transition: opacity var(--dur-xslow) var(--ease);
}

#brain-canvas.is-ready { opacity: 1; }

#burst-canvas { mix-blend-mode: screen; opacity: 0.9; }

/* --------------------------------------------------------------------------
   Kit stage — the 3D box, section 4
   --------------------------------------------------------------------------
   z-index 3 puts it above .section--opaque (z 2) and below the sticky CTA
   (z 50) and header (z 60). It has to clear the sections rather than hide
   behind them, because the box's last move is to land inside the kit card.
   Everything below it stays clickable: the stage never takes pointer events.

   Visibility and opacity are owned by kit-stage.js from the scroll handler —
   not per frame, and not by CSS, because "is the box on screen" is a scroll
   question. The transition here only covers the WebGL handover.
   -------------------------------------------------------------------------- */

#kit-stage {
  position: fixed;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  visibility: hidden;
  opacity: 0;
}

/* Deliberately keyed by id rather than scoped to the stage: once the sequence
   lands, kit-box.js reparents this canvas into .kit-card__figure so it scrolls
   with the card, and these same rules have to size it against its new parent.
   pointer-events is on the canvas, not just the stage, for the same reason. */
#kit-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease);
}

#kit-canvas.is-ready { opacity: 1; }

/* The spacer that buys the sequence its scroll distance. Zero unless the box
   mounted — the class is added by kit-stage.js only after mountKitBox()
   returns something. The lengths come from KIT_BOX.track in config.js; the
   breakpoint is the CSS's business so a resize needs no JS. */
#kit-track { height: 0; }

.kit-box-ready #kit-track {
  height: calc(var(--kit-track-desktop, 3.2) * 100vh);
}

@media (max-width: 48rem) {
  .kit-box-ready #kit-track {
    height: calc(var(--kit-track-mobile, 2.4) * 100vh);
  }
}

/* The flat pack art is the fallback and stays in the DOM in every case. Once
   the box has drawn a frame it fades out but keeps its box — the figure
   column's height still comes from the SVG, so the card's layout is identical
   either way and the 3D box has a stable rect to land in.

   Keyed to .kit-box-live (first drawn frame), not .kit-box-ready (mounted):
   the track needs its height the moment the box exists, but the art must not
   go until there is something to replace it with.

   .pack__img is here because renderProductShots() swaps the vector for a real
   photo the moment PRODUCT_SHOTS["the-kit"] stops being null — and a founder
   adding photography shouldn't end up with a photo showing *through* the 3D
   box. Whichever stand-in is in the slot, the live box retires it. */
.kit-box-live .pack--kit .pack__art,
.kit-box-live .pack--kit .pack__img {
  opacity: 0;
  transition: opacity var(--dur-mid) var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  /* Belt and braces: mountKitBox() already returns null here, so the class is
     never added. If anything ever changes that, the section must not grow a
     three-viewport hole. */
  #kit-track,
  .kit-box-ready #kit-track { height: 0; }
  #kit-stage { display: none; }
  .kit-box-live .pack--kit .pack__art,
  .kit-box-live .pack--kit .pack__img { opacity: 1; }
}

/* --------------------------------------------------------------------------
   1 — Hero
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding-block: calc(var(--space-10) + 2rem) var(--space-9);

  /* At peak letter-spacing the headline is ~1.5x its settled width and runs
     past the viewport at some sizes. Clip so that never becomes a horizontal
     scrollbar. `clip` rather than `hidden` deliberately: hidden would make
     this a scroll container, clip does not — and overflow-y stays visible so
     nothing below is cut. */
  overflow-x: clip;
  overflow-y: visible;
}

.hero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-8);
  will-change: opacity, transform;
}

.hero__headline {
  /* 20ch, not 16ch: `ch` scales with font-size, so shrinking --fs-hero alone
     would shrink this box by the same proportion and gain nothing. At 20ch the
     shell (--content-max) becomes the real constraint, which is what we want. */
  max-width: 20ch;
  text-shadow: 0 2px 40px rgba(10, 11, 13, 0.85);
}

/* ⚠ `white-space: nowrap` is load-bearing, not cosmetic.
   It is what makes the line count STRUCTURAL rather than a function of text
   measurement — which in turn is what allows assemble-in to animate
   letter-spacing at all. Remove it and the headline goes back to reflowing
   mid-animation and again when the webfont swaps in. Settled text fits with
   huge margin at every width (measured 320-1920), so nothing ever needs to
   wrap; .hero's overflow-x: clip absorbs the wide entrance state. */
.hero__emphasis,
.hero__rest {
  display: block;
  white-space: nowrap;
}

/* The first line carries the emphasis: heavier, accented, and assembled in
   from a blur. The second clause simply fades. */
/* --- "Supercharge": alive and powered ---------------------------------
   Two layers, both composited, neither touching layout:

   1. an irregular glow pulse — a neon tube that occasionally dips. The
      timing is deliberately uneven; a smooth sine reads as a loading
      indicator rather than as something energised.
   2. a charge sweeping across the glyphs every ~7s, via a gradient clipped
      to the text. Only background-position animates.

   Both wait out the 1.6s entrance (200ms delay + 1400ms duration) — run
   them during assemble-in and the two fight each other. Palette colours
   only; anything outside violet/cyan immediately reads as another brand. */
.hero__emphasis {
  position: relative;
  color: var(--accent-text);
  font-weight: 700;
  animation:
    assemble-in 1400ms var(--ease) both 200ms,
    charge-breathe 6.5s ease-in-out 1700ms infinite;
}

/* The sweep. A copy of the word laid exactly over the original, painted with
   a moving highlight and clipped to the glyphs. `screen` so it only ever adds
   light. Inherits letter-spacing, so it tracks the entrance automatically. */
.hero__emphasis::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    100deg,
    transparent 42%,
    rgba(242, 242, 240, 0.92) 50%,
    rgba(169, 155, 255, 0.55) 56%,
    transparent 64%
  );
  background-size: 260% 100%;
  background-position: 180% 0;
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  mix-blend-mode: screen;
  animation: charge-sweep 7s var(--ease) 2400ms infinite;
}

/* Mostly steady, with two short irregular dips — the tell that it's a live
   circuit rather than a fade loop. */
@keyframes charge-breathe {
  0%, 100% { text-shadow: 0 0 24px rgba(124, 111, 240, 0.34); }
  18%      { text-shadow: 0 0 30px rgba(124, 111, 240, 0.44); }
  22%      { text-shadow: 0 0 14px rgba(124, 111, 240, 0.20); }
  26%      { text-shadow: 0 0 32px rgba(124, 111, 240, 0.48); }
  55%      { text-shadow: 0 0 22px rgba(124, 111, 240, 0.30); }
  71%      { text-shadow: 0 0 16px rgba(124, 111, 240, 0.22); }
  74%      { text-shadow: 0 0 34px rgba(124, 111, 240, 0.50); }
}

/* Travels once, then sits off-screen for the rest of the cycle. */
@keyframes charge-sweep {
  0%        { background-position: 180% 0; }
  22%, 100% { background-position: -80% 0; }
}

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

.hero__rest {
  color: var(--text);
  font-weight: 600;
  animation: fade-up 900ms var(--ease) both 700ms;
}

/* --- The rotating word -------------------------------------------------
   An inline-block slot whose width is set in px by hero-word.js. The width
   change is timed to land while the word is dissolved into particles, so
   nothing legible is sliding around when the line resizes — "lifts" and
   "focus" differ by ~47%, which would otherwise shove "your" sideways on
   every swap.

   The settled word is plain --text. It used to carry its state's colour, but
   "Supercharge" directly above is violet (--accent-text) and the steel, amber
   and teal words fought it — violet-on-violet read muddiest of all. The state
   colour now lives only in the PARTICLES between words, peaking at full
   scatter and resolving back to white before the new word appears: the same
   tie back to the four states, none of the clash.

   --word-tint is still set per state by hero-word.js, but it's a canvas-only
   signal now — nothing in CSS reads it. */
.hero__word {
  position: relative;
  display: inline-block;
  vertical-align: baseline;
  text-align: left;
  transition: width var(--dur-mid) var(--ease);
}

/* Hidden while the particles have the word, restored when they hand back. */
.hero__word-text {
  display: inline-block;
  transition: opacity 160ms linear;
}

.hero__word.is-dissolving .hero__word-text { opacity: 0; }

/* Sits over the slot; sized and positioned by the module.
   max-width: none defeats the `canvas { max-width: 100% }` reset in base.css,
   which would otherwise clamp this to the SLOT's width. The slot is sized to
   the current word but the canvas has to span the widest one — without this,
   swapping from "lifts" (narrow) to "focus" (wide) clips the incoming word's
   particles at the outgoing word's width. */
.hero__word-canvas {
  position: absolute;
  left: 0;
  top: 0;
  max-width: none;
  pointer-events: none;
}

/* Nothing to rotate without JS — the slot is just the word. */
.no-js .hero__word { width: auto !important; }

/* The letters open wide and draw together as they come into focus.
   ⚠ This animates `letter-spacing`, which DOES affect layout — a ~1.5x width
   swing at hero size. That is only safe because of two guards above:
     · .hero__emphasis / .hero__rest are `white-space: nowrap`, so a width
       change can never change the line count
     · .hero has `overflow-x: clip`, so the wide state can't produce a
       horizontal scrollbar
   Both were added for exactly this reason. If you remove either one, remove
   the letter-spacing here too, or the headline will visibly reflow on load.

   translateX cancels the trailing letter-space: letter-spacing adds a gap
   after the final character, which drags centred text half a space to the
   left. Without this the word slides sideways as it settles. */
@keyframes assemble-in {
  from {
    opacity: 0;
    letter-spacing: 0.24em;
    filter: blur(14px);
    transform: translateY(10px) scale(0.985) translateX(0.12em);
  }
  60% { filter: blur(2px); }
  to {
    opacity: 1;
    letter-spacing: var(--tracking-display);
    filter: blur(0);
    transform: none;
  }
}

@keyframes fade-up {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .hero__emphasis, .hero__rest { animation: none; }
}

/* Holds the status line and the state chips — no form since the header
   carries the CTA. Wider than the paragraph itself so the four chips sit on
   one row down to tablet widths. */
.hero__cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  max-width: 40rem;
  animation: fade-up 900ms var(--ease) both 1100ms;
}

@media (prefers-reduced-motion: reduce) {
  .hero__cta { animation: none; }
}

.hero__status {
  font-size: var(--fs-sm);
  color: var(--text-dim);
  max-width: 40ch;
  text-wrap: balance;
}

/* --- Scroll cue: a simplified mouse, not a chevron --- */

.scroll-cue {
  position: absolute;
  bottom: var(--space-7);
  left: 50%;
  transform: translateX(-50%);
  opacity: 1;
  transition: opacity var(--dur-slow) var(--ease);
}

.scroll-cue.is-hidden { opacity: 0; }

.scroll-cue__body {
  display: block;
  width: 22px;
  height: 36px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  position: relative;
}

.scroll-cue__dot {
  position: absolute;
  left: 50%;
  top: 7px;
  width: 2px;
  height: 6px;
  margin-left: -1px;
  border-radius: var(--r-pill);
  background: var(--accent-text);
  animation: scroll-cue 2400ms var(--ease) infinite;
}

/* Travels down and fades before reaching the bottom, then loops. */
@keyframes scroll-cue {
  0%        { transform: translateY(0);    opacity: 0; }
  15%       { opacity: 1; }
  70%       { transform: translateY(14px); opacity: 0; }
  100%      { transform: translateY(14px); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .scroll-cue__dot { animation: none; opacity: 0.8; }
}

/* --------------------------------------------------------------------------
   2 — Value proposition
   -------------------------------------------------------------------------- */

.value-prop { min-height: 100svh; display: grid; align-items: center; }

.value-prop__grid { display: grid; }

/* Left column only — the right half is left clear for the brain to occupy. */
@media (min-width: 60rem) {
  .value-prop__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr); }
  .value-prop__copy { grid-column: 1; }
}

.value-prop__copy {
  max-width: 40rem;
  animation: slide-in-left 900ms var(--ease) both;
  animation-timeline: view();
  animation-range: entry 0% entry 60%;
}

/* Browsers without scroll-driven animations just get the static layout;
   the .reveal class already handles progressive disclosure there. */
@supports not (animation-timeline: view()) {
  .value-prop__copy { animation: none; }
}

@keyframes slide-in-left {
  from { opacity: 0; transform: translateX(-40px); }
  to   { opacity: 1; transform: none; }
}

.value-prop__pull {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.02em;
  margin-top: var(--space-6);
  padding-left: var(--space-5);
  border-left: 2px solid var(--accent);
}

.value-prop__body { margin-top: var(--space-6); color: var(--text-dim); max-width: var(--measure); }

.value-prop__rebuttal { margin-top: var(--space-7); }

.value-prop__rebuttal-title {
  font-size: var(--fs-body);
  font-family: var(--font-display);
  margin-bottom: var(--space-3);
  color: var(--text);
}

.value-prop__rebuttal p { color: var(--text-dim); font-size: var(--fs-sm); line-height: var(--lh-loose); }

/* --------------------------------------------------------------------------
   Opaque sections — everything below the brain stage
   -------------------------------------------------------------------------- */

.section--opaque {
  background: var(--bg);
  position: relative;
  z-index: 2;
}

.section-head--center {
  margin-inline: auto;
  text-align: center;
  max-width: 46rem;
}

.section-head--center .eyebrow { justify-content: center; }
.section-head--center .lead { margin-inline: auto; }

/* --------------------------------------------------------------------------
   4 — The Kit (convergence diagram)
   -------------------------------------------------------------------------- */

.hiw__diagram { position: relative; }

#hiw-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.hiw__inputs {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(4, minmax(0, 1fr));
  position: relative;
  z-index: 1;
}

.hiw__input {
  position: relative;
  padding: var(--space-5);
  padding-bottom: var(--space-6);
  border: 1px solid var(--line);
  border-top: 2px solid var(--tint, var(--accent));
  border-radius: var(--r-md);
  background: linear-gradient(180deg, color-mix(in srgb, var(--tint) 9%, transparent), transparent 70%);
}

.hiw__step {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--fs-mono-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--tint, var(--accent));
  margin-bottom: var(--space-3);
}

.hiw__name { font-size: var(--fs-h3); margin-bottom: var(--space-2); }

.hiw__desc { font-size: var(--fs-sm); color: var(--text-dim); line-height: var(--lh-normal); }

/* Tier honesty — small and unobtrusive, but never omitted. */
.hiw__tier {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px dashed var(--line-strong);
  font-size: var(--fs-xs);
  color: var(--text-dim);
  font-style: italic;
}

/* The point where a line leaves the card — measured by the canvas. */
.hiw__port {
  position: absolute;
  bottom: -4px;
  left: 50%;
  transform: translateX(-50%);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--tint, var(--accent));
  box-shadow: 0 0 10px var(--tint, var(--accent));
}

.hiw__target {
  margin-top: clamp(11rem, 26vh, 17rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-5);
  position: relative;
  z-index: 1;
}

.hiw__node {
  width: 132px;
  height: 132px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  text-align: center;
  border: 1px solid var(--line-strong);
  background: radial-gradient(circle at 50% 40%, rgba(124, 111, 240, 0.20), var(--bg-raised) 70%);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: -0.02em;
  transition: box-shadow var(--dur-slow) var(--ease), border-color var(--dur-slow) var(--ease);
}

.hiw__node.is-live {
  border-color: var(--accent);
  box-shadow:
    0 0 0 1px rgba(124, 111, 240, 0.4),
    0 0 60px -10px rgba(124, 111, 240, 0.75),
    inset 0 0 30px -10px rgba(124, 111, 240, 0.6);
}

.hiw__node-note {
  max-width: 46ch;
  text-align: center;
  font-size: var(--fs-sm);
  color: var(--text-dim);
  line-height: var(--lh-loose);
}

.hiw__node-note strong { color: var(--accent-text); font-weight: 600; }

/* Stacked layout: inputs become rows, lines collect onto a left rail. */
@media (max-width: 60rem) {
  .hiw__inputs {
    grid-template-columns: 1fr;
    margin-left: 46px;
  }

  .hiw__input { padding-bottom: var(--space-5); }

  .hiw__port {
    bottom: auto;
    top: 50%;
    left: -1px;
    transform: translate(-50%, -50%);
  }

  .hiw__target { margin-top: var(--space-8); }
}

/* --------------------------------------------------------------------------
   App showcase — section is commented out in index.html, kept for restore
   -------------------------------------------------------------------------- */

.app__grid {
  display: grid;
  gap: var(--space-7) var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

.app__item { display: flex; flex-direction: column; gap: var(--space-4); }

.app__caption {
  font-size: var(--fs-sm);
  color: var(--text-dim);
  line-height: var(--lh-normal);
  text-align: center;
  max-width: 26ch;
  margin-inline: auto;
}

.app__tag {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--accent-3);
  border: 1px solid rgba(255, 122, 69, 0.4);
  border-radius: var(--r-pill);
  padding: 1px 8px;
  margin-bottom: var(--space-2);
}

/* --------------------------------------------------------------------------
   6 — Impact map
   --------------------------------------------------------------------------
   The hint used to have --desktop/--mobile variants, because the interaction
   genuinely differed (scroll vs. tap). Now both viewports get the same chips
   and the same markers, so it's one sentence.
   -------------------------------------------------------------------------- */

/* The state selector. Centred against the section head above it. */
.impact__states {
  list-style: none;
  margin: var(--space-6) 0 var(--space-8);
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.impact__layout {
  display: grid;
  gap: var(--space-7);
  align-items: center;
}

@media (min-width: 60rem) {
  .impact__layout { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

.impact__stage {
  position: relative;
  aspect-ratio: 3 / 5;
  max-height: 74svh;
  margin-inline: auto;
  width: 100%;
  max-width: 420px;
}

#impact-canvas { width: 100%; height: 100%; }

/* --- Markers --- */

.impact__marker {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1);
  border-radius: var(--r-pill);
  color: var(--marker-color, var(--accent));
  white-space: nowrap;
}

.impact__marker-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid currentColor;
  background: var(--bg);
  flex: none;
  transition: box-shadow var(--dur-mid) var(--ease), background-color var(--dur-mid) var(--ease);
}

.impact__marker.is-active .impact__marker-dot {
  background: currentColor;
  box-shadow: 0 0 0 4px color-mix(in srgb, currentColor 22%, transparent), 0 0 18px currentColor;
}

.impact__marker-label {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity var(--dur-mid) var(--ease), transform var(--dur-mid) var(--ease);
}

.impact__marker.is-active .impact__marker-label { opacity: 1; transform: none; }

/* --- Callouts --- */

.impact__callouts { display: grid; gap: var(--space-4); }

.impact__callout {
  padding: var(--space-5);
  border: 1px solid var(--line);
  border-left: 2px solid transparent;
  border-radius: var(--r-md);
  background: var(--bg-raised);
  opacity: 0.42;
  transition:
    opacity var(--dur-mid) var(--ease),
    border-color var(--dur-mid) var(--ease),
    transform var(--dur-mid) var(--ease);
}

.impact__callout.is-active {
  opacity: 1;
  border-left-color: var(--tint);
  transform: translateX(4px);
}

.impact__callout-name {
  font-size: var(--fs-body);
  font-family: var(--font-display);
  color: var(--tint);
  margin-bottom: var(--space-1);
}

.impact__callout-fn { font-size: var(--fs-sm); color: var(--text); margin-bottom: var(--space-2); }

.impact__callout-tied {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.impact__callout-tied span {
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  opacity: 0.7;
  margin-right: var(--space-2);
}

/* Mobile tap-to-reveal: only the active callout is shown, so the section
   doesn't become a wall of five cards. */
@media (max-width: 60rem) {
  .impact__callout { display: none; opacity: 1; }
  .impact__callout.is-active { display: block; }
  .impact__stage { max-height: 58svh; }
}

.impact__loop-note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  margin-top: var(--space-7);
  max-width: var(--measure);
  font-size: var(--fs-sm);
  color: var(--text-dim);
}

.impact__loop-mark {
  flex: none;
  width: 28px;
  height: 2px;
  margin-top: 0.7em;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
}

/* --------------------------------------------------------------------------
   DEAD — Science (no #science section since the reposition)
   -------------------------------------------------------------------------- */

.science__grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(288px, 1fr));
}

.science__viz {
  width: 100%;
  height: auto;
  margin-bottom: var(--space-5);
  opacity: 0.95;
}

.science__notes {
  display: grid;
  gap: var(--space-4);
  margin-top: var(--space-7);
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

.science__note {
  font-size: var(--fs-sm);
  color: var(--text-dim);
  line-height: var(--lh-loose);
}

.science__note strong { color: var(--text); font-weight: 600; }

.science__note--trust { border-color: rgba(94, 234, 212, 0.26); }
.science__note--trust strong { color: var(--accent-2); }

/* Horizontal scroll on narrow screens keeps the section scannable rather
   than turning it into six stacked walls of text. */
@media (max-width: 46rem) {
  .science__grid {
    grid-auto-flow: column;
    grid-auto-columns: 82%;
    grid-template-columns: none;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: var(--space-4);
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    scrollbar-width: thin;
  }

  .science__card { scroll-snap-align: start; }
}

/* --------------------------------------------------------------------------
   8 — Pricing
   -------------------------------------------------------------------------- */

.pricing__grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  max-width: 900px;
  margin-inline: auto;
}

.pricing__tier {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
  padding: var(--space-7) var(--space-6);
}

.pricing__tier--black {
  border-color: rgba(255, 122, 69, 0.26);
  background-image: var(--halftone), radial-gradient(120% 90% at 50% 0%, rgba(255, 122, 69, 0.09), transparent 60%);
}

.pricing__name { font-size: 2rem; }

.pricing__price { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2); }

.pricing__price-note {
  flex-basis: 100%;
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

.pricing__price-value {
  font-size: 2.25rem;
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.03em;
}

.pricing__price-period { color: var(--text-dim); font-size: var(--fs-sm); }

/* À la carte has no capacity bar between its pill and its price, so without
   this its price would sit a bar-plus-gap higher than the memberships'. The
   adjacent-sibling selector only matches when no bar is present, so this
   self-corrects if one is ever added. */
.pricing__tier .status-pill + .pricing__price { margin-top: calc(var(--space-4) + 3px); }

.pricing__features {
  display: grid;
  gap: var(--space-3);
  font-size: var(--fs-sm);
  color: var(--text-dim);
  line-height: var(--lh-normal);
  width: 100%;
}

.pricing__features li {
  position: relative;
  padding-left: var(--space-5);
}

.pricing__features li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  border: 1px solid var(--accent);
}

.pricing__tier--black .pricing__features li::before { border-color: var(--accent-3); }

.pricing__everything { color: var(--text); font-weight: 600; }

.pricing__addon {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  line-height: var(--lh-loose);
  padding: var(--space-4);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-md);
  width: 100%;
}

.pricing__addon strong { color: var(--text); }

/* Push the CTA to the bottom so both cards' buttons line up. */
.pricing__tier .btn { margin-top: auto; }

/* --------------------------------------------------------------------------
   9 — Founder story  (the deliberate quiet moment)
   -------------------------------------------------------------------------- */

.founder {
  position: relative;
  background: var(--bg-sunken);
  overflow: hidden;
}

/* A soft STATIC particle field — no motion at all. This section should read
   as one person speaking, not as another data visualization. */
.founder__field {
  position: absolute;
  inset: 0;
  background-image: radial-gradient(circle at center, rgba(242, 242, 240, 0.16) 0.6px, transparent 0.9px);
  background-size: 22px 22px;
  mask-image: radial-gradient(70% 55% at 50% 45%, #000 0%, transparent 78%);
  -webkit-mask-image: radial-gradient(70% 55% at 50% 45%, #000 0%, transparent 78%);
  opacity: 0.5;
  pointer-events: none;
}

.founder__inner { max-width: 44rem; }

.founder__heading { font-size: var(--fs-h3); color: var(--text-dim); font-weight: 500; margin-bottom: var(--space-7); }

.founder__prose {
  font-size: var(--fs-lead);
  line-height: 1.8;
  color: var(--text-dim);
}

.founder__prose p + p { margin-top: var(--space-6); }

.founder__pull {
  color: var(--text);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  line-height: 1.35;
  letter-spacing: -0.02em;
}

.founder__caveat {
  font-size: var(--fs-sm);
  color: var(--text-faint);
  border-left: 1px solid var(--line-strong);
  padding-left: var(--space-5);
}

.founder__sign {
  margin-top: var(--space-7);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  color: var(--text-dim);
  letter-spacing: 0.02em;
}

/* --------------------------------------------------------------------------
   10 — FAQ
   -------------------------------------------------------------------------- */

.faq__list { max-width: 52rem; }

.faq__list h3 { font-size: inherit; letter-spacing: normal; margin: 0; }

/* With JS off the panels stay open, so every answer remains readable. */
.no-js .accordion__panel { grid-template-rows: 1fr; }
.no-js .accordion__content { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   11 — Final CTA
   -------------------------------------------------------------------------- */

.final-cta {
  text-align: center;
  background:
    radial-gradient(95% 75% at 50% 100%, rgba(124, 111, 240, 0.07), transparent 78%),
    var(--bg);
}

/* Grain, painted as violet (not blended gray-on-violet — that washes toward
   white) by using the noise texture purely as an alpha mask over an actual
   violet fill. Three bands, stacked on a dedicated wrapper so the tile size
   steps up twice (fine -> medium -> coarse) instead of once — reading closer
   to a continuous widening the further it sits from the glow's origin than a
   single before/after pair can. Same noise asset throughout; only the mask
   tile size and paired ring gradient differ per layer. */
.final-cta__grain {
  opacity: 0.45;
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background: radial-gradient(95% 75% at 50% 100%, rgba(124, 111, 240, 0.30), transparent 90%);
  -webkit-mask-image: var(--noise-glow);
  mask-image: var(--noise-glow);
  -webkit-mask-size: 130px 130px;
  mask-size: 130px 130px;
}

.final-cta__grain::before,
.final-cta__grain::after {
  content: "";
  position: absolute;
  inset: 0;
  -webkit-mask-image: var(--noise-glow);
  mask-image: var(--noise-glow);
}

.final-cta__grain::before {
  background: radial-gradient(95% 75% at 50% 100%, transparent 0%, rgba(124, 111, 240, 0.20) 45%, transparent 90%);
  -webkit-mask-size: 300px 300px;
  mask-size: 300px 300px;
}

.final-cta__grain::after {
  background: radial-gradient(95% 75% at 50% 100%, transparent 0%, rgba(124, 111, 240, 0.14) 72%, transparent 98%);
  -webkit-mask-size: 560px 560px;
  mask-size: 560px 560px;
}

.final-cta__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-5);
  max-width: 46rem;
}

.final-cta .eyebrow { justify-content: center; }

.final-cta__heading { max-width: 20ch; }

.final-cta__lead { text-align: center; }

/* The inner column is `align-items: center`, which shrink-to-fits every child.
   Left alone the form collapses to the input's intrinsic ~20ch and a real email
   address scrolls out of view while it's being typed. Cap it instead — 36rem
   lands the input at ~398px, the same width as The Kit's. */
.final-cta__inner > waitlist-form {
  width: 100%;
  max-width: 36rem;
}

.final-cta__note {
  font-size: var(--fs-xs);
  color: var(--text-faint);
  letter-spacing: 0.06em;
}

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

.site-footer {
  position: relative;
  z-index: 2;
  background: var(--bg-sunken);
  border-top: 1px solid var(--line);
  padding-block: var(--space-7) var(--space-6);
}

.site-footer__top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  margin-bottom: var(--space-6);
}

.site-footer__nav { display: flex; gap: var(--space-6); }

.site-footer__nav a {
  color: var(--text-dim);
  text-decoration: none;
  font-size: var(--fs-sm);
}

.site-footer__nav a:hover { color: var(--text); text-decoration: underline; }

.site-footer__disclaimer {
  margin-top: var(--space-6);
  max-width: 78ch;
  color: var(--text-faint);
  line-height: var(--lh-normal);
}

.site-footer__legal { margin-top: var(--space-5); color: var(--text-faint); }

/* --------------------------------------------------------------------------
   Legal stub pages
   -------------------------------------------------------------------------- */

.legal { padding-block: calc(var(--space-10) + 1rem) var(--space-9); }

.legal__inner { max-width: 46rem; }

.legal h1 { font-size: var(--fs-h2); margin-bottom: var(--space-3); }

.legal__updated {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-bottom: var(--space-7);
}

.legal h2 {
  font-size: var(--fs-h3);
  margin-top: var(--space-7);
  margin-bottom: var(--space-3);
}

.legal p, .legal li { color: var(--text-dim); line-height: var(--lh-loose); }
.legal p + p { margin-top: var(--space-4); }

.legal ul { display: grid; gap: var(--space-2); margin-top: var(--space-3); }

.legal li { padding-left: var(--space-5); position: relative; }

.legal li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.7em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
}

/* Marks content that still needs a lawyer's eyes. Visible on purpose.
   Currently unused — the legal pages have been through counsel review — but kept
   so the mechanism is one class away if a page needs flagging again. */
.legal__todo {
  margin-top: var(--space-5);
  padding: var(--space-5);
  border: 1px dashed var(--accent-3);
  border-radius: var(--r-md);
  background: rgba(255, 122, 69, 0.05);
}

.legal__todo strong {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--accent-3);
  margin-bottom: var(--space-2);
}

.legal__todo p { color: var(--text-dim); font-size: var(--fs-sm); }

/* ==========================================================================
   HERO STATE CHIPS
   ========================================================================== */

/* The top margin used to clear the waitlist form that sat above these. With
   the form gone they follow the status line directly, so it comes down —
   space-4 plus the .hero__cta flex gap reads as one deliberate step. */
.hero__states {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
}

/* ==========================================================================
   3 — THE LINEUP
   ========================================================================== */

/* The states are the section's opener now that The Kit has moved to section 4,
   so this no longer needs to clear a card above it. */
.lineup__states {
  display: grid;
  gap: var(--space-8);
}

/* ---- One block per state ----------------------------------------------- */

.state-block {
  /* The tint as SMALL TEXT. Defaults to the tint itself — steel, amber and
     teal all clear 7.6:1 on --bg and need no help. Violet is the exception,
     overridden below. */
  --tint-text: var(--tint);

  display: grid;
  gap: var(--space-6);
  padding-top: var(--space-7);
  border-top: 1px solid var(--line);
  /* The state's colour bleeds in from the top-left of its own block only. */
  background: radial-gradient(
    70% 60% at 0% 0%,
    color-mix(in srgb, var(--tint) 8%, transparent),
    transparent 68%
  );
}

/* --state-focus (#7C6FF0) is 5.05:1 on --bg — it passes AA, but every other
   eyebrow on the page is --accent-text at 8.27:1, and tokens.css keeps that
   lighter violet around specifically for violet used as text at small sizes.
   Same hue, text weight. The big display "Focus" keeps the saturated tint. */
#state-focus { --tint-text: var(--accent-text); }

@media (min-width: 60rem) {
  .state-block {
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
    grid-template-areas:
      "viz     product"
      "copy    product";
    align-items: start;
    column-gap: var(--space-8);
  }
  .state-block__viz     { grid-area: viz; }
  .state-block__copy    { grid-area: copy; }
  .state-block__product { grid-area: product; align-self: center; }
}

/* The per-state canvas. It used to be a rounded 110px box sitting in the gap
   below the block's top rule, which read as a stray bar floating clear of the
   section. It now starts ON that rule — pulled up through the block's own
   padding, so only the viz moves and the product card in the right-hand column
   keeps its normal top inset — and dissolves down and to the right into the
   card. No box, no radius: the mask is the edge treatment.

   The mask origin (0% 0%) is deliberately the same corner the block's tint
   gradient washes from, so the two agree instead of fighting. */
.state-block__viz {
  --viz-mask: radial-gradient(
    125% 150% at 0% 0%,
    #000 0%,
    rgba(0, 0, 0, 0.88) 32%,
    rgba(0, 0, 0, 0.45) 62%,
    transparent 88%
  );

  position: relative;
  margin-top: calc(var(--space-7) * -1);
  height: 180px;
  overflow: hidden;
  mask-image: var(--viz-mask);
  -webkit-mask-image: var(--viz-mask);
}

/* Stacked layout: viz, copy, then product. A full-height band here would push
   the state's name below the fold on a phone. */
@media (max-width: 59.999rem) {
  .state-block__viz { height: 150px; }
}

.state-block__viz canvas { display: block; width: 100%; height: 100%; }

/* The block's eyebrow carries its own state, not the page accent — it sits
   directly above a state-tinted name and read as a mismatch in violet. The
   24px ::before rule follows through currentColor. */
.state-block .eyebrow { color: var(--tint-text); }

.state-block__name {
  margin-top: var(--space-2);
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: 600;
  letter-spacing: var(--tracking-display);
  color: var(--tint);
}

.state-block__tagline {
  margin-top: var(--space-2);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--text);
}

.state-block__copy > p:not([class]) {
  margin-top: var(--space-4);
  max-width: var(--measure);
  color: var(--text-dim);
  line-height: var(--lh-loose);
}

.state-block__when {
  margin-top: var(--space-5);
  font-size: var(--fs-sm);
  color: var(--text-dim);
}

/* Same small-mono case as the eyebrow, so it takes the same text-safe tint —
   otherwise the Focus block shows two different violets on two labels that
   are meant to match. */
.state-block__when span {
  display: inline-block;
  margin-right: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--tint-text);
}

.lineup__caffeine {
  margin-top: var(--space-8);
  padding: var(--space-5);
  border: 1px solid rgba(255, 122, 69, 0.28);
  border-radius: var(--r-md);
  background: rgba(255, 122, 69, 0.05);
  font-size: var(--fs-sm);
  line-height: var(--lh-loose);
  color: var(--text-dim);
}

/* ==========================================================================
   4 — THE KIT
   --------------------------------------------------------------------------
   The convergence diagram's own rules are the .hiw__* block up at "4 — The
   Kit (convergence diagram)". These are the card that sits below it.
   ========================================================================== */

/* Visually dominant: it's the one thing on the page you can actually buy. The
   margin separates it from the diagram directly above. */
.kit-card {
  position: relative;
  margin-top: var(--space-9);
  padding: var(--space-7);
  border: 1px solid rgba(124, 111, 240, 0.28);
  border-radius: var(--r-xl);
  background:
    radial-gradient(90% 120% at 12% 0%, rgba(124, 111, 240, 0.14), transparent 62%),
    var(--halftone) 0 0 / var(--halftone-size),
    var(--bg-raised);
  box-shadow: var(--glow-soft);
  display: grid;
  gap: var(--space-6);
  grid-template-areas:
    "meta"
    "figure"
    "body";
}

/* Two columns with one job each: the pack alone on the left, everything you
   read on the right. The stock pill spans both as its own top row, so it reads
   as a state of the whole card rather than of either column.

   The gap has to be split here. A uniform `gap` can't say "pill tight to the
   card top, columns 3rem apart" — row-gap only ever falls between meta and the
   figure/body row, column-gap only between the two columns. */
@media (min-width: 52rem) {
  .kit-card {
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    grid-template-areas:
      "meta   meta"
      "figure body";
    column-gap: var(--space-7);
    row-gap: var(--space-4);
    align-items: center;
    padding: var(--space-8);
  }
}

.kit-card__meta   { grid-area: meta; }
.kit-card__figure { grid-area: figure; }
.kit-card__body   { grid-area: body; }

/* .kit-card__meta needs no rules of its own: a plain block holding one
   inline-flex pill, which sits flush left unaided. It used to be a
   price-left/stock-right row hanging off the bottom of the pack; the price now
   belongs to the head, next to the name it prices. */

/* Nothing but the pack lives here now, but the centring still earns its keep —
   the figure is the shorter column, and the grid's `align-items: center`
   centres the box, not the art inside it. */
.kit-card__figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* The positioning context the 3D box parks into (css/sections.css,
     #kit-canvas). The flat pack art stays in flow and keeps setting this
     column's height, so the slot the box lands in is the same size either
     way — and the same size it was before the box existed. */
  position: relative;
}

/* Name left, price right, unit tucked under the name — the same three-cell grid
   the product cards use (.product-card__head, components.css). Mirrored rather
   than shared: the two are separate blocks, and every .kit-card__* rule lives
   here. `align-items: baseline` is what sits the price on the name's baseline
   rather than on its box. */
.kit-card__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: var(--space-4);
  margin-bottom: var(--space-5);
}

.kit-card__head .kit-card__name { grid-area: 1 / 1; }
.kit-card__head .price          { grid-area: 1 / 2; }

/* Spans the whole second row rather than sitting under the name like the
   product cards' does. Nothing shares that row, and the Kit's price is the
   page's largest — at 375px it takes enough of the head that a name-width unit
   wraps "30-day / supply". */
.kit-card__head .kit-card__unit { grid-area: 2 / 1 / 3 / -1; }

/* --fs-h3 tops out at 1.75rem, the price's size — so once there's room for
   both, name and price carry the same weight. */
.kit-card__name {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 600;
  letter-spacing: var(--tracking-display);
}

.kit-card__unit {
  margin-top: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--text-faint);
}

/* The page's one headline price, so it carries more than the product cards'. */
.kit-card__head .price { font-size: 1.75rem; }

/* Separated the way the product cards' action is. */
.kit-card__body > waitlist-form {
  display: block;
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}

.kit-card__blurb {
  font-size: var(--fs-lead);
  line-height: var(--lh-normal);
  color: var(--text-dim);
  max-width: 44ch;
}

.kit-card__contents {
  list-style: none;
  margin: var(--space-5) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.kit-card__contents li {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--text-dim);
}

/* ==========================================================================
   5 — CARRY FORMATS
   ========================================================================== */

.formats__grid {
  margin-top: var(--space-8);
  display: grid;
  gap: var(--space-6);
}

@media (min-width: 60rem) {
  .formats__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The nicotine SKU is set apart on purpose — full width, amber-edged, and
   after a visible gap, so it never reads as "just another one of the range". */
.product-card--restricted {
  margin-top: var(--space-8);
  border-color: rgba(255, 122, 69, 0.35);
  background:
    radial-gradient(100% 90% at 50% 0%, rgba(255, 122, 69, 0.07), transparent 70%),
    var(--halftone) 0 0 / var(--halftone-size),
    var(--bg-raised);
}

.product-card--restricted:hover { border-color: rgba(255, 122, 69, 0.5); }

@media (min-width: 48rem) {
  .product-card--restricted {
    display: grid;
    grid-template-columns: minmax(0, 0.6fr) minmax(0, 1.6fr);
    grid-template-areas:
      "meta meta"
      "pack body";
    align-items: start;
    gap: var(--space-7);
  }
  .product-card--restricted > .product-card__meta { grid-area: meta; }
  .product-card--restricted > .pack { grid-area: pack; margin-top: var(--space-4); }
  .product-card--restricted > .product-card__body { grid-area: body; }
}

/* ==========================================================================
   7 — QUALITY & TESTING
   ========================================================================== */

.quality__grid {
  list-style: none;
  margin: var(--space-8) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

/* ==========================================================================
   8 — TIERS: three columns
   ========================================================================== */

/* Three cards need a smaller minimum than two did, or the grid drops to a
   single column far too early. Verified at 900-1100px, where it goes to two
   columns with the third wrapping rather than squeezing all three. */
.pricing__grid--three {
  max-width: 1140px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr));
}

/* Between roughly 640px and 1136px the grid resolves to two columns, which
   would leave VIP orphaned alone on a second row. Let it span instead — the
   premium tier reading as the widest card is the right hierarchy anyway, and
   it removes the ragged gap. Harmless in the single-column case, where
   1 / -1 is just the one column. */
@media (max-width: 71rem) {
  .pricing__tier--vip { grid-column: 1 / -1; }
}

.pricing__tier--light { opacity: 0.94; }

.pricing__tier--vip {
  border-color: rgba(255, 122, 69, 0.28);
  background:
    radial-gradient(100% 70% at 50% 0%, rgba(255, 122, 69, 0.09), transparent 70%),
    var(--halftone) 0 0 / var(--halftone-size),
    var(--bg-raised);
}
