// reveal.jsx — scroll-triggered reveal animation primitives
// Uses IntersectionObserver + CSS transitions. Lightweight, no external deps.

const Reveal = ({ children, delay = 0, y = 24, x = 0, scale = 1, duration = 700, threshold = 0.12, once = true, as: Tag = 'div', className = '', style = {}, ...rest }) => {
  const ref = React.useRef(null);
  const [shown, setShown] = React.useState(false);

  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (typeof IntersectionObserver === 'undefined') { setShown(true); return; }
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          setShown(true);
          if (once) io.unobserve(e.target);
        } else if (!once) {
          setShown(false);
        }
      });
    }, { threshold: Math.min(threshold, 0.05), rootMargin: '0px 0px 80px 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, [once, threshold]);

  const transform = shown
    ? 'translate3d(0,0,0) scale(1)'
    : `translate3d(${x}px, ${y}px, 0) scale(${scale})`;

  return (
    <Tag
      ref={ref}
      className={className}
      style={{
        opacity: shown ? 1 : 0,
        transform,
        transition: `opacity ${duration}ms cubic-bezier(.22,.8,.32,1) ${delay}ms, transform ${duration}ms cubic-bezier(.22,.8,.32,1) ${delay}ms`,
        willChange: 'opacity, transform',
        ...style
      }}
      {...rest}
    >
      {children}
    </Tag>
  );
};

const Stagger = ({ children, gap = 80, startDelay = 0, ...rest }) => {
  const arr = React.Children.toArray(children);
  return (
    <>
      {arr.map((child, i) => (
        <Reveal key={i} delay={startDelay + i * gap} {...rest}>
          {child}
        </Reveal>
      ))}
    </>
  );
};

// Parallax — translates element on scroll based on its viewport position
const Parallax = ({ children, speed = 0.15, className = '', style = {}, ...rest }) => {
  const ref = React.useRef(null);
  const [y, setY] = React.useState(0);
  React.useEffect(() => {
    let raf = 0;
    const update = () => {
      const el = ref.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const center = r.top + r.height / 2;
      const offset = (center - window.innerHeight / 2) * speed;
      setY(-offset);
    };
    const onScroll = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(update); };
    update();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      cancelAnimationFrame(raf);
    };
  }, [speed]);
  return (
    <div
      ref={ref}
      className={className}
      style={{ transform: `translate3d(0, ${y.toFixed(1)}px, 0)`, willChange: 'transform', ...style }}
      {...rest}
    >
      {children}
    </div>
  );
};

// Progress rail at top of page
const ScrollProgress = () => {
  const [p, setP] = React.useState(0);
  React.useEffect(() => {
    const onScroll = () => {
      const h = document.documentElement;
      const max = h.scrollHeight - h.clientHeight;
      setP(max > 0 ? h.scrollTop / max : 0);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return (
    <div style={{
      position: 'fixed', top: 0, left: 0, right: 0, height: 2, zIndex: 100, pointerEvents: 'none',
      background: 'transparent'
    }}>
      <div style={{
        height: '100%',
        width: `${p * 100}%`,
        background: 'linear-gradient(90deg, var(--accent), var(--ai))',
        boxShadow: '0 0 12px var(--accent)',
        transition: 'width 0.05s linear'
      }} />
    </div>
  );
};

window.Reveal = Reveal;
window.Stagger = Stagger;
window.Parallax = Parallax;
window.ScrollProgress = ScrollProgress;
