/* ═══════════════════════════════════════════════════════════
   Blue Arctic V6 · /affiliate/
   ────────────────────────────
   Page chrome ONLY. Everything is scoped to `.af-*`, so loading
   affiliate.css cannot move a pixel on any other page. Nothing
   here redefines a shared component, and nothing here is hero
   CSS: the hero markup uses the .bahero contract from hero.css
   unchanged and this file only styles what sits inside its
   media slot.

   ── SURFACES ──────────────────────────────────────────────
   Three carbon anchors carry the page: the hero, the earnings
   band and the closing block. Light sections sit between them.
   Every dark surface is --carbon-* (matte black, charcoal,
   graphite). --navy-* is NOT used anywhere in this file, and
   the shared navy .cta-band is deliberately not used by this
   page and is not modified by it.

   ── MOTION ────────────────────────────────────────────────
   Every animation lives inside a
   `@media (prefers-reduced-motion: no-preference)` block and is
   additive. The static state is the finished design: nothing is
   hidden waiting for an animation, so a reduced-motion reader,
   a print, or a browser that never fires the observer all get a
   complete page. Animations are transform/opacity only.

   Mobile-first, per the project layout rules: base rules are
   the phone composition, wider steps live in min-width queries.
   ═══════════════════════════════════════════════════════════ */


/* ═══════════════ 0 · SHARED ═══════════════ */

/* The recurring mark. One glyph, three sizes, used wherever the
   word "recurring" appears, so it becomes this program's
   signature rather than decoration. Optical alignment only. */
.af-recur { flex: none; vertical-align: -.14em; }

.af-cta-inline {
  display: flex;
  flex-direction: column;
  gap: .6rem;
  margin-top: var(--space-band);
}
.af-cta-inline .btn { width: 100%; }
@media (min-width: 560px) {
  .af-cta-inline { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: .8rem; }
  .af-cta-inline .btn { width: auto; }
}

.af-note-small {
  max-width: 44rem;
  margin-top: 1.25rem;
  font-size: var(--fs-tiny);
  line-height: 1.6;
  color: var(--ink-3);
}
.af-note-small--center { margin-inline: auto; text-align: center; }


/* ═══════════════ 1 · HERO ═══════════════ */

/* The bonus chip. Secondary to the headline by construction: it
   sits below the lead, its figure is a fraction of the H1 size,
   and the blue is the accent rather than the surface. The 25%
   must stay the loudest thing in this hero. */
.af-hero-bonus {
  display: flex;
  align-items: center;
  gap: .85rem;
  padding: .8rem 1rem;
  border: 1px solid var(--carbon-700);
  border-left: 3px solid var(--sky-400);
  border-radius: var(--r-md);
  background: linear-gradient(90deg, rgba(95, 185, 238, .10), rgba(95, 185, 238, 0) 70%);
}
.af-hero-bonus-amt {
  flex: none;
  font-family: var(--font-display);
  font-size: 1.7rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--sky-400);
  font-variant-numeric: tabular-nums;
}
.af-hero-bonus-txt {
  font-size: var(--fs-tiny);
  line-height: 1.5;
  color: var(--steel-400);
}
.af-hero-bonus-txt strong {
  display: block;
  font-size: var(--fs-small);
  font-weight: 700;
  color: var(--silver-100);
}

/* ── Hero illustration ─────────────────────────────────────
   Real DOM, not an exported graphic: the figures stay selectable
   text, it reflows on a phone instead of shrinking, it renders in
   the page's own typeface and it costs no extra request. */
.af-viz { margin: 0; }

.af-viz-panel {
  position: relative;
  padding: 1.25rem 1.15rem 1.15rem;
  border: 1px solid var(--carbon-700);
  border-radius: var(--r-lg);
  background:
    radial-gradient(120% 80% at 78% -10%, rgba(95, 185, 238, .11), transparent 60%),
    linear-gradient(170deg, var(--carbon-850) 0%, var(--carbon-900) 100%);
  box-shadow: 0 24px 60px -30px rgba(0, 0, 0, .9), inset 0 1px 0 var(--carbon-hair);
}
@media (min-width: 560px) {
  .af-viz-panel { padding: 1.6rem 1.5rem 1.4rem; }
}

.af-viz-label {
  font-size: var(--fs-tiny);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--steel-400);
}

/* Three referral nodes. Equal width so the connector geometry
   below lands on their centres at every viewport. */
.af-viz-nodes {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .5rem;
  margin-top: .7rem;
}
.af-viz-node {
  display: grid;
  place-items: center;
  gap: .3rem;
  padding: .7rem .35rem;
  min-width: 0;
  border: 1px solid var(--carbon-700);
  border-radius: var(--r-sm);
  background: var(--carbon-800);
  color: var(--sky-400);
}
.af-viz-node span {
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--steel-400);
}

/* Connectors. Stretched with preserveAspectRatio="none" on
   purpose: this is a diagram, so distorting the paths to meet
   the nodes is the correct behaviour, not a defect. */
.af-viz-wires {
  display: block;
  width: 100%;
  height: 2.6rem;
  margin: -1px 0;
}
.af-wire {
  fill: none;
  stroke: var(--carbon-700);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}
.af-wire--trunk { stroke: var(--sky-400); stroke-width: 2; opacity: .8; }

