/* lib.jsx — shared hooks, icons, helpers (exported to window) */
const { useState, useEffect, useRef, useCallback } = React;

/* ---- icons ---- */
const Arrow = ({ size = 16, cls = "" }) => (
  <svg className={cls} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
    <path d="M5 12h14M13 6l6 6-6 6" />
  </svg>
);
const ArrowUR = ({ size = 16, cls = "" }) => (
  <svg className={cls} width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
    <path d="M7 17 17 7M8 7h9v9" />
  </svg>
);
const Check = ({ size = 22 }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
    <path d="m5 13 4 4L19 7" />
  </svg>
);

/* ---- scroll reveal ---- */
function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll(".reveal:not(.in)");
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); }
      });
    }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" });
    els.forEach((el) => io.observe(el));
    return () => io.disconnect();
  });
}

/* ---- count up when in view ---- */
function useCountUp(end, { duration = 1600, decimals = 0 } = {}) {
  const ref = useRef(null);
  const [val, setVal] = useState(0);
  const done = useRef(false);
  useEffect(() => {
    const node = ref.current;
    if (!node) return;
    const io = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting && !done.current) {
        done.current = true;
        const start = performance.now();
        const tick = (now) => {
          const p = Math.min(1, (now - start) / duration);
          const eased = 1 - Math.pow(1 - p, 3);
          setVal(end * eased);
          if (p < 1) requestAnimationFrame(tick);
          else setVal(end);
        };
        requestAnimationFrame(tick);
      }
    }, { threshold: 0.4 });
    io.observe(node);
    return () => io.disconnect();
  }, [end, duration]);
  return [ref, decimals ? val.toFixed(decimals) : Math.round(val)];
}

/* ---- image slot (user-fillable) with fallback label ---- */
function Slot({ id, label, shape = "rounded", radius = 14, fit = "cover" }) {
  return (
    <image-slot
      id={id}
      shape={shape}
      radius={String(radius)}
      fit={fit}
      placeholder={label}
    ></image-slot>
  );
}

/* ---- eyebrow ---- */
function Eyebrow({ n, children, tag }) {
  return (
    <span className="eyebrow">
      <span className="tag">[{n}]</span>
      {children}
    </span>
  );
}

/* parse "*word*" emphasis into <em> */
function Emphi({ text }) {
  const parts = String(text).split(/(\*[^*]+\*)/g);
  return parts.map((p, i) =>
    p.startsWith("*") && p.endsWith("*")
      ? <em key={i}>{p.slice(1, -1)}</em>
      : <React.Fragment key={i}>{p}</React.Fragment>
  );
}

Object.assign(window, { useState, useEffect, useRef, useCallback, Arrow, ArrowUR, Check, useReveal, useCountUp, Slot, Eyebrow, Emphi });
