// ===========================================================================
// Shared utilities: icons, scroll-reveal, parallax. Exposed on window so each
// Babel-transpiled file can use them (separate scopes don't share locals).
// ===========================================================================
(function () {
  const { useRef, useEffect, useState } = React;

  // ---- Line icons (Lucide-style, 2px round) ----
  const ico = (paths, vb = "0 0 24 24") => ({ size = 20, stroke = 2, style = {}, ...rest }) =>
    React.createElement("svg", {
      width: size, height: size, viewBox: vb, fill: "none", stroke: "currentColor",
      strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round", style, ...rest,
    }, paths.map((d, i) => React.createElement("path", { key: i, d })));

  const Icons = {
    arrow: ico(["M5 12h14", "M13 6l6 6-6 6"]),
    arrowUpRight: ico(["M7 17 17 7", "M8 7h9v9"]),
    moon: ico(["M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"]),
    sun: ico(["M12 17a5 5 0 1 0 0-10 5 5 0 0 0 0 10z", "M12 1v2", "M12 21v2", "M4.2 4.2l1.4 1.4", "M18.4 18.4l1.4 1.4", "M1 12h2", "M21 12h2", "M4.2 19.8l1.4-1.4", "M18.4 5.6l1.4-1.4"]),
    menu: ico(["M3 6h18", "M3 12h18", "M3 18h18"]),
    close: ico(["M18 6 6 18", "M6 6l12 12"]),
    mail: ico(["M4 5h16a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z", "m3 7 9 6 9-6"]),
    instagram: ico(["M4 4h16v16H4z", "M16 11.4A4 4 0 1 1 12.6 8 4 4 0 0 1 16 11.4z", "M17.5 6.5h.01"]),
    heart: ico(["M19 14c1.5-1.5 3-3.2 3-5.5A3.5 3.5 0 0 0 12 6 3.5 3.5 0 0 0 2 8.5c0 2.3 1.5 4 3 5.5l7 7z"]),
    check: ico(["M20 6 9 17l-5-5"]),
    chevronLeft: ico(["M15 18l-6-6 6-6"]),
    chevronRight: ico(["M9 18l6-6-6-6"]),
    expand: ico(["M15 3h6v6", "M9 21H3v-6", "M21 3l-7 7", "M3 21l7-7"]),
  };

  function prefersReduced() {
    return window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  }

  // ---- Scroll reveal ----
  function Reveal({ children, delay = 0, variant = "rise", as = "div", className = "", style = {}, ...rest }) {
    const ref = useRef(null);
    const [shown, setShown] = useState(false);
    useEffect(() => {
      const el = ref.current;
      if (!el) return;
      if (prefersReduced()) { setShown(true); return; }
      let done = false;
      const reveal = () => { if (!done) { done = true; setShown(true); } };
      // Immediate check — if already in view on mount, reveal now.
      const r = el.getBoundingClientRect();
      if (r.top < (window.innerHeight || 800) * 0.95 && r.bottom > 0) {
        // still let it animate from hidden on the next frame
        requestAnimationFrame(() => requestAnimationFrame(reveal));
      }
      const obs = new IntersectionObserver((entries) => {
        entries.forEach((e) => { if (e.isIntersecting) { reveal(); obs.unobserve(el); } });
      }, { threshold: 0.1, rootMargin: "0px 0px -6% 0px" });
      obs.observe(el);
      // Backstop: if the observer never delivers (e.g. a non-visible/background
      // document defers notifications), reveal anyway so content is never stuck.
      const t = setTimeout(reveal, 1500 + (delay || 0));
      return () => { obs.disconnect(); clearTimeout(t); };
    }, []);
    const base = variant === "scale" ? "reveal-scale" : "reveal";
    return React.createElement(as, {
      ref, className: base + (shown ? " in" : "") + (className ? " " + className : ""),
      style: { transitionDelay: (delay || 0) + "ms", ...style }, ...rest,
    }, children);
  }

  // ---- Parallax (translateY relative to scroll) ----
  function useParallax(speed = 0.12, max = 80) {
    const ref = useRef(null);
    useEffect(() => {
      const el = ref.current;
      if (!el || prefersReduced() || document.documentElement.classList.contains("motion-off")) return;
      let raf = 0;
      const onScroll = () => {
        cancelAnimationFrame(raf);
        raf = requestAnimationFrame(() => {
          const rect = el.getBoundingClientRect();
          const center = rect.top + rect.height / 2 - window.innerHeight / 2;
          const y = Math.max(-max, Math.min(max, -center * speed));
          el.style.transform = "translate3d(0," + y.toFixed(1) + "px,0)";
        });
      };
      onScroll();
      window.addEventListener("scroll", onScroll, { passive: true });
      window.addEventListener("resize", onScroll);
      return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); cancelAnimationFrame(raf); };
    }, [speed, max]);
    return ref;
  }

  // ---- Count-up for stats ----
  function useInView(threshold = 0.3) {
    const ref = useRef(null);
    const [inView, setInView] = useState(false);
    useEffect(() => {
      const el = ref.current; if (!el) return;
      const obs = new IntersectionObserver((e) => { if (e[0].isIntersecting) { setInView(true); obs.disconnect(); } }, { threshold });
      obs.observe(el); return () => obs.disconnect();
    }, []);
    return [ref, inView];
  }

  window.Icons = Icons;
  window.Reveal = Reveal;
  window.useParallax = useParallax;
  window.useInView = useInView;
  window.prefersReduced = prefersReduced;

  // ---- Build an Instagram profile URL from a handle (e.g. "@eng.dina1119") ----
  window.igUrl = function (handle) {
    const h = String(handle || "").trim().replace(/^@/, "").replace(/^https?:\/\/(www\.)?instagram\.com\//i, "").replace(/\/$/, "");
    return "https://instagram.com/" + h;
  };


  // ---- Submit a form to the studio via Web3Forms (delivers to your inbox) ----
  // Get/replace the key at web3forms.com → it's tied to the email that receives
  // submissions. payload is a flat object of fields (name, email, message, …).
  window.WEB3FORMS_KEY = "ceb200dd-0a5d-48b8-84e0-e7f674185121";
  window.sendStudioForm = async function (payload) {
    const res = await fetch("https://api.web3forms.com/submit", {
      method: "POST",
      headers: { "Content-Type": "application/json", Accept: "application/json" },
      body: JSON.stringify({ access_key: window.WEB3FORMS_KEY, ...payload }),
    });
    const json = await res.json().catch(() => ({}));
    if (!res.ok || !json.success) throw new Error(json.message || "Submission failed");
    return json;
  };
})();