.af-viz-rate {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .45rem;
  padding: .5rem .8rem;
  margin-inline: auto;
  width: fit-content;
  max-width: 100%;
  border: 1px solid rgba(95, 185, 238, .35);
  border-radius: var(--r-pill);
  background: rgba(95, 185, 238, .1);
  color: var(--sky-400);
  font-size: var(--fs-tiny);
  line-height: 1.3;
  text-align: center;
}
.af-viz-rate strong { font-weight: 800; }
.af-viz-rate span { color: var(--steel-200); }
.af-viz-rate strong { color: var(--sky-400); }

/* The account panel: the payoff of the whole diagram. */
.af-viz-account {
  margin-top: .9rem;
  padding: 1rem .95rem .9rem;
  border: 1px solid var(--carbon-700);
  border-radius: var(--r-md);
  background: var(--carbon-800);
}
.af-viz-acct-label {
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--steel-400);
}
.af-viz-acct-total {
  margin-top: .15rem;
  font-family: var(--font-display);
  font-size: clamp(2.1rem, 9vw, 2.6rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.035em;
  color: var(--silver-100);
  font-variant-numeric: tabular-nums;
}

/* Six stepped bars: recurring accumulating month over month.
   Heights are authored, because this is a picture of a shape
   rather than a data set, and the caption says so. */
.af-viz-bars {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  align-items: end;
  gap: .3rem;
  height: 3rem;
  margin-top: .85rem;
}
.af-viz-bar {
  display: block;
  height: var(--h);
  border-radius: 3px 3px 1px 1px;
  background: linear-gradient(180deg, var(--sky-400), var(--blue-600));
  opacity: .85;
  transform-origin: bottom center;
}
.af-viz-bar:last-child { opacity: 1; }

.af-viz-bonus {
  display: flex;
  align-items: center;
  gap: .45rem;
  margin-top: .9rem;
  padding-top: .8rem;
  border-top: 1px solid var(--carbon-700);
  font-size: var(--fs-tiny);
  color: var(--steel-200);
}
.af-viz-bonus .ico { flex: none; color: var(--ok-600); }
.af-viz-bonus em {
  margin-left: auto;
  font-style: normal;
  font-size: .6875rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--steel-400);
}

.af-viz-cap {
  margin-top: .7rem;
  font-size: .6875rem;
  line-height: 1.5;
  color: var(--steel-400);
  text-align: center;
}
@media (min-width: 901px) {
  .af-viz-cap { text-align: left; }
}


/* ═══════════════ 2 · CARBON BANDS ═══════════════
   The shared .section-* classes are built for light surfaces, so
   the two dark content bands get their own shell. Same rhythm
   tokens, same container, different canvas. */
.af-band {
  position: relative;
  padding-block: var(--space-section-lg);
  background-color: var(--carbon-900);
  background-image:
    radial-gradient(900px 420px at 78% -18%, rgba(95, 185, 238, .10), transparent 62%),
    linear-gradient(178deg, var(--carbon-950) 0%, var(--carbon-900) 55%, var(--carbon-850) 100%);
  color: var(--steel-200);
  border-block: 1px solid var(--carbon-800);
}
/* blue-500 is invisible on carbon: match the site-wide dark-surface ring. */
.af-band :focus-visible { outline-color: var(--sky-400); }

/* The commission band butts straight onto the hero so the two read
   as one campaign block. .bahero paints a blue hairline along its
   own bottom edge; a border here would double it. */
.af-band--rates { border-top: 0; padding-top: var(--space-section-md); }

/* Centred at every width, matching .section-head-center on the light
   sections. Left-aligned on mobile and centred on desktop looked like a
   breakpoint someone forgot rather than a decision. */
.af-band-head {
  max-width: 44rem;
  margin-inline: auto;
  margin-bottom: var(--space-component);
  text-align: center;
}
.af-band-head h2 { color: var(--silver-100); margin-bottom: .5rem; }
.af-band-head .eyebrow { margin-bottom: .7rem; justify-content: center; }
.af-band-lead { color: var(--steel-400); font-size: var(--fs-small); }

.af-band-note {
  margin-top: var(--space-band);
  font-size: var(--fs-tiny);
  line-height: 1.6;
  color: var(--steel-400);
}
.af-band-note a { color: var(--sky-400); font-weight: 600; }
.af-band-note--wide { max-width: 46rem; }
.af-band-note { text-align: center; max-width: 46rem; margin-inline: auto; }


/* ═══════════════ 3 · COMMISSION RATE CARDS ═══════════════
   The core financial offer, styled as one. Oversized numerals,
   the recurring mark at display size, and the two rates at equal
   structural weight. The 25% carries the accent because it is the
   headline; the 10% is not shrunk, because presenting the second
   rate as a caveat is what would make the page untrustworthy. */
.af-rates {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-card-gap);
}
@media (min-width: 780px) {
  .af-rates { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
}

.af-rate {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 1.5rem 1.4rem 1.4rem;
  border: 1px solid var(--carbon-700);
  border-radius: var(--r-lg);
  background: linear-gradient(165deg, var(--carbon-800), var(--carbon-850));
  overflow: hidden;
}
@media (min-width: 560px) {
  .af-rate { padding: 1.9rem 1.75rem 1.7rem; }
}
/* A blue edge and a wash, not a bigger box: hierarchy without
   making the 10% card look like the consolation prize. */
.af-rate--featured {
  border-color: rgba(95, 185, 238, .45);
  background:
    radial-gradient(120% 100% at 100% 0%, rgba(95, 185, 238, .16), transparent 58%),
    linear-gradient(165deg, var(--carbon-800), var(--carbon-850));
  box-shadow: 0 0 0 1px rgba(95, 185, 238, .18), 0 30px 70px -40px rgba(45, 121, 245, .8);
}
.af-rate--featured::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: linear-gradient(90deg, var(--sky-400), var(--blue-600));
}

