/* ════════════════════════════════════════════════════════════
   Thryvera marketing — scroll-driven FX layer
   Every effect is scrubbed by a `--p` (0→1) custom property
   that scroll-fx.js writes on each rAF tick.
   Reverts cleanly as you scroll back up.
   ════════════════════════════════════════════════════════════ */

/* No transitions on FX surfaces — they're scrubbed, not eased. */
html.fx-ready .fx-em,
html.fx-ready .fx-step,
html.fx-ready .fx-pillar,
html.fx-ready .fx-row td,
html.fx-ready .fx-bubble-big,
html.fx-ready .fx-bubble-big .quotemark,
html.fx-ready .fx-bubble-small,
html.fx-ready .fx-ritual-half .ritual__step,
html.fx-ready .price-card .fx-check,
html.fx-ready .fx-outcome,
html.fx-ready .fx-tier-rule,
html.fx-ready .fx-lift,
html.fx-ready .fx-eyebrow,
html.fx-ready .fx-h2 {
  transition: none !important;
}

/* ───────── 0 · Top hairline progress bar ───────── */
#fx-progress-bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 2px;
  transform-origin: 0 50%;
  transform: scaleX(0);
  background: linear-gradient(
    90deg,
    rgba(var(--brand-violet-rgb), 0.85),
    rgba(var(--brand-pink-rgb), 0.95)
  );
  z-index: 1000;
  pointer-events: none;
  will-change: transform;
}

/* ───────── 1 · Italic emphasis underlines ───────── */
/* Pink hairline that draws under every italic <em> word in big headlines. */
html.fx-ready .fx-em {
  position: relative;
  display: inline-block;
}
html.fx-ready .fx-em::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  bottom: -0.06em;
  height: 2px;
  background: rgba(var(--brand-pink-rgb), 0.95);
  transform-origin: 0 50%;
  transform: scaleX(var(--p, 0));
  border-radius: 2px;
}
/* On the violet-deep CTA, the em is already pink-tinted; underline goes pink-cream. */
html.fx-ready .final-cta h2 .fx-em::after {
  background: rgba(var(--brand-pink-rgb), 0.95);
}

/* ───────── 2 · Section eyebrows ───────── */
html.fx-ready .fx-eyebrow {
  opacity: clamp(0, var(--p, 0), 1);
  transform: translateY(calc((1 - clamp(0, var(--p, 0), 1)) * 6px));
}

/* ───────── 3 · Section display-2 headlines ───────── */
html.fx-ready .fx-h2 {
  opacity: clamp(0, var(--p, 0), 1);
  transform: translateY(calc((1 - clamp(0, var(--p, 0), 1)) * 18px));
}

/* ───────── 4 · Process steps + drawn timeline ───────── */
html.fx-ready .process__steps {
  /* JS sets --line-p (0..1) for the timeline draw */
  position: relative;
  border-left-color: transparent !important;
}
html.fx-ready .process__steps::before {
  content: "";
  position: absolute;
  left: -1px;
  top: 0;
  width: 1px;
  background: linear-gradient(
    to bottom,
    rgba(var(--brand-violet-rgb), 0.55) 0%,
    rgba(var(--brand-violet-rgb), 0.18) 100%
  );
  height: calc(var(--line-p, 0) * 100%);
  transition: none;
}
html.fx-ready .fx-step {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: var(--p-local);
  transform: translateX(calc((1 - var(--p-local)) * 18px));
}
html.fx-ready .fx-step::before {
  /* the "01" digit dot — pop scale */
  transform: translateX(-50%) scale(calc(0.6 + var(--p-local, 0) * 0.4));
  transform-origin: 50% 50%;
  opacity: var(--p-local, 0);
}

/* ───────── 5 · Pillars ───────── */
html.fx-ready .fx-pillar {
  --p-local: clamp(0, calc(var(--p, 0) * 1.6 - var(--i, 0) * 0.18), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * 20px));
}
html.fx-ready .fx-pillar .icon {
  stroke-dasharray: 100;
  stroke-dashoffset: calc((1 - var(--p-local, 0)) * 100);
  opacity: clamp(0.2, var(--p-local, 0), 1);
}

