// motion.jsx — scroll-reveal observer, counter animation, hero parallax
// Pure vanilla hooks; no framer/popmotion. Lightweight and bounce-free.

function useReveal() {
  // Watch the DOM for elements with `.reveal` and toggle `.is-visible`
  // when they enter the viewport. Re-scans after every React render via
  // a MutationObserver so dynamically-mounted content also gets picked up.
  React.useEffect(() => {
    if (typeof IntersectionObserver === "undefined") return;

    const io = new IntersectionObserver((entries) => {
      for (const e of entries) {
        if (e.isIntersecting) {
          e.target.classList.add("is-visible");
          io.unobserve(e.target);
        }
      }
    }, { threshold: 0.12, rootMargin: "0px 0px -40px 0px" });

    const scan = () => {
      document.querySelectorAll(".reveal:not(.is-visible)").forEach((el) => {
        // skip ones already being observed
        if (!el.__obs) {
          el.__obs = true;
          io.observe(el);
        }
      });
      // map / coverage block — toggles is-visible on its container
      document.querySelectorAll(".map:not(.is-visible)").forEach((el) => {
        if (!el.__obs) {
          el.__obs = true;
          io.observe(el);
        }
      });
    };

    scan();
    const mo = new MutationObserver(scan);
    mo.observe(document.body, { childList: true, subtree: true });

    return () => { io.disconnect(); mo.disconnect(); };
  }, []);
}

function useCounters() {
  // Animate elements with data-count="<final>" from 0 → final when visible.
  // Uses the same approach as useReveal but kicks the count anim on visibility.
  React.useEffect(() => {
    if (typeof IntersectionObserver === "undefined") return;

    const animate = (el) => {
      const target = parseFloat(el.getAttribute("data-count"));
      if (isNaN(target)) return;
      const duration = parseInt(el.getAttribute("data-duration") || "1400", 10);
      const suffix = el.getAttribute("data-suffix") || "";
      const prefix = el.getAttribute("data-prefix") || "";
      const start = performance.now();
      const ease = (t) => 1 - Math.pow(1 - t, 4); // ease-out quartic
      const tick = (now) => {
        const t = Math.min(1, (now - start) / duration);
        const v = Math.round(target * ease(t));
        el.textContent = prefix + v + suffix;
        if (t < 1) requestAnimationFrame(tick);
      };
      requestAnimationFrame(tick);
    };

    const io = new IntersectionObserver((entries) => {
      for (const e of entries) {
        if (e.isIntersecting) {
          animate(e.target);
          io.unobserve(e.target);
        }
      }
    }, { threshold: 0.4 });

    const scan = () => {
      document.querySelectorAll("[data-count]:not(.is-counted)").forEach((el) => {
        el.classList.add("is-counted");
        io.observe(el);
      });
    };
    scan();
    const mo = new MutationObserver(scan);
    mo.observe(document.body, { childList: true, subtree: true });

    return () => { io.disconnect(); mo.disconnect(); };
  }, []);
}

function useHeroParallax() {
  // Subtle vertical parallax on the hero photo. Drives a CSS custom prop --py
  // so it can pause naturally if the element is hidden.
  React.useEffect(() => {
    let ticking = false;
    const onScroll = () => {
      if (ticking) return;
      ticking = true;
      requestAnimationFrame(() => {
        const el = document.querySelector(".hero__media img");
        if (el) {
          const y = window.scrollY;
          // Only translate while hero is in viewport — bail at 800px
          if (y < 800) {
            el.style.setProperty("--py", (y * 0.08) + "px");
          }
        }
        ticking = false;
      });
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
}

function useMotion() {
  useReveal();
  useCounters();
  useHeroParallax();
}

Object.assign(window, { useReveal, useCounters, useHeroParallax, useMotion });