.af-rate-num {
  display: flex;
  align-items: baseline;
  gap: .04em;
  font-family: var(--font-display);
  font-weight: 800;
  line-height: .86;
  letter-spacing: -0.045em;
  color: var(--silver-100);
  font-variant-numeric: tabular-nums;
}
.af-rate-fig { font-size: clamp(4.4rem, 21vw, 6.5rem); }
.af-rate-pct { font-size: clamp(1.9rem, 8vw, 2.7rem); font-weight: 700; letter-spacing: -0.02em; color: var(--steel-400); }
@media (min-width: 560px) {
  .af-rate-fig { font-size: clamp(5rem, 9vw, 6.5rem); }
  .af-rate-pct { font-size: clamp(2.1rem, 3.6vw, 2.7rem); }
}
.af-rate--featured .af-rate-fig { color: #fff; }
.af-rate--featured .af-rate-pct { color: var(--sky-400); }

/* RECURRING, given the weight the word is owed. */
.af-rate-recur {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  align-self: flex-start;
  margin-top: .75rem;
  padding: .4rem .85rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--carbon-700);
  background: rgba(255, 255, 255, .04);
  font-size: var(--fs-tiny);
  font-weight: 800;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--steel-200);
}
.af-rate--featured .af-rate-recur {
  border-color: rgba(95, 185, 238, .4);
  background: rgba(95, 185, 238, .12);
  color: var(--sky-400);
}

.af-rate-list {
  display: grid;
  gap: .1rem;
  margin-top: 1.4rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--carbon-700);
}
.af-rate-list li { min-width: 0; }
.af-rate-list a,
.af-rate-list span {
  display: block;
  padding: .42rem 0;
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--steel-200);
}
.af-rate-list a { color: var(--silver-100); text-decoration: none; }
.af-rate-list a:hover { color: var(--sky-400); text-decoration: underline; text-underline-offset: 3px; }

.af-rate-foot {
  margin-top: auto;
  padding-top: 1.3rem;
  font-size: var(--fs-tiny);
  color: var(--steel-400);
}
.af-rate-foot a { color: var(--sky-400); font-weight: 600; white-space: nowrap; }


/* ═══════════════ 4 · $10 HEAD START ═══════════════ */
.af-ladder {
  display: grid;
  grid-template-columns: 1fr;
  gap: .75rem;
  counter-reset: none;
}
@media (min-width: 720px) {
  .af-ladder { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-card-gap); }
}

.af-step {
  position: relative;
  min-width: 0;
  padding: 1.25rem 1.25rem 1.15rem;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow-soft);
}
.af-step--now {
  border-color: var(--blue-500);
  background: linear-gradient(160deg, var(--ice-100), var(--paper) 70%);
  box-shadow: 0 0 0 1px var(--blue-500), var(--shadow-lift);
}
.af-step--goal { border-style: dashed; border-color: var(--line-2); background: var(--mist); box-shadow: none; }

.af-step-k {
  font-size: var(--fs-tiny);
  font-weight: 800;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.af-step-v {
  margin-top: .25rem;
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 11vw, 3rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.035em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.af-step--now .af-step-v { color: var(--blue-600); }
.af-step-d {
  margin-top: .5rem;
  font-size: var(--fs-tiny);
  line-height: 1.55;
  color: var(--ink-2);
}

/* The rail: the same claim as the sentence above it, drawn. */
.af-rail { max-width: 40rem; margin: var(--space-band) auto 0; }
.af-rail-track {
  position: relative;
  height: 2.35rem;
  border-radius: var(--r-pill);
  background: var(--frost);
  border: 1px solid var(--line);
  overflow: hidden;
}
.af-rail-fill {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  width: var(--pct);
  height: 100%;
  padding-right: .8rem;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--blue-600), var(--sky-400));
  transform-origin: left center;
}
.af-rail-fill b {
  font-family: var(--font-display);
  font-size: var(--fs-small);
  font-weight: 800;
  color: #fff;
  font-variant-numeric: tabular-nums;
}
.af-rail-ends {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin-top: .5rem;
  font-size: var(--fs-tiny);
  font-weight: 600;
  color: var(--ink-3);
}


/* ═══════════════ 5 · ONE-TIME v RECURRING ═══════════════ */
.af-compare {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-card-gap);
  align-items: start;
}
@media (min-width: 800px) {
  .af-compare { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.af-track {
  min-width: 0;
  padding: 1.4rem 1.35rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--paper);
}
.af-track--recur {
  border-color: var(--blue-500);
  box-shadow: 0 0 0 1px var(--blue-500), var(--shadow-lift);
}

.af-track-h {
  display: flex;
  align-items: center;
  gap: .45rem;
  padding-bottom: .9rem;
  margin-bottom: 1.1rem;
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-small);
  font-weight: 700;
  color: var(--ink-3);
}
.af-track--recur .af-track-h { color: var(--blue-700); }

.af-track-list { display: grid; gap: 0; }

/* Each tick is a row on a vertical spine. The spine is drawn by the
   dot's ::after so it always spans exactly to the next dot, with no
   fixed heights to keep in sync. */
