/* ============ SHARED PRIMITIVES ============ */
const { useState, useEffect, useRef } = React;

/* ── Reveal on scroll ─────────────────────────────────────────── */
function Reveal({ children, as: Tag = "div", delay = 0, className = "", style = {}, ...rest }) {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  const [done,  setDone]  = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let fired = false;
    const reveal = () => { if (!fired) { fired = true; setShown(true); cleanup(); } };
    const check = () => {
      const r  = el.getBoundingClientRect();
      const vh = window.innerHeight || 800;
      if (r.top < vh * 0.9 && r.bottom > 0) reveal();
    };
    const cleanup = () => {
      window.removeEventListener("scroll", check);
      window.removeEventListener("resize", check);
      clearTimeout(safety);
    };
    check();
    window.addEventListener("scroll", check, { passive: true });
    window.addEventListener("resize", check);
    const safety = setTimeout(reveal, 1100);
    return cleanup;
  }, []);
  useEffect(() => {
    if (!shown) return;
    const t = setTimeout(() => setDone(true), 1000);
    return () => clearTimeout(t);
  }, [shown]);
  return (
    <Tag
      ref={ref}
      className={`reveal ${shown ? "in" : ""} ${done ? "done" : ""} ${className}`}
      style={{ animationDelay: `${delay}ms`, ...style }}
      {...rest}
    >
      {children}
    </Tag>
  );
}

/* ── Striped placeholder (fallback when no real photo) ─────────── */
function Placeholder({ label, className = "", style = {}, rounded = "var(--r-md)" }) {
  return (
    <div className={`ph ${className}`} data-label={label}
      style={{ borderRadius: rounded, ...style }} />
  );
}

/* ── Real photo with graceful fallback ─────────────────────────── */
function Photo({ src, alt = "", label = "", className = "", style = {}, rounded = "var(--r-md)" }) {
  const [err, setErr] = useState(false);
  const noImg = !src || err;
  return (
    <div
      className={`ph ${className}`}
      data-label={noImg ? (label || alt) : ""}
      style={{ borderRadius: rounded, ...style }}
    >
      {!noImg && (
        <img
          src={src} alt={alt}
          onError={() => setErr(true)}
          loading="lazy"
        />
      )}
    </div>
  );
}

/* ── Star rating ───────────────────────────────────────────────── */
function Stars({ n = 5 }) {
  return (
    <div style={{ display: "flex", gap: 3, color: "var(--accent-2)" }} aria-label={`${n} out of 5 stars`}>
      {Array.from({ length: 5 }).map((_, i) => (
        <Icon key={i} name={i < n ? "star" : "star-o"} style={{ fontSize: 13 }} />
      ))}
    </div>
  );
}

/* ── Eyebrow label ─────────────────────────────────────────────── */
function Eyebrow({ children }) {
  return <span className="eyebrow">{children}</span>;
}

Object.assign(window, { Reveal, Placeholder, Photo, Stars, Eyebrow });