/* ───────── 6 · Comparison table — editorial one-shot reveal ─────────
   Triggered by IntersectionObserver in scroll-fx.js (sets data-fx="in"
   when the table's top crosses the upper third of the viewport; clears
   it when the table leaves, so scrolling back replays the stagger).

   Design intent: the violet "Thryvera" ribbon must always read as ONE
   continuous shape — never a stack of independently-scaling pillars.
   So we animate WHOLE ROWS as units. Each <tr> lifts up + fades in;
   the violet ribbon segment for that row arrives along with it. The
   ribbon ends up looking like a single piece that gets revealed by
   a horizon line sweeping down the table.

   Sequence (≈0.95s total — tightened from 1.6s; old timing felt laggy
   because IO fired late AND the internal stagger was long, so the table
   was still revealing well after the user had scrolled into it):
     0ms   ─ headers cascade in left→right (slight drop from above)
     100ms ─ first body row lifts in
     +70ms each subsequent row
     +160ms ─ pink us-dot pops in for that row (overshoot easing) */

/* Initial state — rows + headers hidden, ready to be revealed. */
html.fx-ready .compare thead th {
  opacity: 0;
  transform: translateY(-6px);
  will-change: opacity, transform;
}
html.fx-ready .compare tbody tr {
  opacity: 0;
  transform: translateY(14px);
  will-change: opacity, transform;
}
html.fx-ready .compare tbody .us-dot::before {
  transform: scale(0);
  will-change: transform;
}

/* Per-column index on header cells, used by the header stagger delay. */
html.fx-ready .compare thead th               { --col-i: 0; }
html.fx-ready .compare thead th:nth-child(2)  { --col-i: 1; }
html.fx-ready .compare thead th:nth-child(3)  { --col-i: 2; }
html.fx-ready .compare thead th:nth-child(4)  { --col-i: 3; }
html.fx-ready .compare thead th:nth-child(5)  { --col-i: 4; }

/* ──── Reveal — fires when data-fx="in" lands on the table. ──── */
html.fx-ready .compare[data-fx="in"] thead th {
  animation: fxCompareHeader 420ms cubic-bezier(.2,.7,.2,1) both;
  animation-delay: calc(var(--col-i, 0) * 50ms);
}
html.fx-ready .compare[data-fx="in"] tbody tr {
  animation: fxCompareRow 520ms cubic-bezier(.2,.75,.2,1) both;
  animation-delay: calc(100ms + var(--row-i, 0) * 70ms);
}
html.fx-ready .compare[data-fx="in"] tbody .us-dot::before {
  animation: fxCompareDot 360ms cubic-bezier(.3,1.5,.5,1) both;
  animation-delay: calc(260ms + var(--row-i, 0) * 70ms);
}