.af-tick {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: .7rem;
  padding: .6rem 0;
}
.af-tick-dot {
  position: relative;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--line-2);
  flex: none;
}
.af-track--recur .af-tick-dot { background: var(--blue-600); }
.af-tick:not(:last-child) .af-tick-dot::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  width: 2px;
  height: calc(100% + 1.2rem);
  transform: translateX(-50%);
  background: var(--line);
}
.af-track--recur .af-tick:not(:last-child) .af-tick-dot::after { background: var(--blue-100); }

.af-tick-l { font-size: var(--fs-small); color: var(--ink-2); min-width: 0; }
.af-tick-v {
  font-size: var(--fs-tiny);
  font-weight: 700;
  color: var(--ink);
  text-align: right;
  white-space: nowrap;
}
.af-track--recur .af-tick-v { color: var(--blue-700); }

/* The one-time track stops, and looks like it stops. */
.af-tick--stop .af-tick-dot { background: var(--paper); border: 2px solid var(--line-2); }
.af-tick--stop .af-tick-l,
.af-tick--stop .af-tick-v { color: var(--ink-3); }
.af-tick--stop .af-tick-v { font-weight: 600; }

/* The recurring track does not stop: the last row fades rather than
   terminating, which is the whole argument in one detail. */
.af-tick--cont .af-tick-dot {
  background: var(--sky-400);
  box-shadow: 0 0 0 4px rgba(95, 185, 238, .22);
}
.af-tick--cont .af-tick-l { font-weight: 600; color: var(--blue-700); }
.af-tick--cont .af-tick-v { color: var(--blue-600); }

.af-track-f {
  margin-top: 1rem;
  padding-top: .9rem;
  border-top: 1px solid var(--line);
  font-size: var(--fs-tiny);
  color: var(--ink-3);
}
.af-track--recur .af-track-f { color: var(--ink-2); }


/* ═══════════════ 6 · EARNINGS ═══════════════ */

/* Three rungs, ascending. On desktop they step up physically, so
   the shape of the section carries the argument before the numbers
   are read. */
.af-rungs {
  display: grid;
  grid-template-columns: 1fr;
  gap: .7rem;
}
@media (min-width: 720px) {
  .af-rungs { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-card-gap); align-items: end; }
  .af-rung { margin-bottom: calc(var(--i) * .9rem); }
}

.af-rung {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  column-gap: .85rem;
  padding: 1rem 1.15rem;
  border: 1px solid var(--carbon-700);
  border-radius: var(--r-md);
  background: linear-gradient(160deg, var(--carbon-800), var(--carbon-850));
}
@media (min-width: 720px) {
  .af-rung { grid-template-columns: minmax(0, 1fr); row-gap: .1rem; padding: 1.3rem 1.35rem; }
}
.af-rung--top {
  border-color: rgba(95, 185, 238, .4);
  background:
    radial-gradient(130% 110% at 100% 0%, rgba(95, 185, 238, .15), transparent 60%),
    linear-gradient(160deg, var(--carbon-800), var(--carbon-850));
}

.af-rung-n {
  font-family: var(--font-display);
  font-size: 2rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--sky-400);
  font-variant-numeric: tabular-nums;
}
.af-rung-k {
  font-size: var(--fs-tiny);
  font-weight: 600;
  color: var(--steel-400);
}
.af-rung-v {
  grid-column: 1 / -1;
  display: flex;
  align-items: baseline;
  gap: .3rem;
  margin-top: .6rem;
  padding-top: .6rem;
  border-top: 1px solid var(--carbon-700);
}
@media (min-width: 720px) {
  .af-rung-n { font-size: 2.4rem; }
  .af-rung-v { margin-top: .9rem; padding-top: .9rem; }
}
.af-rung-v strong {
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 6vw, 2.1rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--silver-100);
  font-variant-numeric: tabular-nums;
}
.af-rung-v span { font-size: var(--fs-small); font-weight: 600; color: var(--steel-400); }


/* ── Calculator ────────────────────────────────────────────
   Ships with the `hidden` attribute; affiliate.js removes it. The
   [hidden] rule is load-bearing because `display` on .af-calc
   would otherwise beat the attribute's UA default. */
.af-calc[hidden] { display: none; }

.af-calc {
  display: block;
  margin-top: var(--space-band);
  overflow: hidden;
  border: 1px solid var(--carbon-700);
  border-radius: var(--r-lg);
  background: linear-gradient(165deg, var(--carbon-850), var(--carbon-900));
  box-shadow: 0 30px 70px -40px rgba(0, 0, 0, .9);
}
.af-calc-head {
  padding: 1.1rem 1.25rem;
  border-bottom: 1px solid var(--carbon-700);
  background: rgba(255, 255, 255, .025);
}
.af-calc-head h3 {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: var(--fs-h4);
  color: var(--silver-100);
}
.af-calc-head .af-recur { color: var(--sky-400); }

.af-calc-body { display: grid; grid-template-columns: 1fr; }
@media (min-width: 880px) {
  .af-calc-body { grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); }
}

.af-calc-controls {
  display: grid;
  gap: 1.5rem;
  align-content: start;
  padding: 1.4rem 1.25rem;
}
@media (min-width: 880px) {
  .af-calc-controls { padding: 1.9rem 1.75rem; }
}

.af-field label {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .5rem;
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--steel-200);
}
.af-field-val {
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--sky-400);
  font-variant-numeric: tabular-nums;
}

/* Range input. Reset per engine, then one shared visual. The track
   is 6px but the control keeps a 44px hit area through padding, so
   the thumb is comfortably draggable on a phone without the bar
   looking heavy. */
