/* ============================================================================
   Operation Outbreak — Wrapped  ·  SHARED PRIMITIVES
   Tokens, motion hooks, the brand "star" motif, Lucide icon bridge,
   glow tiles, pill buttons, share button, and the modal shell.
   Exposed on window for the other JSX files.
   ============================================================================ */
const { useState, useEffect, useRef, useCallback, useMemo } = React;

/* ── Brand tokens (dark "telemetry" mode, anchored to OO style guide) ────── */
const T = {
  ink:        "#08090A",
  panel:      "#11141A",
  panel2:     "#1F2528",
  line:       "rgba(255,255,255,0.09)",
  lineSoft:   "rgba(255,255,255,0.05)",
  fg:         "#F4F6F6",
  fg2:        "#9BA3A6",
  fg3:        "#5F676A",
  mint:       "#58DBA6",
  brightGreen:"#86F789",
  purple:     "#866AFF",
  blue:       "#5465E5",
  blue2:      "#7387F9",
  teal:       "#2AAD9A",
  coral:      "#ED5050",
  coral2:     "#FF7171",
  gradTech:   "linear-gradient(135deg, #7387F9 0%, #3F4AD1 100%)",
  gradInfect: "linear-gradient(135deg, #FF7171 0%, #DB3A3A 100%)",
  gradVioMint:"linear-gradient(135deg, #866AFF 0%, #58DBA6 100%)",
  sans:       "'Manrope', ui-sans-serif, system-ui, sans-serif",
  mono:       "'Geist Mono', ui-monospace, monospace",
};
window.T = T;

/* ── formatting ──────────────────────────────────────────────────────────── */
const fmt = (n, decimals = 0) =>
  Number(n).toLocaleString("en-US", { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
window.fmt = fmt;

/* ── useInView : fire once when element enters viewport ──────────────────── */
function useInView(opts = { threshold: 0.35 }) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setInView(true); io.disconnect(); }
    }, opts);
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return [ref, inView];
}
window.useInView = useInView;

/* ── useReveal : fire shortly after a slide becomes active (robust vs IO) ── */
function useReveal(active, delay = 140) {
  const [play, setPlay] = useState(false);
  useEffect(() => {
    if (active) { const t = setTimeout(() => setPlay(true), delay); return () => clearTimeout(t); }
  }, [active, delay]);
  return play;
}
window.useReveal = useReveal;

/* ── useCountUp : eased rAF count from 0 → target when `play` ────────────── */
function useCountUp(target, play, { duration = 1400, decimals = 0, delay = 0 } = {}) {
  const [val, setVal] = useState(0);
  useEffect(() => {
    if (!play) return;
    let raf, startT, to;
    const ease = (t) => 1 - Math.pow(1 - t, 3);
    const run = (now) => {
      if (startT == null) startT = now;
      const t = Math.min(1, (now - startT) / duration);
      setVal(target * ease(t));
      if (t < 1) raf = requestAnimationFrame(run);
      else setVal(target);
    };
    to = setTimeout(() => { raf = requestAnimationFrame(run); }, delay);
    return () => { cancelAnimationFrame(raf); clearTimeout(to); };
  }, [play, target, duration, delay]);
  return decimals > 0 ? Number(val).toFixed(decimals) : Math.round(val);
}
window.useCountUp = useCountUp;

/* CountUp display component */
function CountUp({ to, play, decimals = 0, duration = 1400, delay = 0, prefix = "", suffix = "", style, className }) {
  const v = useCountUp(to, play, { duration, decimals, delay });
  return <span style={style} className={className}>{prefix}{fmt(v, decimals)}{suffix}</span>;
}
window.CountUp = CountUp;

/* ── Brand star — 4-point concave "twinkle" matching the logo globe dots ──── */
function Star({ size = 24, color = "currentColor", style, className }) {
  return (
    <svg width={size} height={size} viewBox="0 0 100 100" fill="none" style={style} className={className} aria-hidden="true">
      <path d="M50 3 C56 31 69 44 97 50 C69 56 56 69 50 97 C44 69 31 56 3 50 C31 44 44 31 50 3 Z" fill={color} />
    </svg>
  );
}
window.Star = Star;

/* ── Lucide icon bridge (brand-sanctioned linear icon set) ───────────────── */
function Icon({ name, size = 24, color = "currentColor", stroke = 1.75, style, className }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el || !window.lucide) return;
    el.innerHTML = "";
    const i = document.createElement("i");
    i.setAttribute("data-lucide", name);
    el.appendChild(i);
    try {
      window.lucide.createIcons({
        attrs: { width: size, height: size, stroke: color, "stroke-width": stroke, "stroke-linecap": "round", "stroke-linejoin": "round" },
        nameAttr: "data-lucide",
      });
    } catch (e) {}
  }, [name, size, color, stroke]);
  return <span ref={ref} className={className} style={{ display: "inline-flex", lineHeight: 0, ...style }} />;
}
window.Icon = Icon;

