/* Compound — shared primitives (Swiss/technical) */

/* ---------- META label (mono caps) ---------- */
const Meta = ({ children, className = "", style, large = false }) => (
  <span className={(large ? "meta-lg " : "meta ") + className} style={style}>{children}</span>
);

const Hairline = ({ strong = false, className = "", style }) => (
  <div className={(strong ? "hairline" : "hairline-soft") + " " + className} style={style}></div>
);

const Vrule = ({ strong = false, className = "", style }) => (
  <div className={(strong ? "vrule" : "vrule-soft") + " " + className} style={style}></div>
);

/* Animate an element directly via WAAPI — bypasses CSS class change quirks */
function animateIn(el, keyframes, options) {
  if (!el || !el.animate) return;
  try {
    const a = el.animate(keyframes, { fill: "forwards", easing: "cubic-bezier(.2,.7,.2,1)", ...options });
    if (a.startTime == null) {
      try { a.startTime = document.timeline.currentTime; } catch(e) {}
    }
    return a;
  } catch(e) {}
}

function kickAnimations(el) {
  if (!el || !el.getAnimations) return;
  el.getAnimations().forEach(a => {
    if (a.pending || a.startTime == null) {
      try { a.startTime = document.timeline.currentTime; } catch(e) {}
    }
  });
}

/* Global reveal scheduler — uses rAF polling instead of IntersectionObserver
   (IO doesn't fire reliably in this preview iframe) */
const _revealRegistry = new Set();
let _revealLoopRunning = false;
function _revealTick() {
  const wh = window.innerHeight;
  for (const item of _revealRegistry) {
    const { el } = item;
    if (!el.isConnected) { _revealRegistry.delete(item); continue; }
    const rect = el.getBoundingClientRect();
    if (rect.top < wh - 40 && rect.bottom > 0) {
      _revealRegistry.delete(item);
      try { item.fire(); } catch(e) {}
    }
  }
  if (_revealRegistry.size > 0) requestAnimationFrame(_revealTick);
  else _revealLoopRunning = false;
}
function registerReveal(el, fire) {
  const item = { el, fire };
  _revealRegistry.add(item);
  if (!_revealLoopRunning) {
    _revealLoopRunning = true;
    requestAnimationFrame(_revealTick);
  }
  return () => _revealRegistry.delete(item);
}

/* ---------- Reveal on viewport ---------- */
const Reveal = ({ as: Tag = "div", delay = 0, children, className = "", style = {}, ...rest }) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const fire = () => {
      setTimeout(() => {
        animateIn(el, [
          { opacity: 0, transform: "translateY(18px)" },
          { opacity: 1, transform: "translateY(0)" }
        ], { duration: 900 });
      }, delay);
    };
    return registerReveal(el, fire);
  }, [delay]);
  return <Tag ref={ref} className={className} style={{ opacity: 0, transform: "translateY(18px)", ...style }} {...rest}>{children}</Tag>;
};

/* ---------- Word-by-word reveal ---------- */
const WordReveal = ({ text, className = "", stagger = 60, start = 0 }) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const words = el.querySelectorAll(".word");
    words.forEach(w => {
      w.style.opacity = "0";
      w.style.transform = "translateY(0.4em)";
      w.style.display = "inline-block";
    });
    const fire = () => {
      words.forEach((w, i) => {
        setTimeout(() => {
          animateIn(w, [
            { opacity: 0, transform: "translateY(0.4em)" },
            { opacity: 1, transform: "translateY(0)" }
          ], { duration: 700 });
        }, start + i * stagger);
      });
    };
    return registerReveal(el, fire);
  }, [stagger, start]);

  const lines = text.split("\n");
  return (
    <span ref={ref} className={className} style={{ display: "inline-block", verticalAlign: "top" }}>
      {lines.map((line, li) => (
        <span key={li} style={{ display: "block" }}>
          {line.split(" ").map((w, i) => (
            <span key={i} className="word" style={{ marginRight: "0.22em", display: "inline-block", opacity: 0 }}>{w}</span>
          ))}
        </span>
      ))}
    </span>
  );
};

/* ---------- Count up ---------- */
const CountUp = ({ to, suffix = "", prefix = "", duration = 1400, decimals = 0, className = "" }) => {
  const ref = React.useRef(null);
  const [val, setVal] = React.useState(0);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf;
    const fire = () => {
      const t0 = performance.now();
      const tick = (t) => {
        const p = Math.min(1, (t - t0) / duration);
        const eased = 1 - Math.pow(1 - p, 3);
        setVal(eased * to);
        if (p < 1) raf = requestAnimationFrame(tick);
      };
      raf = requestAnimationFrame(tick);
    };
    const unreg = registerReveal(el, fire);
    return () => { unreg(); if (raf) cancelAnimationFrame(raf); };
  }, [to, duration]);
  return <span ref={ref} className={"tnum " + className}>{prefix}{val.toFixed(decimals)}{suffix}</span>;
};