.af-range {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  margin: .8rem 0 .3rem;
  padding-block: .6rem;
  background: transparent;
  cursor: pointer;
  touch-action: pan-y;      /* vertical scroll still works over the slider */
}
.af-range::-webkit-slider-runnable-track {
  height: 6px; border-radius: var(--r-pill);
  background: var(--carbon-700);
}
.af-range::-moz-range-track {
  height: 6px; border-radius: var(--r-pill);
  background: var(--carbon-700);
}
.af-range::-moz-range-progress {
  height: 6px; border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--blue-600), var(--sky-400));
}
.af-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 22px; height: 22px;
  margin-top: -8px;         /* centres a 22px thumb on a 6px track */
  border-radius: 50%;
  background: var(--sky-400);
  border: 3px solid var(--carbon-850);
  box-shadow: 0 0 0 1px rgba(95, 185, 238, .5), 0 2px 6px rgba(0, 0, 0, .6);
  transition: transform var(--dur-fast) var(--ease-out);
}
.af-range::-moz-range-thumb {
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--sky-400);
  border: 3px solid var(--carbon-850);
  box-shadow: 0 0 0 1px rgba(95, 185, 238, .5), 0 2px 6px rgba(0, 0, 0, .6);
  transition: transform var(--dur-fast) var(--ease-out);
}
.af-range:hover::-webkit-slider-thumb,
.af-range:active::-webkit-slider-thumb { transform: scale(1.1); }
.af-range:hover::-moz-range-thumb,
.af-range:active::-moz-range-thumb { transform: scale(1.1); }
/* The thumb is the focus target, so the ring goes on the thumb and
   not around the full-width control, where it would read as a bug. */
.af-range:focus { outline: none; }
.af-range:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 4px rgba(95, 185, 238, .45), 0 2px 6px rgba(0, 0, 0, .6);
}
.af-range:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 4px rgba(95, 185, 238, .45), 0 2px 6px rgba(0, 0, 0, .6);
}
/* Forced colours does not paint box-shadow, and the rule above drops
   the UA outline, so the slider would have no visible focus state
   there at all. Put a real outline back for that mode only. */
@media (forced-colors: active) {
  .af-range:focus-visible { outline: 2px solid ButtonText; outline-offset: 2px; }
}
@media (prefers-reduced-motion: reduce) {
  .af-range::-webkit-slider-thumb,
  .af-range::-moz-range-thumb { transition: none; }
  .af-range:hover::-webkit-slider-thumb,
  .af-range:active::-webkit-slider-thumb,
  .af-range:hover::-moz-range-thumb,
  .af-range:active::-moz-range-thumb { transform: none; }
}

.af-field-scale {
  display: flex;
  justify-content: space-between;
  font-size: var(--fs-tiny);
  color: var(--steel-400);
}

.af-calc-rate {
  display: flex;
  align-items: flex-start;
  gap: .45rem;
  font-size: var(--fs-tiny);
  line-height: 1.55;
  color: var(--steel-400);
}
.af-calc-rate .af-recur { margin-top: .15rem; color: var(--sky-400); }

/* The result column: the one place on the page where blue is the
   surface rather than the accent, because it is the answer the
   visitor came for. */
.af-calc-result {
  display: grid;
  align-content: center;
  gap: 1.1rem;
  padding: 1.5rem 1.25rem;
  border-top: 1px solid var(--carbon-700);
  background:
    radial-gradient(120% 100% at 80% 0%, rgba(45, 121, 245, .28), transparent 62%),
    linear-gradient(160deg, #0D2A5C 0%, var(--carbon-850) 78%);
}
@media (min-width: 880px) {
  .af-calc-result { padding: 1.9rem 1.75rem; border-top: 0; border-left: 1px solid var(--carbon-700); }
}
.af-calc-k {
  font-size: var(--fs-tiny);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #A9C7EC;
}
.af-calc-v {
  margin-top: .15rem;
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 11vw, 3.2rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  color: #fff;
  font-variant-numeric: tabular-nums;
}
.af-calc-out--yr { padding-top: 1.1rem; border-top: 1px solid rgba(255, 255, 255, .14); }
.af-calc-out--yr .af-calc-v { font-size: clamp(1.6rem, 6vw, 1.95rem); color: #DCE9FB; }

/* The button keeps --blue-600 on carbon: white on --blue-500 measures
   4.07:1, under AA, and a CTA is the last place to spend contrast.
   The hover state lifts instead, where the label flips to carbon. */
.af-cta-inline--dark .btn-primary:hover { background: var(--sky-400); color: var(--carbon-950); box-shadow: none; }


/* ═══════════════ 7 · GROWTH ═══════════════
   A dot matrix, not a bar chart: the unit is a referral, so the
   picture is made of referrals. Each column contains the previous
   column's dots plus the new ones, which is the whole point. */
.af-grow {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-card-gap);
}
@media (min-width: 700px) {
  .af-grow { grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: start; }
}

.af-grow-col {
  min-width: 0;
  padding: 1.25rem 1.2rem 1.15rem;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow-soft);
}
.af-grow-col:last-child { border-color: var(--blue-500); box-shadow: 0 0 0 1px var(--blue-500), var(--shadow-lift); }

