/* ─────────────────────────────────────────────────────────────────────
 * Flowella — Motion overlay
 *
 * Native CSS Scroll-Driven Animations + a small set of perpetual
 * micro-motions. This file is the single home for every animation in
 * the theme. It is loaded after design-system.css and main.css from
 * every site template.
 *
 * Authoring rules (must hold for every rule below):
 *   1. Final-state-first: nothing in this file moves an element off its
 *      final visual position by default. Animations only override.
 *   2. prefers-reduced-motion: reduce → all motion stops cleanly.
 *      Static "ambient" shadows / glows from tasks 5b + 6 remain.
 *   3. Scroll-driven rules sit inside
 *        @media (prefers-reduced-motion: no-preference) {
 *          @supports (animation-timeline: view()) { ... }
 *        }
 *      so unsupported browsers (Safari ≤ 17, Firefox without flag) get
 *      the static final state, no jank.
 *   4. Tasks 5b (italic glow breathe) and 6 (CTA glow pulse + hover
 *      sheen) are NOT scroll-driven and only need the prefers-reduced-
 *      motion guard.
 *   5. Easing for entrances: cubic-bezier(0.22, 1, 0.36, 1). No bounce.
 *   6. animation-duration on scroll-driven rules is 1ms — Firefox needs
 *      a non-zero duration even when timeline drives progress.
 *   7. Tokens come from design-system.css. Where the spec called for
 *      explicit rgba() values, those numbers are the same ones already
 *      compiled into --primary / --secondary / --green.
 * ─────────────────────────────────────────────────────────────────── */

/* ── Tokens ─────────────────────────────────────────────────────────── */
:root {
  --fr-ease-entry: cubic-bezier(0.22, 1, 0.36, 1);
  --fr-glow-secondary-soft: 0 0 12px rgba(79, 182, 236, .25);
  --fr-glow-secondary-strong: 0 0 20px rgba(79, 182, 236, .45);
  --fr-glow-green-soft: 0 0 12px rgba(37, 211, 102, .25);
  --fr-glow-green-strong: 0 0 20px rgba(37, 211, 102, .45);
}


/* Task 1 — Section entry reveal: REMOVED.
   The full-section opacity-0 → 1 fade was reading as a grey placeholder
   on the surrounding sections (especially light ones), which conflicted
   with the "final-state-first" intent. Sections now render in their
   final state on every browser, no cross-fade. The fr-reveal-up
   keyframe below survives because the comparison-card stagger (task 4)
   still depends on it. */
@keyframes fr-reveal-up {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}


/* ─────────────────────────────────────────────────────────────────────
 *  Task 3 — Hero subtle motion (flowella-hero)
 *
 *  hero-art-glow + hero-eyebrow .dot are perpetual micro-motions. They
 *  only need the prefers-reduced-motion guard (no scroll involvement,
 *  no @supports needed for animation-timeline). hero-bg-word IS scroll
 *  driven and sits inside the @supports block.
 *
 *  Tuning: the original 0.85→1.0 / 0.97→1.03 envelope read as too
 *  subtle behind the mascot. We bump the base intensity of the radial
 *  gradient AND widen the breathing envelope to 0.7→1.0 / 0.9→1.1 so
 *  the glow is felt without becoming a strobe.
 * ─────────────────────────────────────────────────────────────────── */

/* More punchy ambient base. Stops match the original sky-blue → mint
   gradient but with higher alpha at the centre. The `.hero` ancestor
   selector lifts specificity to (0,2,0) so we win against the module
   CSS definition (specificity 0,1,0) that loads after motion.css. */
.hero .hero-art-glow {
  background: radial-gradient(circle at 50% 50%,
    rgba(79, 182, 236, .80),
    rgba(48, 213, 200, .55) 35%,
    transparent 70%);
}

@media (prefers-reduced-motion: no-preference) {
  .hero-art-glow {
    animation: fr-hero-glow 6s ease-in-out infinite alternate;
    will-change: opacity, transform;
  }

  .hero-eyebrow .dot {
    animation: fr-hero-dot 1.6s ease-in-out infinite alternate;
    will-change: transform, box-shadow;
  }

  @supports (animation-timeline: scroll()) {
    .hero-bg-word {
      animation: fr-hero-parallax 1ms linear both;
      animation-timeline: scroll();
      animation-range: 0% 100%;
      will-change: transform;
    }
  }
}

@keyframes fr-hero-glow {
  from { opacity: .7; transform: scale(.9);  }
  to   { opacity: 1;  transform: scale(1.1); }
}

