/* ============================================================
   Shared primitives, hooks, icons, logo
   ============================================================ */
const { useState, useEffect, useRef, useCallback } = React;

/* --- reveal on scroll (rect-based; reliable across environments) --- */
function useInView(opts = {}) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let done = false;
    const reveal = () => { if (!done) { done = true; setSeen(true); cleanup(); } };
    const check = () => {
      if (done) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight || document.documentElement.clientHeight;
      if (r.top < vh * 0.92 && r.bottom > 0) reveal();
    };
    const cleanup = () => {
      window.removeEventListener("scroll", check);
      window.removeEventListener("resize", check);
    };
    window.addEventListener("scroll", check, { passive: true });
    window.addEventListener("resize", check);
    const raf = requestAnimationFrame(check);
    // failsafe: never leave content hidden
    const fs = setTimeout(reveal, 1600);
    return () => { cleanup(); cancelAnimationFrame(raf); clearTimeout(fs); };
  }, []);
  return [ref, seen];
}

/* a section wrapper that reveals children */
function Reveal({ as: Tag = "div", className = "", delay = "", children, ...rest }) {
  const [ref, seen] = useInView();
  return (
    <Tag ref={ref} className={`rise ${delay} ${seen ? "in" : ""} ${className}`} {...rest}>
      {children}
    </Tag>
  );
}

/* count-up number when in view (falls back to final value if rAF is paused) */
function CountUp({ to, dur = 1100, suffix = "", className = "" }) {
  const [ref, seen] = useInView({ threshold: 0.5 });
  const [v, setV] = useState(to);
  useEffect(() => {
    if (!seen) return;
    if (document.visibilityState !== "visible") { setV(to); return; }
    let raf, start;
    setV(0);
    const step = (t) => {
      if (!start) start = t;
      const p = Math.min((t - start) / dur, 1);
      const eased = 1 - Math.pow(1 - p, 3);
      setV(Math.round(eased * to));
      if (p < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [seen]);
  return <span ref={ref} className={className}>{v}{suffix}</span>;
}

/* --- logo --- */
function Logo({ light = false }) {
  const ink = light ? "#fff" : "var(--ink)";
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
      <img src="assets/sanddo-icon.png" alt="Sanddo" width="32" height="32" style={{ width: 32, height: 32, borderRadius: 9, display: "block", boxShadow: light ? "none" : "inset 0 0 0 1px var(--line)" }} />
      <span style={{ fontWeight: 800, fontSize: 20, letterSpacing: "-0.04em", color: ink }}>Sanddo</span>
    </div>
  );
}

/* --- inline icons --- */
const Icon = {
  arrow: (p) => <svg width="16" height="16" viewBox="0 0 16 16" fill="none" {...p}><path d="M3 8h9m0 0L8.5 4.5M12 8l-3.5 3.5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  check: (p) => <svg width="16" height="16" viewBox="0 0 16 16" fill="none" {...p}><path d="M3.5 8.5l2.8 2.8L12.5 5" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  shield: (p) => <svg width="20" height="20" viewBox="0 0 24 24" fill="none" {...p}><path d="M12 3l7 2.5v6c0 4.2-2.9 7.5-7 8.8C7.9 19 5 15.7 5 11.5v-6L12 3Z" stroke="currentColor" strokeWidth="1.7" strokeLinejoin="round"/><path d="M8.8 12l2.2 2.2 4.2-4.4" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  eye: (p) => <svg width="20" height="20" viewBox="0 0 24 24" fill="none" {...p}><path d="M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12Z" stroke="currentColor" strokeWidth="1.7" strokeLinejoin="round"/><circle cx="12" cy="12" r="2.6" stroke="currentColor" strokeWidth="1.7"/></svg>,
  spark: (p) => <svg width="20" height="20" viewBox="0 0 24 24" fill="none" {...p}><path d="M12 3v4M12 17v4M3 12h4M17 12h4M6 6l2.5 2.5M15.5 15.5 18 18M18 6l-2.5 2.5M8.5 15.5 6 18" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/></svg>,
  book: (p) => <svg width="20" height="20" viewBox="0 0 24 24" fill="none" {...p}><path d="M4 5.5C4 4.7 4.7 4 5.5 4H11v15H5.5C4.7 19 4 18.3 4 17.5v-12Z" stroke="currentColor" strokeWidth="1.7" strokeLinejoin="round"/><path d="M20 5.5C20 4.7 19.3 4 18.5 4H13v15h5.5c.8 0 1.5-.7 1.5-1.5v-12Z" stroke="currentColor" strokeWidth="1.7" strokeLinejoin="round"/></svg>,
  users: (p) => <svg width="20" height="20" viewBox="0 0 24 24" fill="none" {...p}><circle cx="9" cy="8" r="3" stroke="currentColor" strokeWidth="1.7"/><path d="M3.5 19c.5-3 2.8-4.6 5.5-4.6S14 16 14.5 19" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/><path d="M16 5.2A3 3 0 0 1 16.5 11M17 14.6c2 .5 3.6 2 4 4.4" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/></svg>,
  bolt: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" {...p}><path d="M13 2 4 14h7l-1 8 9-12h-7l1-8Z" stroke="currentColor" strokeWidth="1.7" strokeLinejoin="round"/></svg>,
  lock: (p) => <svg width="16" height="16" viewBox="0 0 24 24" fill="none" {...p}><rect x="5" y="10.5" width="14" height="9.5" rx="2" stroke="currentColor" strokeWidth="1.7"/><path d="M8 10.5V8a4 4 0 0 1 8 0v2.5" stroke="currentColor" strokeWidth="1.7"/></svg>,
};

/* circular trust-score gauge */
function Gauge({ value = 92, size = 64, stroke = 6, color = "var(--auto)", label }) {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const [ref, seen] = useInView({ threshold: 0.6 });
  const [shown, setShown] = useState(value);
  useEffect(() => {
    if (!seen) return;
    if (document.visibilityState !== "visible") { setShown(value); return; }
    let raf, start;
    setShown(0);
    const step = (t) => { if (!start) start = t; const p = Math.min((t - start) / 900, 1);
      setShown(Math.round((1 - Math.pow(1 - p, 3)) * value)); if (p < 1) raf = requestAnimationFrame(step); };
    raf = requestAnimationFrame(step); return () => cancelAnimationFrame(raf);
  }, [seen, value]);
  return (
    <div ref={ref} style={{ position: "relative", width: size, height: size, flex: "none" }}>
      <svg width={size} height={size} style={{ transform: "rotate(-90deg)" }}>
        <circle cx={size/2} cy={size/2} r={r} stroke="var(--line-2)" strokeWidth={stroke} fill="none" opacity=".5"/>
        <circle cx={size/2} cy={size/2} r={r} stroke={color} strokeWidth={stroke} fill="none"
          strokeLinecap="round" strokeDasharray={c} strokeDashoffset={c - (shown/100)*c}
          style={{ transition: "stroke-dashoffset .1s linear" }}/>
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", lineHeight: 1 }}>
        <div style={{ fontFamily: "var(--mono)", fontWeight: 600, fontSize: size*0.27, color: "var(--ink)" }}>{shown}</div>
      </div>
    </div>
  );
}

Object.assign(window, { useInView, Reveal, CountUp, Logo, Icon, Gauge,
  useState, useEffect, useRef, useCallback });