.af-grow-when {
  font-size: var(--fs-tiny);
  font-weight: 800;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* Fixed 5-across so the three columns read as one growing block
   rather than three differently-shaped clouds. min-height keeps the
   card tops aligned when the dot counts differ wildly. */
.af-grow-dots {
  display: grid;
  grid-template-columns: repeat(5, 12px);
  gap: 7px;
  margin: .9rem 0 1rem;
  min-height: 5.5rem;
  align-content: start;
}
.af-grow-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--blue-600);
}
.af-grow-col:last-child .af-grow-dot { background: linear-gradient(160deg, var(--blue-600), var(--sky-400)); }

.af-grow-n { font-size: var(--fs-small); color: var(--ink-2); }
.af-grow-n strong { font-family: var(--font-display); font-size: 1.3rem; font-weight: 800; color: var(--ink); letter-spacing: -0.02em; }
.af-grow-v {
  display: flex;
  align-items: center;
  gap: .45rem;
  margin-top: .75rem;
  padding-top: .75rem;
  border-top: 1px solid var(--line);
  font-size: var(--fs-small);
  font-weight: 700;
  color: var(--blue-700);
}
.af-grow-v .af-recur { color: var(--blue-600); }


/* ═══════════════ 8 · HOW IT WORKS ═══════════════ */
.af-steps {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-card-gap);
  counter-reset: none;
}
@media (min-width: 760px) {
  .af-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.af-how {
  position: relative;
  min-width: 0;
  padding: 1.4rem 1.35rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow-soft);
}
.af-how h3 { font-size: var(--fs-h4); margin-bottom: .35rem; }
.af-how p { font-size: var(--fs-small); color: var(--ink-2); }
/* The step numeral is a <p>, so it has to out-specify the rule above it.
   Without the parent selector `.af-how p` won on specificity and painted
   the numerals --ink-2 on --ink: 2.3:1, unreadable. */
.af-how .af-how-n {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  margin-bottom: .85rem;
  border-radius: 50%;
  background: var(--ink);
  color: #fff;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 800;
}

.af-how-badge {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  margin-top: .9rem;
  padding: .35rem .8rem;
  border-radius: var(--r-pill);
  background: var(--blue-100);
  border: 1px solid var(--ice-200);
  font-size: var(--fs-tiny);
  font-weight: 700;
  color: var(--blue-700);
}
.af-how-badge--recur .af-recur { color: var(--blue-600); }


/* ═══════════════ 9 · AUDIENCE ═══════════════ */
.af-agency {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.75rem;
  align-items: center;
  padding: 1.6rem 1.4rem;
  border: 1px solid var(--ice-200);
  border-radius: var(--r-xl);
  background:
    radial-gradient(110% 130% at 100% 0%, rgba(95, 185, 238, .18), transparent 58%),
    var(--ice-100);
}
@media (min-width: 900px) {
  .af-agency { grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); gap: 2.75rem; padding: 2.4rem 2.25rem; }
}
.af-agency-copy h2 { margin-bottom: .6rem; }
.af-agency-copy .lead { margin-bottom: .8rem; }
.af-agency-copy p { color: var(--ink-2); }
.af-agency-copy a { font-weight: 600; }

/* Three identical rows: the repetition IS the argument. */
.af-flow { display: grid; gap: .6rem; min-width: 0; }
.af-flow-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 12px minmax(0, 1fr) 12px minmax(0, 1fr);
  align-items: center;
  gap: .35rem;
  padding: .55rem .6rem;
  border: 1px solid var(--paper);
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .72);
  box-shadow: var(--shadow-soft);
}
.af-flow-node {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .3rem;
  min-width: 0;
  padding: .4rem .3rem;
  border-radius: var(--r-sm);
  font-size: .6875rem;
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
}
.af-flow-node .ico, .af-flow-node .af-recur { flex: none; }
.af-flow-node--client { background: var(--frost); color: var(--ink-2); }
.af-flow-node--ba     { background: var(--ink); color: #fff; }
.af-flow-node--pay    { background: var(--blue-600); color: #fff; }
.af-flow-arrow {
  width: 12px; height: 2px;
  border-radius: 1px;
  background: var(--line-2);
}
@media (max-width: 430px) {
  /* Three labels plus two arrows across a narrow phone. The labels are
     worth more than the arrows are, so the arrows give up their width
     first and the type tightens; nothing is hidden and nothing clips. */
  .af-flow-row { grid-template-columns: minmax(0, 1fr) 8px minmax(0, 1fr) 8px minmax(0, 1fr); gap: .25rem; padding: .5rem .45rem; }
  .af-flow-node { font-size: .625rem; gap: .22rem; padding: .4rem .2rem; }
  .af-flow-node .ico, .af-flow-node .af-recur { width: 13px; height: 13px; }
  .af-flow-arrow { width: 8px; }
}

.af-who {
  display: flex;
  flex-wrap: wrap;
  gap: .55rem;
  margin-top: var(--space-band);
  justify-content: center;
}
.af-who-chip {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .55rem 1rem;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--paper);
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--ink-2);
  box-shadow: var(--shadow-soft);
}
.af-who-chip .ico { color: var(--blue-600); flex: none; }


/* ═══════════════ 10 · WHY RECOMMEND ═══════════════ */
.af-why {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-card-gap);
}
@media (min-width: 620px) { .af-why { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .af-why { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.af-why-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-rows: auto auto;
  column-gap: .85rem;
  align-items: center;
  min-width: 0;
  padding: 1.25rem 1.2rem;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow-soft);
  color: inherit;
  text-decoration: none;
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out), border-color var(--dur-fast);
}
.af-why-card:hover {
  transform: translateY(-3px);
  border-color: var(--ice-200);
  box-shadow: var(--shadow-lift);
  text-decoration: none;
}
.af-why-card:hover .af-why-go { color: var(--blue-600); transform: translateX(3px); }
/* Every cell is placed explicitly. Auto-placement cannot be trusted
   here: two of the four children span both rows, and letting the
   browser flow the other two around them put the heading in the
   arrow's column and squeezed the description to a third of the
   card. Three columns, two rows, nothing left to inference. */