@keyframes fr-hero-dot {
  from {
    transform: scale(1);
    box-shadow: 0 0 8px var(--green);
  }
  to {
    transform: scale(1.25);
    box-shadow: 0 0 16px var(--green);
  }
}

@keyframes fr-hero-parallax {
  from { transform: translateY(0); }
  to   { transform: translateY(-60px); }
}


/* ─────────────────────────────────────────────────────────────────────
 *  Task 4 — Comparison chat row stagger
 *
 *  The two cards in flowella-comparison-cards are .vs-card.bad (the
 *  "broadcast mode") and .vs-card.good (the "Flowella mode"). Inside
 *  each, the chat-style messages (.vs-msg) and field rows
 *  (.vs-form-row) fade up on view() entry. Cap stagger at 4 children
 *  per card — anything past nth-child(4) reuses the 4th delay so we
 *  never accumulate runaway lag on long lists. animation-range: entry
 *  10% entry 60%; the offset is via animation-range-start step.
 * ─────────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .vs-card .vs-msg,
    .vs-card .vs-form-row {
      animation: fr-reveal-up 1ms linear both;
      animation-timing-function: var(--fr-ease-entry);
      animation-timeline: view();
      animation-range: entry 10% entry 60%;
    }

    /* 80ms-ish stagger via animation-range-start offsets. With a 50%
       view-progress window, ~4% of viewport ≈ a perceptible step. */
    .vs-card .vs-msg:nth-child(2),
    .vs-card .vs-form-row:nth-child(2) {
      animation-range: entry 14% entry 64%;
    }
    .vs-card .vs-msg:nth-child(3),
    .vs-card .vs-form-row:nth-child(3) {
      animation-range: entry 18% entry 68%;
    }
    .vs-card .vs-msg:nth-child(n + 4),
    .vs-card .vs-form-row:nth-child(n + 4) {
      animation-range: entry 22% entry 72%;
    }
  }
}


/* ─────────────────────────────────────────────────────────────────────
 *  Task 5a — Italic accent shimmer (continuous gradient pulse)
 *
 *  Originally a one-shot scroll-driven sweep on `animation-timeline:
 *  view()`. The user found that too subtle — once a heading scrolled
 *  past its entry range the sheen stopped and only re-played on scroll-
 *  back. Switched to a continuous time-driven loop with a hold-then-
 *  sweep keyframe so every italic accent pulses on its own rhythm
 *  whether the user is scrolling or sitting still.
 *
 *  Per-context --shimmer-base sets the gradient base colour so the
 *  keyframe is shared. Three families:
 *     - blue (var(--secondary))  — italics on dark headings + hero
 *     - blue (var(--primary))    — italics on light .ph-headline
 *     - green (var(--green))     — .acc accents
 *
 *  Hero h1 stagger: .acc lags 600ms behind so the "conversations →
 *  conversions" read order is reinforced — the first italic peaks,
 *  then the green .acc peaks just after.
 *
 *  Apparent text colour is preserved at every rest endpoint: the
 *  gradient is 250% wide with the white sheen centred at 50%, so the
 *  visible window at background-position 100% 50% (rest right) and
 *  0% 50% (rest left) both show pure --shimmer-base only. The text
 *  colour during the rest hold is therefore identical to the base.
 *
 *  Why the @supports gate stays:
 *  -----------------------------
 *  background-clip: text + color: transparent suppresses text-shadow
 *  in Safari. The @supports (animation-timeline: view()) gate keeps
 *  this whole block out of Safari's reach so the persistent text-
 *  shadow glow from task 5b still renders there. Chromium-family
 *  browsers (which DO have view()) get the pulse but lose the
 *  text-shadow during it — a known and accepted trade-off.
 * ─────────────────────────────────────────────────────────────────── */

/* ── Shimmer base colour per context ──────────────────────────────── */
.hero h1 i,
.hero h1 em,
.hero h1 .it,
.ph--dark .ph-headline i,
.ph--dark .ph-headline em,
.ph--dark .ph-headline .it,
.cta-strip h2 i,
.cta-strip h2 em,
.big-quote p i,
section.s.s-dark-1 .h-display i,
section.s.s-dark-2 .h-display i {
  --shimmer-base: var(--secondary);
}

.ph--light .ph-headline i,
.ph--light .ph-headline em,
.ph--light .ph-headline .it,
.about-feature h3 i,
.about-feature h3 em,
.legal-block h2 i,
.legal-block h2 em,
.flowella-rich .rich-prose h2 i,
.flowella-rich .rich-prose h2 em,
.flowella-rich .rich-prose h3 i,
.flowella-rich .rich-prose h3 em,
.feature-heading i,
.feature-heading em,
.h-display i {
  --shimmer-base: var(--primary);
}