@keyframes fxCompareHeader {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes fxCompareRow {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes fxCompareDot {
  from { transform: scale(0); }
  to   { transform: scale(1); }
}

/* ───────── 7 · Testimonials chat-bubble entrance ───────── */
html.fx-ready .fx-bubble-big {
  --p-local: clamp(0, var(--p, 0), 1);
  transform-origin: 8% 92%;
  opacity: var(--p-local);
  transform:
    translate3d(
      calc((1 - var(--p-local)) * -14px),
      calc((1 - var(--p-local)) * 36px),
      0
    )
    rotate(calc((1 - var(--p-local)) * -2.2deg))
    scale(calc(0.9 + var(--p-local) * 0.1));
  will-change: transform, opacity;
}
html.fx-ready .fx-bubble-big .quotemark {
  opacity: clamp(0, calc(var(--p, 0) * 1.6 - 0.55), 1);
  transform: scale(clamp(0.45, calc(var(--p, 0) * 1.05), 1));
  transition: none;
}
html.fx-ready .fx-bubble-small {
  --p-local: clamp(0, var(--p, 0), 1);
  transform-origin: 92% 50%;
  opacity: var(--p-local);
  transform:
    translate3d(
      calc((1 - var(--p-local)) * 14px),
      calc((1 - var(--p-local)) * 22px),
      0
    )
    scale(calc(0.94 + var(--p-local) * 0.06));
  will-change: transform, opacity;
}

/* ───────── 8 · Ritual step dots (per-half stagger) ───────── */
html.fx-ready .fx-ritual-half .ritual__step {
  --step-p: clamp(0, calc(var(--p, 0) * 3.5 - var(--i, 0) * 0.7), 1);
  opacity: var(--step-p);
  transform: translateX(calc((1 - var(--step-p)) * -10px));
}
html.fx-ready .fx-ritual-half .ritual__step .dot {
  transform: scale(calc(0.55 + var(--step-p, 0) * 0.45));
}

/* ───────── 9 · Pricing card checkmarks ───────── */
html.fx-ready .price-card .fx-check {
  --check-p: clamp(0, calc(var(--p, 0) * 3 - var(--i, 0) * 0.55), 1);
  opacity: var(--check-p);
  transform: translateX(calc((1 - var(--check-p)) * -10px));
}
html.fx-ready .price-card .fx-check svg {
  stroke-dasharray: 24;
  stroke-dashoffset: calc((1 - var(--check-p, 0)) * 24);
}

/* ───────── 10 · Outcomes — timeline labels slide in ───────── */
html.fx-ready .fx-outcome {
  --p-local: clamp(0, calc(var(--p, 0) * 1.6 - var(--i, 0) * 0.22), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * 24px));
}
html.fx-ready .fx-outcome .timeline-label {
  /* magnetic slide on the big italic word */
  transform: translateX(calc((1 - var(--p-local, 0)) * 18px));
  opacity: clamp(0.15, var(--p-local, 0), 1);
}

/* ───────── 11 · Tier rules ───────── */
html.fx-ready .fx-tier-rule {
  transform-origin: 0 50%;
  transform: scaleX(clamp(0, var(--p, 0), 1));
}

/* ───────── 12 · Lift-in cards (ingredients / formulas / price / care / faq) ───────── */
html.fx-ready .fx-lift {
  --p-local: clamp(0, calc(var(--p, 0) * 1.5 - var(--i, 0) * 0.08), 1);
  opacity: clamp(0.0, var(--p-local), 1);
  transform: translateY(calc((1 - var(--p-local)) * 18px));
}

/* ───────── 13 · Final CTA gradient line ───────── */
html.fx-ready .final-cta .gradient-line {
  transform-origin: 0 50%;
  transform: scaleX(clamp(0, var(--p, 0), 1));
  width: 160px;
}

/* ═════════════════════════════════════════════════════════════
   ADDITIONAL FX
   ════════════════════════════════════════════════════════════ */

/* Shared transition kill for new fx surfaces */
html.fx-ready .fx-fade-up,
html.fx-ready .fx-mark,
html.fx-ready .fx-veil,
html.fx-ready .fx-pin,
html.fx-ready .fx-badge,
html.fx-ready .fx-portrait,
html.fx-ready .fx-portrait-mark,
html.fx-ready .fx-quote,
html.fx-ready .fx-sig,
html.fx-ready .fx-tier-num,
html.fx-ready .fx-care-pillar,
html.fx-ready .fx-shield,
html.fx-ready .fx-risk-text,
html.fx-ready .fx-ribbon,
html.fx-ready .fx-cta-btn,
html.fx-ready .fx-foot-col {
  transition: none !important;
}

/* ───── Hero secondary stagger ───── */
html.fx-ready .fx-fade-up {
  --p-local: clamp(0, calc(var(--p, 0) * 1.7 - var(--i, 0) * 0.13), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * 14px));
  will-change: transform, opacity;
}

/* ───── Hero brand mark slow drift-rotation ─────
   The mark is positioned with left/top 50% + translate(-50%,-50%) for
   centering; we preserve that offset and add the drift rotation on top. */
html.fx-ready .fx-mark {
  transform: translate(-50%, -50%) rotate(var(--rot, 0deg));
  transform-origin: 50% 50%;
  will-change: transform;
}

/* ───── Manhattan map veil ───── */
html.fx-ready .fx-veil {
  opacity: clamp(0.15, var(--p, 0), 1);
}

/* ───── Manhattan pin: drop in, then the existing pulse takes over ───── */
html.fx-ready .fx-pin {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: var(--p-local);
  /* drop from above + settle (pin is positioned by top/left, no -50% offset) */
  transform: translateY(calc((1 - var(--p-local)) * -28px))
             scale(calc(0.55 + var(--p-local) * 0.45));
  transform-origin: 50% 50%;
}