.af-why-ico {
  grid-column: 1;
  grid-row: 1 / span 2;
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: var(--r-md);
  background: var(--ice-100);
  border: 1px solid var(--ice-200);
  color: var(--blue-600);
}
.af-why-card strong {
  grid-column: 2;
  grid-row: 1;
  align-self: end;
  font-size: 1.0625rem;
  color: var(--ink);
  line-height: 1.3;
}
.af-why-d {
  grid-column: 2;
  grid-row: 2;
  align-self: start;
  margin-top: .2rem;
  font-size: var(--fs-small);
  color: var(--ink-2);
  line-height: 1.5;
}
.af-why-go {
  grid-column: 3;
  grid-row: 1 / span 2;
  color: var(--line-2);
  transition: color var(--dur-fast), transform var(--dur-fast) var(--ease-out);
}
@media (prefers-reduced-motion: reduce) {
  .af-why-card, .af-why-go { transition: none; }
  .af-why-card:hover { transform: none; }
  .af-why-card:hover .af-why-go { transform: none; }
}


/* ═══════════════ 11 · REVIEWS ═══════════════ */
.af-reviews {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-card-gap);
}
@media (min-width: 820px) { .af-reviews { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.af-review {
  display: flex;
  flex-direction: column;
  min-width: 0;
  margin: 0;
  padding: 1.4rem 1.35rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow-soft);
}
.af-review-stars { display: flex; gap: .1rem; margin-bottom: .8rem; }
.af-review .af-star { color: #F5A623; fill: currentColor; }
.af-review blockquote { margin: 0; }
.af-review blockquote p {
  font-size: var(--fs-small);
  line-height: 1.65;
  color: var(--ink-2);
}
.af-review figcaption {
  display: grid;
  gap: .1rem;
  margin-top: auto;
  padding-top: 1.1rem;
}
.af-review figcaption strong { font-size: var(--fs-small); color: var(--ink); }
.af-review figcaption span { font-size: var(--fs-tiny); color: var(--ink-3); }
.af-review-src { font-weight: 600; }

/* The platform row is .trust-row from hero.css, which is authored for
   a dark hero. On this light section it needs its own text colours,
   and centring. Scoped to .af-trust so no other page is touched. */
.af-trust {
  display: flex;
  justify-content: center;
  margin-top: var(--space-band);
  --trust-name-color: var(--ink);
  --trust-rule-color: var(--line-2);
  --trust-star-empty: var(--line);
}
.af-trust .trust-row { justify-content: center; flex-wrap: wrap; gap: .75rem 1.75rem; }
.af-trust .trust-name { color: var(--ink); }
.af-trust .trust-meta,
.af-trust .trust-score,
.af-trust .trust-linktext { color: var(--ink-3); }
.af-trust .trust-item:hover .trust-name,
.af-trust .trust-item:hover .trust-meta { color: var(--blue-600); }


/* ═══════════════ 12 · TOOLKIT ═══════════════ */
.af-tools {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-card-gap);
}
@media (min-width: 620px) { .af-tools { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 980px) { .af-tools { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.af-tool {
  min-width: 0;
  padding: 1.3rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--paper);
  box-shadow: var(--shadow-soft);
}
.af-tool-ico {
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  margin-bottom: .8rem;
  border-radius: var(--r-md);
  background: var(--ink);
  color: var(--sky-400);
}
.af-tool strong { display: block; font-size: 1.0625rem; color: var(--ink); margin-bottom: .3rem; }
.af-tool p { font-size: var(--fs-small); color: var(--ink-2); }
.af-tool .link-arrow { margin-top: .75rem; font-size: var(--fs-tiny); }


/* ═══════════════ 13 · DISCLOSURE ═══════════════
   One quiet paragraph. Legible, complete, and visually subordinate:
   the earnings warning lives once, beside the numbers it qualifies. */
.af-disclosure {
  max-width: 58rem;
  margin-inline: auto;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
  font-size: var(--fs-tiny);
  line-height: 1.7;
  color: var(--ink-3);
  text-align: center;
}
.af-disclosure a { font-weight: 600; color: var(--blue-600); }


/* ═══════════════ 14 · CLOSING ═══════════════
   Page-scoped. The shared navy .cta-band is not used and not
   modified. Concentric rings behind the copy restate the hero's
   recurring language so the page closes where it opened. */
.af-close {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-block: var(--space-section-lg);
  background-color: var(--carbon-950);
  background-image:
    radial-gradient(760px 380px at 50% 8%, rgba(45, 121, 245, .20), transparent 66%),
    linear-gradient(178deg, var(--carbon-950) 0%, var(--carbon-900) 60%, var(--carbon-850) 100%);
  color: var(--steel-200);
  text-align: center;
}
.af-close :focus-visible { outline-color: var(--sky-400); }

.af-close-orbit {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: -1;
  width: min(46rem, 130vw);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  pointer-events: none;
  opacity: .5;
}
.af-close-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px solid rgba(95, 185, 238, .16);
}
.af-close-ring--2 { inset: 14%; border-color: rgba(95, 185, 238, .12); }
.af-close-ring--3 { inset: 28%; border-color: rgba(95, 185, 238, .09); }

.af-close-inner { max-width: 44rem; margin-inline: auto; }
.af-close .eyebrow { justify-content: center; margin-bottom: .9rem; }
.af-close h2 {
  font-size: clamp(2rem, 5.4vw, 3rem);
  line-height: 1.12;
  letter-spacing: -0.03em;
  color: #fff;
  margin-bottom: .9rem;
}
.af-close h2 em { font-style: normal; color: var(--sky-400); }
.af-close-lead {
  font-size: var(--fs-lead);
  line-height: 1.6;
  color: var(--steel-400);
  max-width: 38rem;
  margin-inline: auto;
}
.af-close-ctas {
  display: flex;
  flex-direction: column;
  gap: .6rem;
  margin-top: 1.9rem;
}
.af-close-ctas .btn { width: 100%; }
@media (min-width: 560px) {
  .af-close-ctas { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: .8rem; }
  .af-close-ctas .btn { width: auto; }
}
.af-close .btn-primary:hover { background: var(--sky-400); color: var(--carbon-950); box-shadow: none; }

.af-close-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .5rem .65rem;
  margin-top: 1.6rem;
}
.af-close-chips span {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .4rem .9rem;
  border: 1px solid var(--carbon-700);
  border-radius: var(--r-pill);
  font-size: var(--fs-tiny);
  font-weight: 600;
  color: var(--steel-200);
}
.af-close-chips .af-recur { color: var(--sky-400); }


/* ═══════════════ 15 · MOTION ═══════════════
   Additive only. Every rule below has a static counterpart above,
   so the reduced-motion state is the finished design rather than a
   degraded one. .js gates the reveal animations so a browser
   without JS never has content stuck at opacity 0.

   The reveal classes are added by affiliate.js through an
   IntersectionObserver; .is-in is what triggers them. */
@media (prefers-reduced-motion: no-preference) {

  /* Hero: the diagram assembles once, top to bottom, in the order a
     reader would follow it. */
  /* These start at opacity 0, so they are gated on .js AND on a CSS
     animation that always runs to completion on load: there is no
     observer in this path and therefore nothing that can fail to fire.
     `forwards` holds the finished state. */
  .js .af-viz-node { opacity: 0; animation: af-pop var(--dur-slow) var(--ease-out) forwards; animation-delay: calc(120ms + var(--n) * 90ms); }
  .js .af-viz-rate { opacity: 0; animation: af-pop var(--dur-slow) var(--ease-out) .45s forwards; }
  .js .af-viz-account { opacity: 0; animation: af-pop var(--dur-slow) var(--ease-out) .6s forwards; }
  .js .af-viz-bar { transform: scaleY(0); animation: af-rise var(--dur-slow) var(--ease-out) forwards; animation-delay: calc(750ms + var(--i) * 70ms); }

  /* The trunk wire keeps flowing: recurring, stated as motion. */
  .af-wire--trunk {
    stroke-dasharray: 5 7;
    animation: af-flow 1.5s linear infinite;
  }

  /* Sections that reveal on scroll.
     ⚠ The hiding is gated on .af-reveal, which affiliate.js adds to each
     container AT THE MOMENT it starts observing it, and never before.
     That ordering is the whole safety property: if the script fails to
     parse, if IntersectionObserver is missing, or if the observer never
     fires for any reason, .af-reveal is simply never added and the
     content renders normally. Gating on .js instead would leave three
     whole sections permanently at opacity 0 for anyone whose observer
     did not run. affiliate.js also force-reveals everything after a
     short backstop timeout, so nothing can stay hidden. */
  .af-reveal .af-step,
  .af-reveal .af-rung,
  .af-reveal .af-grow-col,
  .af-reveal .af-track { opacity: 0; transform: translateY(14px); transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out); }
  .af-reveal .af-step { transition-delay: calc(var(--i, 0) * 80ms); }
  .af-reveal.is-in .af-step,
  .af-reveal.is-in .af-rung,
  .af-reveal.is-in .af-grow-col,
  .af-reveal.is-in .af-track { opacity: 1; transform: none; }
  .af-reveal.is-in .af-rung { transition-delay: calc(var(--i) * 90ms); }

  /* The rail fills from zero once it is on screen. */
  .af-reveal.af-rail .af-rail-fill { transform: scaleX(0); transition: transform 900ms var(--ease-out); }
  .af-reveal.af-rail.is-in .af-rail-fill { transform: scaleX(1); }

  /* Growth dots land one after another, so the columns visibly
     accumulate rather than appearing fully formed. */
  .af-reveal .af-grow-dot { opacity: 0; transform: scale(.4); }
  .af-reveal.is-in .af-grow-dot {
    animation: af-dot 380ms var(--ease-out) forwards;
    animation-delay: calc(var(--g) * 260ms + var(--d) * 26ms);
  }

  .js .af-close-ring { animation: af-breathe 9s ease-in-out infinite alternate; }
  .js .af-close-ring--2 { animation-duration: 11s; }
  .js .af-close-ring--3 { animation-duration: 13s; }
}

@keyframes af-pop    { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes af-rise   { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes af-dot    { from { opacity: 0; transform: scale(.4); } to { opacity: 1; transform: scale(1); } }
@keyframes af-flow   { to { stroke-dashoffset: -24; } }
@keyframes af-breathe{ from { transform: scale(1); } to { transform: scale(1.045); } }