.hero h1 .acc,
.ph-headline .acc {
  --shimmer-base: var(--green);
}

/* ── Shimmer animation ────────────────────────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .hero h1 i,
    .hero h1 em,
    .hero h1 .it,
    .hero h1 .acc,
    .ph-headline i,
    .ph-headline em,
    .ph-headline .it,
    .ph-headline .acc,
    .about-feature h3 i,
    .about-feature h3 em,
    .legal-block h2 i,
    .legal-block h2 em,
    .big-quote p i,
    .cta-strip h2 i,
    .cta-strip h2 em,
    .flowella-rich .rich-prose h2 i,
    .flowella-rich .rich-prose h2 em,
    .flowella-rich .rich-prose h3 i,
    .flowella-rich .rich-prose h3 em,
    .feature-heading i,
    .feature-heading em,
    .h-display i {
      /* Sheen brightened to full white (was .85 alpha) and the band
         widened from 40-60% to 32-68% so the highlight is more
         legible during the sweep. */
      background-image: linear-gradient(110deg,
        var(--shimmer-base, currentColor) 0%,
        var(--shimmer-base, currentColor) 32%,
        rgba(255, 255, 255, 1) 50%,
        var(--shimmer-base, currentColor) 68%,
        var(--shimmer-base, currentColor) 100%);
      background-size: 250% 100%;
      background-position: 100% 50%;
      background-repeat: no-repeat;
      -webkit-background-clip: text;
              background-clip: text;
      -webkit-text-fill-color: transparent;
              color: transparent;
      /* 6s cycle, linear so the sweep travels at constant speed.
         `both` keeps the rest position painted before the first
         iteration starts. */
      animation: fr-shimmer-sweep 6s linear infinite both;
    }

    /* Hero / placeholder .acc lags 600ms behind the surrounding
       italics so the "conversations → conversions" read order plays
       as a sequence each cycle, not a chord. */
    .hero h1 .acc,
    .ph-headline .acc {
      animation-delay: 600ms;
    }
  }
}

/* Hold-then-sweep rhythm: the highlight rests off-screen-right for
   ~50% of the cycle (≈3s on a 6s loop), sweeps across the text in
   ~35% (≈2.1s), then sits off-screen-left for the final ~15% (≈0.9s)
   before the loop instantly snaps back to the start. The snap is
   invisible because both endpoint positions render the visible area
   in pure --shimmer-base. */
@keyframes fr-shimmer-sweep {
  0%, 50%   { background-position: 100% 50%; }
  85%       { background-position: 0% 50%; }
  100%      { background-position: 0% 50%; }
}


/* ─────────────────────────────────────────────────────────────────────
 *  Task 5b — Italic accent persistent glow (low-amplitude breathing)
 *
 *  text-shadow is the persistent ambient — it stays as a soft static
 *  glow even with prefers-reduced-motion: reduce. The breathing
 *  animation only runs when the user hasn't asked us to be still.
 *
 *  Applied ONLY to italic accents that act as heading accents. Body
 *  copy contexts (.tone-line, .ed-drop, etc.) are deliberately
 *  excluded — see the selector list in 5a.
 * ─────────────────────────────────────────────────────────────────── */

.hero h1 i,
.hero h1 em,
.hero h1 .it,
.ph-headline i,
.ph-headline em,
.ph-headline .it,
.about-feature h3 i,
.about-feature h3 em,
.legal-block h2 i,
.legal-block h2 em,
.big-quote p i,
.cta-strip h2 i,
.cta-strip h2 em,
.flowella-rich .rich-prose h2 i,
.flowella-rich .rich-prose h2 em,
.flowella-rich .rich-prose h3 i,
.flowella-rich .rich-prose h3 em,
.feature-heading i,
.feature-heading em,
.h-display i {
  text-shadow: var(--fr-glow-secondary-soft);
}

.hero h1 .acc,
.ph-headline .acc {
  text-shadow: var(--fr-glow-green-soft);
}