/* ───── Manhattan "Same-day" badge: scale + fade-up ───── */
html.fx-ready .fx-badge {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * 22px))
             scale(calc(0.92 + var(--p-local) * 0.08));
  transform-origin: 100% 100%;
}

/* ───── Founder portrait: mask reveal + subtle scale ───── */
html.fx-ready .fx-portrait {
  --p-local: clamp(0, var(--p, 0), 1);
  /* a soft top-down reveal using clip-path */
  clip-path: inset(calc((1 - var(--p-local)) * 100%) 0 0 0 round 18px);
}
html.fx-ready .fx-portrait > img {
  transform: scale(calc(1.08 - var(--p, 0) * 0.08));
  transform-origin: 50% 50%;
  will-change: transform;
}
html.fx-ready .fx-portrait-mark {
  opacity: clamp(0, var(--p, 0), 1);
  transform: translateY(calc((1 - clamp(0, var(--p, 0), 1)) * 8px));
}

/* ───── Founder quote (display serif) ───── */
html.fx-ready .fx-quote {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: clamp(0.05, var(--p-local), 1);
  transform: translateY(calc((1 - var(--p-local)) * 12px));
}
html.fx-ready .fx-sig {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * 10px));
}

/* ───── Tier numerals (01 / 02) — pop ───── */
html.fx-ready .fx-tier-num {
  display: inline-block;
  transform-origin: 0 100%;
  opacity: clamp(0, var(--p, 0), 1);
  transform: scale(calc(0.6 + clamp(0, var(--p, 0), 1) * 0.4));
}

/* ───── Care pillars stagger ───── */
html.fx-ready .fx-care-pillar {
  --p-local: clamp(0, calc(var(--p, 0) * 1.6 - var(--i, 0) * 0.22), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * 18px));
}

/* ───── Risk-reversal shield: gentle rotate-in ───── */
html.fx-ready .fx-shield {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: var(--p-local);
  transform: rotate(calc((1 - var(--p-local)) * -18deg))
             scale(calc(0.78 + var(--p-local) * 0.22));
  transform-origin: 50% 50%;
}
html.fx-ready .fx-risk-text {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: var(--p-local);
  transform: translateX(calc((1 - var(--p-local)) * 14px));
}

/* ───── Pricing ribbon: pop in once card is ~70% in ───── */
html.fx-ready .fx-ribbon {
  --p-local: clamp(0, calc(var(--p, 0) * 1.4 - 0.35), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * -8px))
             scale(calc(0.7 + var(--p-local) * 0.3));
  transform-origin: 0 100%;
}

/* ───── Final CTA buttons stagger ───── */
html.fx-ready .fx-cta-btn {
  --p-local: clamp(0, calc(var(--p, 0) * 1.6 - var(--i, 0) * 0.2), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * 14px));
}

/* ───── Footer columns stagger ───── */
html.fx-ready .fx-foot-col {
  --p-local: clamp(0, calc(var(--p, 0) * 1.5 - var(--i, 0) * 0.18), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * 14px));
}

/* ═════════════════════════════════════════════════════════════
   ADDITIONAL FX — round 2
   ════════════════════════════════════════════════════════════ */

html.fx-ready .fx-lede,
html.fx-ready .fx-copy,
html.fx-ready .fx-ritual-time,
html.fx-ready .fx-ritual-h3,
html.fx-ready .fx-ritual-visual,
html.fx-ready .fx-ingr-role,
html.fx-ready .fx-ingr-desc,
html.fx-ready .fx-price-meta,
html.fx-ready .fx-collection-tab {
  transition: none !important;
}

/* ───── Lede paragraphs — slow fade-up under each h2 ───── */
html.fx-ready .fx-lede {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: clamp(0, calc(var(--p-local) * 1.05), 1);
  transform: translateY(calc((1 - var(--p-local)) * 10px));
}

/* ───── Body copy ───── */
html.fx-ready .fx-copy {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: clamp(0, calc(var(--p-local) * 1.1), 1);
  transform: translateY(calc((1 - var(--p-local)) * 8px));
}

