// ─────────────────────────────────────────────────────────────
// Animation helpers — small primitives the brainstorm screens use.
// All animations follow the brand motion rules:
//   • ease "organic" cubic-bezier(0.32, 0.72, 0.34, 1)
//   • 140 / 220 / 360 ms only
//   • soft scale 0.95→1, never slide-from-side
//   • leaf-stagger 40ms between siblings
// ─────────────────────────────────────────────────────────────


// Global keyframes — injected once.
(function injectAnimKeyframes() {
  if (document.getElementById('fl-anim-kf')) return;
  const s = document.createElement('style');
  s.id = 'fl-anim-kf';
  s.textContent = `
    @keyframes flLeafEnter {
      0%   { opacity: 0; transform: translateY(6px) scale(0.97); }
      100% { opacity: 1; transform: translateY(0)   scale(1); }
    }
    @keyframes flSheetEnter {
      0%   { opacity: 0; transform: translateY(18px); }
      100% { opacity: 1; transform: translateY(0); }
    }
    @keyframes flSunbeam {
      0%   { opacity: 0.0; transform: translateX(-40%) skewX(-12deg); }
      40%  { opacity: 0.30; }
      100% { opacity: 0.0; transform: translateX(120%) skewX(-12deg); }
    }
    @keyframes flRowSunbeam {
      0%   { opacity: 0; transform: translateX(-110%) skewX(-14deg); }
      14%  { opacity: 0.18; }
      40%  { opacity: 0; transform: translateX(120%) skewX(-14deg); }
      100% { opacity: 0; transform: translateX(120%) skewX(-14deg); }
    }
    @keyframes flGlyphBreathe {
      0%, 100% { transform: scale(1);    opacity: 0.86; }
      50%      { transform: scale(1.06); opacity: 1; }
    }
    @keyframes flGlyphBreatheWarm {
      0%, 100% { transform: scale(1);    opacity: 0.88; }
      50%      { transform: scale(1.035); opacity: 1; }
    }
    @keyframes flGlyphBreatheQuiet {
      0%, 100% { transform: scale(1);    opacity: 0.78; }
      50%      { transform: scale(1.018); opacity: 0.95; }
    }
    @keyframes flCanopyRipple {
      0%   { transform: scale(0.6); opacity: 0.6; }
      100% { transform: scale(1.8); opacity: 0; }
    }
    @keyframes flBeadGrow {
      0%   { transform: scale(0); opacity: 0; }
      55%  { transform: scale(1.15); opacity: 1; }
      100% { transform: scale(1); opacity: 1; }
    }
    @keyframes flCaretBlink {
      0%, 60% { opacity: 1; }
      61%,100%{ opacity: 0; }
    }
    @keyframes flBranchDraw {
      0%   { stroke-dashoffset: 200; }
      100% { stroke-dashoffset: 0; }
    }
    @keyframes flTypingDot {
      0%, 80%, 100% { transform: translateY(0);    opacity: 0.4; }
      40%           { transform: translateY(-3px); opacity: 1;   }
    }
    @keyframes flSlideDown {
      0%   { max-height: 0; opacity: 0; }
      100% { max-height: 600px; opacity: 1; }
    }
    @keyframes flCountUp {
      0% { transform: translateY(8px); opacity: 0; }
      100% { transform: translateY(0); opacity: 1; }
    }
    @keyframes flCountTick {
      0%   { transform: translateY(0)   scale(1);    color: var(--tick-from, #F4DCA0); }
      30%  { transform: translateY(-4px) scale(1.15); color: var(--tick-to,   #F4DCA0); }
      100% { transform: translateY(0)   scale(1);    color: var(--tick-to,   #F4DCA0); }
    }
    @keyframes flMoteDrift {
      0%   { transform: translate(0, 0) scale(0.6); opacity: 0; }
      20%  { opacity: 0.55; }
      80%  { opacity: 0.45; }
      100% { transform: translate(var(--mote-dx, -8px), -28px) scale(1); opacity: 0; }
    }
    @keyframes flRingSpin {
      0%   { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
    }
    @keyframes flRingSpinReverse {
      0%   { transform: rotate(0deg); }
      100% { transform: rotate(-360deg); }
    }
    @keyframes flLeafDrift {
      0%   { transform: translate(0, 110%) rotate(0deg);   opacity: 0; }
      8%   { opacity: 0.65; }
      90%  { opacity: 0.35; }
      100% { transform: translate(var(--leaf-dx, 30px), -10%) rotate(var(--leaf-rot, 40deg)); opacity: 0; }
    }
    @keyframes flSparkPulse {
      0%, 100% { opacity: 0.55; transform: scaleY(0.85); }
      50%      { opacity: 1;    transform: scaleY(1); }
    }
    @keyframes flSparkDot {
      0%, 100% { transform: scale(1);   opacity: 0.8; }
      50%      { transform: scale(1.5); opacity: 1; }
    }
    @keyframes flDashFlow {
      0%   { stroke-dashoffset: 24; }
      100% { stroke-dashoffset: 0; }
    }
    @keyframes flSunburst {
      0%   { transform: rotate(0deg)   scale(0.6); opacity: 0.9; }
      100% { transform: rotate(220deg) scale(1.7); opacity: 0; }
    }
    @keyframes flShootingStar {
      0%   { transform: translate(-30px, 30px) scale(0); opacity: 0; }
      10%  { transform: translate(-30px, 30px) scale(1); opacity: 0.9; }
      80%  { opacity: 0.6; }
      100% { transform: translate(120px, -110px) scale(0.4); opacity: 0; }
    }
    @keyframes flTickerSlide {
      0%   { transform: translateY(100%); opacity: 0; }
      12%  { transform: translateY(0);    opacity: 1; }
      88%  { transform: translateY(0);    opacity: 1; }
      100% { transform: translateY(-100%); opacity: 0; }
    }
    @keyframes flNewLeafBloom {
      0%   { transform: scaleY(0) translateY(-8px); opacity: 0; max-height: 0; }
      40%  { opacity: 0.7; }
      100% { transform: scaleY(1) translateY(0);    opacity: 1; max-height: 600px; }
    }
    @media (prefers-reduced-motion: reduce) {
      [data-fl-anim] { animation: none !important; }
    }
  `;
  document.head.appendChild(s);
})();