/* ── Logo (white wordmark / globe) ───────────────────────────────────────── */
function Logo({ kind = "globe", height = 26, style }) {
  const src = kind === "globe" ? ((window.__resources && window.__resources.appIcon) || "assets/app-icon.png") : ((window.__resources && window.__resources.logoWhite) || "assets/logo-primary-white.png");
  return <img src={src} alt="Operation Outbreak" style={{ height, width: "auto", display: "block", borderRadius: kind === "globe" ? height * 0.22 : 0, ...style }} />;
}
window.Logo = Logo;

/* ── GlowTile : bento cell w/ optional accent glow on hover ──────────────── */
function GlowTile({ accent = T.mint, glow = true, span = 1, rowSpan = 1, pad = 22, children, style, className }) {
  const [hover, setHover] = useState(false);
  return (
    <div
      className={"oo-tile " + (className || "")}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{
        position: "relative",
        gridColumn: `span ${span}`,
        gridRow: `span ${rowSpan}`,
        background: T.panel,
        border: `1px solid ${hover && glow ? accentAlpha(accent, 0.5) : T.line}`,
        borderRadius: 18,
        padding: pad,
        overflow: "hidden",
        transition: "border-color 220ms cubic-bezier(.2,.8,.2,1), box-shadow 220ms cubic-bezier(.2,.8,.2,1), transform 220ms cubic-bezier(.2,.8,.2,1)",
        boxShadow: hover && glow ? `0 0 0 1px ${accentAlpha(accent, 0.18)}, 0 10px 40px -8px ${accentAlpha(accent, 0.35)}` : "none",
        transform: hover && glow ? "translateY(-2px)" : "none",
        ...style,
      }}
    >
      {children}
    </div>
  );
}
window.GlowTile = GlowTile;

function accentAlpha(hex, a) {
  const h = hex.replace("#", "");
  const r = parseInt(h.slice(0, 2), 16), g = parseInt(h.slice(2, 4), 16), b = parseInt(h.slice(4, 6), 16);
  return `rgba(${r},${g},${b},${a})`;
}
window.accentAlpha = accentAlpha;

/* ── Eyebrow (mono, uppercase, tracked) ──────────────────────────────────── */
function Eyebrow({ children, color = T.fg2, style }) {
  return (
    <div style={{ fontFamily: T.mono, fontSize: 12, fontWeight: 700, letterSpacing: "0.18em", textTransform: "uppercase", color, ...style }}>
      {children}
    </div>
  );
}
window.Eyebrow = Eyebrow;

/* ── ShareButton : the [ ↗ Label ] viral trigger ─────────────────────────── */
function ShareButton({ children, onClick, accent = T.mint, solid = false, style }) {
  const [hover, setHover] = useState(false);
  return (
    <button
      onClick={onClick}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{
        display: "inline-flex", alignItems: "center", gap: 10,
        fontFamily: T.mono, fontSize: 13, fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase",
        color: solid ? T.ink : (hover ? T.ink : accent),
        background: solid || hover ? accent : "transparent",
        border: `1.5px solid ${accent}`,
        padding: "13px 22px", borderRadius: 999, cursor: "pointer",
        transition: "all 200ms cubic-bezier(.2,.8,.2,1)",
        boxShadow: hover ? `0 8px 30px -6px ${accentAlpha(accent, 0.6)}` : "none",
        transform: hover ? "translateY(-1px)" : "none",
        ...style,
      }}
    >
      <Icon name="arrow-up-right" size={16} color={solid || hover ? T.ink : accent} stroke={2.25} />
      {children}
    </button>
  );
}
window.ShareButton = ShareButton;

/* ── Modal shell ─────────────────────────────────────────────────────────── */
function Modal({ open, onClose, children, maxWidth = 1080 }) {
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, onClose]);
  if (!open) return null;
  return (
    <div
      onClick={onClose}
      style={{
        position: "fixed", inset: 0, zIndex: 100,
        background: "rgba(5,6,7,0.74)",
        backdropFilter: "blur(8px)", WebkitBackdropFilter: "blur(8px)",
        display: "flex", alignItems: "center", justifyContent: "center", padding: 28,
      }}
    >
      <div onClick={(e) => e.stopPropagation()} style={{ width: "100%", maxWidth, maxHeight: "92vh", overflow: "auto" }}>
        {children}
      </div>
    </div>
  );
}
window.Modal = Modal;

/* ── Small ticker / status pill ──────────────────────────────────────────── */
function StatusDot({ color = T.mint, label, style }) {
  return (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 8, fontFamily: T.mono, fontSize: 11, fontWeight: 600, letterSpacing: "0.1em", color: T.fg2, ...style }}>
      <span style={{ width: 8, height: 8, borderRadius: 999, background: color, boxShadow: `0 0 10px ${accentAlpha(color, 0.8)}` }} />
      {label}
    </span>
  );
}
window.StatusDot = StatusDot;

Object.assign(window, { T, fmt, useInView, useCountUp, CountUp, Star, Icon, Logo, GlowTile, accentAlpha, Eyebrow, ShareButton, Modal, StatusDot });
