// Scroll-triggered reveal + subtle parallax — IntersectionObserver driven.
// Adds `.is-visible` to [data-reveal] elements when they enter the viewport,
// and drives `--scroll` on [data-parallax] elements so CSS can move them.

(function installReveal() {
  if (window.__revealInstalled) return;
  window.__revealInstalled = true;

  // One-shot reveal: add .is-visible, keep it (no flicker on scroll back).
  const io = new IntersectionObserver((entries) => {
    for (const entry of entries) {
      if (entry.isIntersecting) {
        entry.target.classList.add('is-visible');
        io.unobserve(entry.target);
      }
    }
  }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' });

  // rAF-driven parallax. Updates --scroll (0..1 across viewport) per element.
  const parallaxEls = new Set();
  let rafId = null;
  function tick() {
    rafId = null;
    const vh = window.innerHeight || 800;
    parallaxEls.forEach((el) => {
      const r = el.getBoundingClientRect();
      // Progress: 0 when element just entered from bottom, 1 when it leaves top.
      const progress = 1 - (r.top + r.height * 0.5) / (vh + r.height * 0.5);
      el.style.setProperty('--scroll', Math.max(0, Math.min(1, progress)).toFixed(3));
    });
  }
  function onScroll() {
    if (rafId == null) rafId = requestAnimationFrame(tick);
  }

  // Observe any matching elements already in DOM + watch for new ones.
  function isInView(el) {
    const r = el.getBoundingClientRect();
    const vh = window.innerHeight || 800;
    return r.bottom > 0 && r.top < vh * 0.92;
  }
  function scan(root = document) {
    const process = (el) => {
      if (el.classList.contains('is-visible')) return;
      // If already in view at scan time, reveal immediately (no IO dependency)
      if (isInView(el)) {
        el.classList.add('is-visible');
      } else {
        io.observe(el);
      }
    };
    if (root.nodeType === 1 && root.matches && root.matches('[data-reveal]:not(.is-visible)')) {
      process(root);
    }
    if (root.nodeType === 1 && root.matches && root.matches('[data-parallax]')) {
      parallaxEls.add(root);
    }
    if (root.querySelectorAll) {
      root.querySelectorAll('[data-reveal]:not(.is-visible)').forEach(process);
      root.querySelectorAll('[data-parallax]').forEach((el) => parallaxEls.add(el));
    }
    tick();
  }

  const mo = new MutationObserver((muts) => {
    for (const m of muts) {
      m.addedNodes.forEach((n) => {
        if (n.nodeType === 1) scan(n);
      });
    }
  });

  function start() {
    scan();
    mo.observe(document.body, { childList: true, subtree: true });
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    // Belt-and-suspenders: re-scan repeatedly for first 2s in case
    // React mounts after the initial scan. Cheap — scan is a qsa.
    let ticks = 0;
    const poll = setInterval(() => {
      scan();
      if (++ticks >= 20) clearInterval(poll); // stop after 2s
    }, 100);
    // Fallback on scroll (some browsers/iframes IO can be flaky)
    window.addEventListener('scroll', () => scan(), { passive: true });
  }

  if (document.body) start();
  else document.addEventListener('DOMContentLoaded', start);
})();

// Simple Reveal wrapper component — wraps children in a div with
// `data-reveal` so the observer above handles it.
function Reveal({ as: Tag = 'div', delay = 0, kind = 'up', className = '', children, ...rest }) {
  const ref = React.useRef(null);
  return (
    <Tag
      ref={ref}
      data-reveal
      data-reveal-kind={kind}
      style={{ '--reveal-delay': `${delay}ms`, ...(rest.style || {}) }}
      className={className}
      {...rest}
    >
      {children}
    </Tag>
  );
}

// Stagger container: auto-delays direct [data-reveal] children.
function Stagger({ step = 80, start = 0, children, className = '', as: Tag = 'div', ...rest }) {
  const childArr = React.Children.toArray(children);
  return (
    <Tag className={className} {...rest}>
      {childArr.map((child, i) =>
        React.isValidElement(child)
          ? React.cloneElement(child, {
              style: {
                ...(child.props.style || {}),
                '--reveal-delay': `${start + i * step}ms`,
              },
            })
          : child
      )}
    </Tag>
  );
}

Object.assign(window, { Reveal, Stagger });