// Wrap children in a staggered leaf-enter animation. Sibling children
// share staggerMs gap. Honors prefers-reduced-motion.
function LeafStagger({ children, staggerMs = 40, delay = 0, style = {} }) {
  const arr = React.Children.toArray(children);
  const prefersReduce = useReducedMotion();
  return (
    <div style={style}>
      {arr.map((c, i) => (
        <div key={i} style={{
          animation: prefersReduce
            ? 'none'
            : `flLeafEnter 360ms var(--ease-organic) both`,
          animationDelay: `${delay + i * staggerMs}ms`,
        }}>{c}</div>
      ))}
    </div>
  );
}


// A small "+N typing" indicator — three dots, gentle bob.
function TypingDots({ color = '#A8C5A0', size = 5 }) {
  return (
    <span style={{ display: 'inline-flex', gap: 3, alignItems: 'center', height: size + 4 }}>
      {[0, 1, 2].map(i => (
        <span key={i} style={{
          width: size, height: size, borderRadius: '50%', background: color,
          animation: `flTypingDot 1.2s var(--ease-organic) ${i * 120}ms infinite`,
        }}/>
      ))}
    </span>
  );
}


// Number that fades + slides up when the key changes.
function CountUp({ value, style = {} }) {
  return (
    <span key={value} style={{
      ...style, display: 'inline-block',
      animation: 'flCountUp 280ms var(--ease-organic) both',
    }}>{value}</span>
  );
}


// Glyph that gently breathes — every forest gets a heartbeat. Speed/depth
// is determined by intensity ('hot' | 'warm' | 'quiet'); quiet rows still
// breathe, just slowly + shallowly so the row never reads as dead.
function BreathingForestGlyph({ size = 28, hot = false, intensity, period }) {
  const i = intensity || (hot ? 'hot' : 'quiet');
  const config = {
    hot:   { dur: period || '3.6s', kf: 'flGlyphBreathe' },
    warm:  { dur: period || '5.4s', kf: 'flGlyphBreatheWarm' },
    quiet: { dur: period || '9.0s', kf: 'flGlyphBreatheQuiet' },
  }[i];
  return (
    <span data-fl-anim style={{
      display: 'inline-flex',
      animation: `${config.kf} ${config.dur} var(--ease-organic) infinite`,
      willChange: 'transform, opacity',
    }}>
      <ForestGlyph size={size} hot={hot} alive intensity={i}/>
    </span>
  );
}