@media (prefers-reduced-motion: no-preference) {
  .hero h1 i,
  .hero h1 em,
  .hero h1 .it,
  .ph-headline i,
  .ph-headline em,
  .ph-headline .it,
  .about-feature h3 i,
  .about-feature h3 em,
  .legal-block h2 i,
  .legal-block h2 em,
  .big-quote p i,
  .cta-strip h2 i,
  .cta-strip h2 em,
  .flowella-rich .rich-prose h2 i,
  .flowella-rich .rich-prose h2 em,
  .flowella-rich .rich-prose h3 i,
  .flowella-rich .rich-prose h3 em,
  .feature-heading i,
  .feature-heading em,
  .h-display i {
    animation: fr-glow-breathe-blue 5.5s ease-in-out infinite alternate;
  }

  .hero h1 .acc,
  .ph-headline .acc {
    animation: fr-glow-breathe-green 5.5s ease-in-out infinite alternate;
  }
}

@keyframes fr-glow-breathe-blue {
  from { text-shadow: var(--fr-glow-secondary-soft); }
  to   { text-shadow: var(--fr-glow-secondary-strong); }
}

@keyframes fr-glow-breathe-green {
  from { text-shadow: var(--fr-glow-green-soft); }
  to   { text-shadow: var(--fr-glow-green-strong); }
}


/* ─────────────────────────────────────────────────────────────────────
 *  Task 6 — CTA glow (per-button colour, subtle)
 *
 *  Reworked: the glow now picks up the button's own brand colour so it
 *  reads as a soft halo rather than a contrasting accent.
 *    .btn-primary  → sky-blue halo  (var(--primary))
 *    .btn-wa       → WhatsApp green (var(--green))
 *  Other variants (.btn-ghost, .btn-secondary, text links) stay
 *  untouched.
 *
 *  Amplitudes are deliberately low — felt-not-seen. Hover lifts the
 *  shadow to a stronger fixed state (no breathing), and a 200ms sheen
 *  sweeps once across the button via a ::before. The sheen tints
 *  match the button colour family.
 *
 *  The button needs `position: relative; overflow: hidden` to host the
 *  sheen pseudo-element. Specificity is bumped to (0,2,1) via the
 *  `body` ancestor so the `.cta-strip .btn-primary` override in the
 *  cta-banner module (which loads AFTER motion.css) doesn't win.
 *
 *  Per-CTA colour comes from --fr-cta-glow-rgb, set per selector. The
 *  keyframes consume rgb(var(...) / opacity) so a single keyframe pair
 *  serves every variant.
 * ─────────────────────────────────────────────────────────────────── */
body .btn.btn-primary,
body .btn.btn-wa {
  position: relative;
  overflow: hidden;
}

body .btn.btn-primary {
  --fr-cta-glow-rgb: 14, 110, 166;
  box-shadow: 0 6px 18px -8px rgba(14, 110, 166, .35);
}
body .btn.btn-primary:hover {
  box-shadow: 0 10px 26px -8px rgba(14, 110, 166, .55);
}

body .btn.btn-wa {
  --fr-cta-glow-rgb: 37, 211, 102;
  box-shadow: 0 6px 18px -8px rgba(37, 211, 102, .35);
}
body .btn.btn-wa:hover {
  box-shadow: 0 10px 26px -8px rgba(37, 211, 102, .55);
}

body .btn.btn-primary::before,
body .btn.btn-wa::before {
  content: "";
  position: absolute;
  inset: 0;
  /* Sheen tint stays on-brand. White at .25 across all variants reads
     as a soft brushed-metal pass; tinting it would over-saturate. */
  background: linear-gradient(110deg,
    transparent 0%,
    transparent 38%,
    rgba(255, 255, 255, .28) 50%,
    transparent 62%,
    transparent 100%);
  background-size: 250% 100%;
  background-position: 100% 50%;
  background-repeat: no-repeat;
  pointer-events: none;
  opacity: 0;
}

@media (prefers-reduced-motion: no-preference) {
  body .btn.btn-primary,
  body .btn.btn-wa {
    animation: fr-cta-glow 6s ease-in-out infinite alternate;
  }

  /* Suspend the breathing while hovered so the stronger static hover
     shadow reads as a single, deliberate state. */
  body .btn.btn-primary:hover,
  body .btn.btn-wa:hover {
    animation: none;
  }

  body .btn.btn-primary:hover::before,
  body .btn.btn-wa:hover::before {
    animation: fr-cta-sheen 220ms linear forwards;
  }
}

@keyframes fr-cta-glow {
  from { box-shadow: 0 6px 18px -8px rgba(var(--fr-cta-glow-rgb, 14, 110, 166), .25); }
  to   { box-shadow: 0 8px 22px -8px rgba(var(--fr-cta-glow-rgb, 14, 110, 166), .42); }
}

@keyframes fr-cta-sheen {
  0%   { opacity: 1; background-position: 100% 50%; }
  100% { opacity: 1; background-position: 0% 50%; }
}
