// ── Cinematic page: animation primitives + icons (no framer-motion) ───
const { useState, useEffect, useRef } = React;

function useInView(threshold = 0.2) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    let done = false;
    const reveal = () => { if (done) return; done = true; setSeen(true); };
    let io;
    try {
      io = new IntersectionObserver((entries) => {
        if (entries.some((e) => e.isIntersecting)) reveal();
      }, { threshold: 0.12, rootMargin: '0px 0px -6% 0px' });
      if (ref.current) io.observe(ref.current);
    } catch (e) { /* ignore */ }
    // Safety: setTimeout fires even when the document is hidden (rAF/IO may not),
    // so animated content can never get stuck invisible.
    const t = setTimeout(reveal, 1800);
    return () => { done = true; if (io) io.disconnect(); clearTimeout(t); };
  }, []);
  return [ref, seen];
}

// Splits text into words; each slides up + fades, staggered.
function WordsPullUp({ text, style, stagger = 0.08, asterisk = false }) {
  const [ref, seen] = useInView(0.25);
  const words = text.split(' ');
  return (
    <div ref={ref} style={{ display: 'inline-flex', flexWrap: 'wrap', ...style }}>
      {words.map((w, i) => {
        const last = i === words.length - 1;
        return (
          <span key={i} style={{ display: 'inline-block', overflow: 'hidden', paddingBottom: '0.06em' }}>
            <span style={{
              display: 'inline-block', whiteSpace: 'pre',
              transform: seen ? 'translateY(0)' : 'translateY(0.5em)',
              opacity: seen ? 1 : 0,
              transition: `transform 0.75s cubic-bezier(0.16,1,0.3,1) ${i * stagger}s, opacity 0.75s cubic-bezier(0.16,1,0.3,1) ${i * stagger}s`,
            }}>
              {w}{last && asterisk ? <sup style={{ fontSize: '0.31em', verticalAlign: 'super', color: 'var(--accent)' }}>*</sup> : null}{!last ? '\u00A0' : ''}
            </span>
          </span>
        );
      })}
    </div>
  );
}

// Multi-style: array of { text, style } segments, split into words, shared reveal.
function WordsPullUpMultiStyle({ segments, style, stagger = 0.06, justify = 'center' }) {
  const [ref, seen] = useInView(0.2);
  const words = [];
  segments.forEach((seg, si) => {
    seg.text.split(' ').forEach((w) => words.push({ w, style: seg.style, si }));
  });
  let idx = -1;
  return (
    <div ref={ref} style={{ display: 'inline-flex', flexWrap: 'wrap', justifyContent: justify, ...style }}>
      {words.map((item, i) => {
        idx++;
        return (
          <span key={i} style={{ display: 'inline-block', overflow: 'hidden', paddingBottom: '0.08em' }}>
            <span style={{
              display: 'inline-block', whiteSpace: 'pre',
              transform: seen ? 'translateY(0)' : 'translateY(0.6em)',
              opacity: seen ? 1 : 0,
              transition: `transform 0.7s cubic-bezier(0.16,1,0.3,1) ${idx * stagger}s, opacity 0.7s cubic-bezier(0.16,1,0.3,1) ${idx * stagger}s`,
              ...item.style,
            }}>{item.w}&nbsp;</span>
          </span>
        );
      })}
    </div>
  );
}

// Scroll-linked progressive character reveal.
function ScrollReveal({ text, style }) {
  const ref = useRef(null);
  const [p, setP] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const el = ref.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const start = vh * 0.85;
      const end = vh * 0.25;
      const prog = (start - r.top) / (start - end + r.height * 0.6);
      setP(Math.max(0, Math.min(1, prog)));
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); };
  }, []);
  const chars = [...text];
  const n = chars.length;
  return (
    <p ref={ref} style={style}>
      {chars.map((c, i) => {
        const cp = i / n;
        const o = 0.15 + 0.85 * Math.max(0, Math.min(1, (p - (cp - 0.08)) / 0.12));
        return <span key={i} style={{ opacity: o, transition: 'opacity 0.12s linear' }}>{c}</span>;
      })}
    </p>
  );
}

function IconArrow({ size = 18, style }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={style}>
      <line x1="5" y1="12" x2="19" y2="12" /><polyline points="12 5 19 12 12 19" />
    </svg>
  );
}

function IconCheck({ size = 15 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
      <polyline points="20 6 9 17 4 12" />
    </svg>
  );
}

Object.assign(window, { useInView, WordsPullUp, WordsPullUpMultiStyle, ScrollReveal, IconArrow, IconCheck });
