/* Shared motion helpers for the looping stage animations.
 *
 * Every stage on the home page used to tick from mount, forever — eight of them
 * at once, plus one more inside the hero that mobile hides but still mounts. On
 * a phone that is a lot of main-thread work competing with the scroll, which is
 * what the stutter was. These hooks let a stage run only while it is actually
 * on screen and the tab is in front, and hold a settled frame for anyone who
 * asks for reduced motion.
 *
 * Usage inside a stage:
 *   const ref = useRef(null);
 *   const { active, still } = useStage(ref);      // ref goes on the root node
 *   const [x, setX] = useState(() => still ? SETTLED : START);
 *   useEffect(() => { if (!active) return; ...timers... }, [active]);
 */
const { useState: useStateMo, useEffect: useEffectMo } = React;

function useReducedMotion() {
  const query = () =>
    typeof window.matchMedia === "function" &&
    window.matchMedia("(prefers-reduced-motion: reduce)").matches;

  const [reduced, setReduced] = useStateMo(query);

  useEffectMo(() => {
    if (typeof window.matchMedia !== "function") return;
    const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
    const onChange = () => setReduced(mq.matches);
    mq.addEventListener("change", onChange);
    return () => mq.removeEventListener("change", onChange);
  }, []);

  return reduced;
}

/* True only while `ref` is on screen AND the tab is in the foreground.
 * A display:none element never intersects, so the hero stage that mobile hides
 * stays idle instead of animating into the void. */
function useInView(ref, rootMargin = "160px") {
  const [inView, setInView] = useStateMo(false);
  const [awake, setAwake] = useStateMo(() => document.visibilityState !== "hidden");

  useEffectMo(() => {
    const el = ref.current;
    if (!el || typeof IntersectionObserver === "undefined") {
      setInView(true);
      return;
    }
    const io = new IntersectionObserver(([e]) => setInView(e.isIntersecting), { rootMargin });
    io.observe(el);
    return () => io.disconnect();
  }, [ref, rootMargin]);

  useEffectMo(() => {
    const onVis = () => setAwake(document.visibilityState !== "hidden");
    document.addEventListener("visibilitychange", onVis);
    return () => document.removeEventListener("visibilitychange", onVis);
  }, []);

  return inView && awake;
}

function useStage(ref) {
  const still = useReducedMotion();
  const inView = useInView(ref);
  return { active: inView && !still, still };
}

window.useReducedMotion = useReducedMotion;
window.useInView = useInView;
window.useStage = useStage;