// A row ambience overlay — adds a slow, randomized sunbeam sweep + a few
// drifting motes behind whatever it's wrapped around. Stays absolutely
// positioned so it never affects layout.
function RowAmbience({ children, seed = '', intensity = 'quiet', borderRadius = 12 }) {
  // Stable per-row offsets from seed (so beams don't all fire in sync).
  const h = React.useMemo(() => {
    let x = 0;
    for (let i = 0; i < seed.length; i++) x = (x * 31 + seed.charCodeAt(i)) | 0;
    return Math.abs(x);
  }, [seed]);
  const beamDelay  = (h % 1000) / 100;
  const beamPeriod = intensity === 'hot' ? 9 : intensity === 'warm' ? 12 : 18;
  const moteCount  = intensity === 'hot' ? 3 : intensity === 'warm' ? 2 : 1;

  return (
    <div style={{ position: 'relative', isolation: 'isolate' }}>
      <span data-fl-anim aria-hidden="true" style={{
        position: 'absolute', inset: 0, borderRadius, overflow: 'hidden',
        pointerEvents: 'none', zIndex: 0,
      }}>
        <span style={{
          position: 'absolute', top: 0, bottom: 0, left: 0, width: '55%',
          background: 'linear-gradient(90deg, transparent 0%, rgba(212,150,12,0.10) 50%, transparent 100%)',
          animation: `flRowSunbeam ${beamPeriod}s var(--ease-organic) ${beamDelay}s infinite`,
          willChange: 'transform, opacity',
        }}/>
      </span>
      <span aria-hidden="true" style={{
        position: 'absolute', inset: 0, borderRadius, overflow: 'hidden',
        pointerEvents: 'none', zIndex: 0,
      }}>
        {Array.from({ length: moteCount }).map((_, i) => {
          const ox = ((h + i * 137) % 90) + 5;
          const dx = (((h >> (i + 1)) % 16) - 8);
          const delay = ((h + i * 53) % 1000) / 100;
          const dur   = 6 + ((h + i * 91) % 6);
          const sz    = 2 + (i === 0 ? 1 : 0);
          return (
            <span data-fl-anim key={i} style={{
              position: 'absolute',
              left: `${ox}%`, bottom: -4,
              width: sz, height: sz, borderRadius: '50%',
              background: i === 0 ? '#A8C5A0' : '#5E8048',
              opacity: 0,
              '--mote-dx': `${dx}px`,
              animation: `flMoteDrift ${dur}s var(--ease-organic) ${delay}s infinite`,
              willChange: 'transform, opacity',
            }}/>
          );
        })}
      </span>
      <div style={{ position: 'relative', zIndex: 1 }}>
        {children}
      </div>
    </div>
  );
}

// Map a forest's signal/activeNow to an intensity bucket for RowAmbience.
function forestIntensity(f) {
  if (!f) return 'quiet';
  if (f.signal === 'rising' || (f.activeNow || 0) > 15) return 'hot';
  if ((f.activeNow || 0) > 3 || f.signal === 'steady') return 'warm';
  return 'quiet';
}

// A horizontal "sunbeam" sweep — used as a subtle accent on hero rows.
function SunbeamSweep() {
  return (
    <span aria-hidden="true" style={{
      position: 'absolute', inset: 0, pointerEvents: 'none',
      borderRadius: 'inherit', overflow: 'hidden',
    }}>
      <span data-fl-anim style={{
        position: 'absolute', top: 0, bottom: 0, width: '60%',
        background: 'linear-gradient(90deg, transparent 0%, rgba(212,150,12,0.10) 50%, transparent 100%)',
        animation: 'flSunbeam 5.6s var(--ease-organic) infinite',
      }}/>
    </span>
  );
}


// prefers-reduced-motion hook
function useReducedMotion() {
  const [prefers, setPrefers] = React.useState(false);
  React.useEffect(() => {
    const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
    setPrefers(mq.matches);
    const fn = e => setPrefers(e.matches);
    mq.addEventListener?.('change', fn);
    return () => mq.removeEventListener?.('change', fn);
  }, []);
  return prefers;
}


// A "slot machine" rolling number — used for the live activity counter.
// Tickles by 1 every `period` ms; resets to baseline on remount.
function LiveCounter({ baseline = 41, range = 3, period = 2200, style = {} }) {
  const [v, setV] = React.useState(baseline);
  React.useEffect(() => {
    const id = setInterval(() => {
      // Random walk inside ±range, never below baseline-range
      setV(prev => {
        const drift = Math.floor((Math.random() - 0.5) * 2 * range);
        return Math.max(baseline - range, Math.min(baseline + range, prev + drift));
      });
    }, period);
    return () => clearInterval(id);
  }, [baseline, range, period]);
  return <CountUp value={v} style={style}/>;
}


Object.assign(window, {
  LeafStagger, TypingDots, CountUp, BreathingForestGlyph,
  SunbeamSweep, useReducedMotion, LiveCounter, RowAmbience, forestIntensity,
});