/* ───── Ritual time badges (Morning / Evening) ───── */
html.fx-ready .fx-ritual-time {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * 6px))
             scale(calc(0.85 + var(--p-local) * 0.15));
  transform-origin: 0 50%;
}

/* ───── Ritual headings (Morning ritual / Evening ritual) ───── */
html.fx-ready .fx-ritual-h3 {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * 12px));
}

/* ───── Ritual sun/moon glyphs — soft rotate-in ───── */
html.fx-ready .fx-ritual-visual {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: clamp(0.2, var(--p-local), 1);
  transform: rotate(calc((1 - var(--p-local)) * -22deg))
             scale(calc(0.7 + var(--p-local) * 0.3));
  transform-origin: 50% 50%;
}

/* ───── Ingredient sub-text ───── */
html.fx-ready .fx-ingr-role {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: clamp(0, calc(var(--p-local) * 1.2 - 0.05), 1);
  transform: translateY(calc((1 - var(--p-local)) * 6px));
}
html.fx-ready .fx-ingr-desc {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: clamp(0, calc(var(--p-local) * 1.2 - 0.15), 1);
  transform: translateY(calc((1 - var(--p-local)) * 8px));
}

/* ───── Price card sub-text ───── */
html.fx-ready .fx-price-meta {
  --p-local: clamp(0, var(--p, 0), 1);
  opacity: clamp(0, calc(var(--p-local) * 1.15 - 0.08), 1);
  transform: translateY(calc((1 - var(--p-local)) * 6px));
}

/* ───── Collection tabs stagger ───── */
html.fx-ready .fx-collection-tab {
  --p-local: clamp(0, calc(var(--p, 0) * 1.5 - var(--i, 0) * 0.2), 1);
  opacity: var(--p-local);
  transform: translateY(calc((1 - var(--p-local)) * 8px));
}

/* Reduced-motion safety net for new effects */
html.fx-reduced .fx-fade-up,
html.fx-reduced .fx-veil,
html.fx-reduced .fx-pin,
html.fx-reduced .fx-badge,
html.fx-reduced .fx-portrait-mark,
html.fx-reduced .fx-quote,
html.fx-reduced .fx-sig,
html.fx-reduced .fx-tier-num,
html.fx-reduced .fx-care-pillar,
html.fx-reduced .fx-shield,
html.fx-reduced .fx-risk-text,
html.fx-reduced .fx-ribbon,
html.fx-reduced .fx-cta-btn,
html.fx-reduced .fx-foot-col,
html.fx-reduced .fx-lede,
html.fx-reduced .fx-copy,
html.fx-reduced .fx-ritual-time,
html.fx-reduced .fx-ritual-h3,
html.fx-reduced .fx-ritual-visual,
html.fx-reduced .fx-ingr-role,
html.fx-reduced .fx-ingr-desc,
html.fx-reduced .fx-price-meta,
html.fx-reduced .fx-collection-tab {
  opacity: 1 !important;
  transform: none !important;
}
html.fx-reduced .fx-portrait { clip-path: none !important; }
html.fx-reduced .fx-portrait > img { transform: none !important; }
html.fx-reduced .fx-mark { transform: none !important; }

/* Reduce-motion — JS strips fx-ready, but as a safety net force everything visible. */
html.fx-reduced .fx-em::after,
html.fx-reduced .fx-tier-rule,
html.fx-reduced .final-cta .gradient-line { transform: none !important; }
html.fx-reduced .fx-step,
html.fx-reduced .fx-pillar,
html.fx-reduced .fx-bubble-big,
html.fx-reduced .fx-bubble-small,
html.fx-reduced .fx-ritual-half .ritual__step,
html.fx-reduced .price-card .fx-check,
html.fx-reduced .fx-outcome,
html.fx-reduced .fx-lift,
html.fx-reduced .fx-eyebrow,
html.fx-reduced .fx-h2 {
  opacity: 1 !important;
  transform: none !important;
}
html.fx-reduced .compare tbody td,
html.fx-reduced .compare tbody td.us::before,
html.fx-reduced .compare tbody .us-dot::before {
  opacity: 1 !important;
  transform: none !important;
  animation: none !important;
}
html.fx-reduced #fx-progress-bar { display: none; }