/* ---------- Bar that fills on view ---------- */
const BarFill = ({ to = 100, duration = 1400, delay = 0, accent = false, className = "" }) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const fill = el.querySelector(".bar-fill");
    if (!fill) return;
    const fire = () => {
      setTimeout(() => {
        animateIn(fill, [
          { width: "0%" },
          { width: to + "%" }
        ], { duration });
        // also set final width so it stays
        fill.style.width = to + "%";
      }, delay);
    };
    return registerReveal(el, fire);
  }, [to, duration, delay]);
  return (
    <div ref={ref} className={"bar-track " + className}>
      <div className={"bar-fill " + (accent ? "accent" : "")}></div>
    </div>
  );
};

/* ---------- Parallax (vertical translate) ---------- */
const Parallax = ({ children, speed = 0.25, className = "", style = {} }) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let rafId = 0;
    const onScroll = () => {
      if (rafId) return;
      rafId = requestAnimationFrame(() => {
        rafId = 0;
        const rect = el.getBoundingClientRect();
        const winH = window.innerHeight;
        if (rect.bottom < 0 || rect.top > winH) return;
        const center = rect.top + rect.height / 2;
        const delta = (center - winH / 2) * speed;
        el.style.transform = `translate3d(0, ${-delta}px, 0)`;
      });
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => { window.removeEventListener("scroll", onScroll); };
  }, [speed]);
  return <div ref={ref} className={className} style={{ willChange: "transform", ...style }}>{children}</div>;
};

/* ---------- Scroll progress bar ---------- */
function ScrollProgress() {
  React.useEffect(() => {
    const bar = document.getElementById("scroll-progress");
    if (!bar) return;
    let rafId = 0;
    const onScroll = () => {
      if (rafId) return;
      rafId = requestAnimationFrame(() => {
        rafId = 0;
        const h = document.documentElement;
        const max = h.scrollHeight - h.clientHeight;
        const p = max > 0 ? (h.scrollTop || window.scrollY) / max : 0;
        bar.style.width = (p * 100).toFixed(2) + "%";
      });
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  return null;
}

/* ---------- Live clock (UTC ticker) ---------- */
function LiveClock({ tz = "America/Los_Angeles", label = "PT" }) {
  const [now, setNow] = React.useState(new Date());
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  const fmt = new Intl.DateTimeFormat("en-US", {
    timeZone: tz, hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false
  }).format(now);
  return <span className="tnum">{fmt} {label}</span>;
}

/* ---------- Corner crosshairs (decorative) ---------- */
const Crosshairs = ({ children, className = "", style = {} }) => (
  <div className={"relative " + className} style={style}>
    <span className="crosshair" style={{ top: -6, left: -6 }}></span>
    <span className="crosshair" style={{ top: -6, right: -6 }}></span>
    <span className="crosshair" style={{ bottom: -6, left: -6 }}></span>
    <span className="crosshair" style={{ bottom: -6, right: -6 }}></span>
    {children}
  </div>
);

/* ---------- Animated compound mark ---------- */
/* A target/crosshair that draws itself in. SVG with stroke-dasharray reveal. */
function Mark({ size = 32, className = "", style }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const paths = el.querySelectorAll("[data-draw]");
    paths.forEach(p => {
      const len = p.getTotalLength ? p.getTotalLength() : 200;
      p.style.strokeDasharray = len;
      p.style.strokeDashoffset = len;
    });
    const fire = () => {
      paths.forEach((p, i) => {
        const len = p.getTotalLength ? p.getTotalLength() : 200;
        animateIn(p, [
          { strokeDashoffset: len },
          { strokeDashoffset: 0 }
        ], { duration: 900, delay: i * 120 });
        p.style.strokeDashoffset = 0;
      });
    };
    return registerReveal(el, fire);
  }, []);
  const s = size;
  return (
    <svg ref={ref} width={s} height={s} viewBox="0 0 32 32" className={className} style={style} fill="none">
      <circle cx="16" cy="16" r="14" stroke="currentColor" strokeWidth="1" data-draw />
      <circle cx="16" cy="16" r="7" stroke="currentColor" strokeWidth="1" data-draw />
      <line x1="16" y1="0" x2="16" y2="6" stroke="currentColor" strokeWidth="1" data-draw />
      <line x1="16" y1="26" x2="16" y2="32" stroke="currentColor" strokeWidth="1" data-draw />
      <line x1="0" y1="16" x2="6" y2="16" stroke="currentColor" strokeWidth="1" data-draw />
      <line x1="26" y1="16" x2="32" y2="16" stroke="currentColor" strokeWidth="1" data-draw />
      <circle cx="16" cy="16" r="1.5" fill="currentColor" />
    </svg>
  );
}

Object.assign(window, {
  Meta, Hairline, Vrule, Reveal, WordReveal, CountUp, BarFill, Parallax,
  ScrollProgress, LiveClock, Crosshairs, Mark
});
