// ui.jsx - shared primitives + icon set (exported to window)
const { useState, useEffect, useRef, useCallback } = React;

/* ---------------- Icons (VaultCova brand language: vault-dial + V-chevron) ---------------- */
const ICON_PATHS = {
  // command tiles + one live dial
  dashboard: "M3 5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5zM17.5 4.2a3.3 3.3 0 1 0 0 6.6a3.3 3.3 0 1 0 0-6.6M17.5 7.5h.01M3 15a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4zM14 15a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2v-4z",
  // vault plate - the logo mark as an icon
  vault: "M8 3h8a5 5 0 0 1 5 5v8a5 5 0 0 1-5 5H8a5 5 0 0 1-5-5V8a5 5 0 0 1 5-5zM12 8.6a3.4 3.4 0 1 0 0 6.8a3.4 3.4 0 1 0 0-6.8M12 11.95h.01M12 6v1.4M12 16.6V18M6 12h1.4M16.6 12H18",
  // shield carrying the V
  shield: "M12 3l7 2.6v5.6c0 4.4-2.9 7.6-7 9.8-4.1-2.2-7-5.4-7-9.8V5.6L12 3zM9 9.5l3 4.8 3-4.8",
  // speech bubble with dial ticks - talking to the vault
  chat: "M21 15a2 2 0 0 1-2 2H8l-4 4V5a2 2 0 0 1 2-2h13a2 2 0 0 1 2 2v10zM12 7.4v1.3M12 13.3v1.3M9 11h1.3M13.7 11H15M12 10.95h.01",
  quote: "M9 7h6M9 11h6M9 15h4M5 4h14a1 1 0 0 1 1 1v15l-3-2-3 2-3-2-3 2-3-2V5a1 1 0 0 1 1-1z",
  // settlement seal - V-check in a stamped circle with ribbons
  claim: "M12 2.6a6.7 6.7 0 1 0 0 13.4a6.7 6.7 0 1 0 0-13.4M9.3 9.2l2.1 2.6 3.4-4M8.6 15.4L7 21.4l5-2.1 5 2.1-1.6-6",
  plus: "M12 5v14M5 12h14",
  arrowR: "M5 12h14M13 6l6 6-6 6",
  arrowUpR: "M7 17L17 7M9 7h8v8",
  send: "M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z",
  mic: "M12 2a3 3 0 0 1 3 3v6a3 3 0 0 1-6 0V5a3 3 0 0 1 3-3zM19 10a7 7 0 0 1-14 0M12 19v3",
  // Cova - the vault dial itself (the logo)
  sparkle: "M12 5.2a6.8 6.8 0 1 0 0 13.6a6.8 6.8 0 1 0 0-13.6M12 11.95h.01M12 2v3.2M12 18.8V22M2 12h3.2M18.8 12H22",
  bell: "M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9M13.7 21a2 2 0 0 1-3.4 0",
  search: "M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM21 21l-4.3-4.3",
  car: "M5 13l1.5-4.5A2 2 0 0 1 8.4 7h7.2a2 2 0 0 1 1.9 1.5L19 13M5 13h14v4a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1H8a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-4zM7.5 16h.01M16.5 16h.01",
  home: "M3 11l9-7 9 7M5 10v9a1 1 0 0 0 1 1h4v-6h4v6h4a1 1 0 0 0 1-1v-9",
  // vault wheel - settings as the handle you turn
  cog: "M12 6.2a5.8 5.8 0 1 0 0 11.6a5.8 5.8 0 1 0 0-11.6M12 9.8a2.2 2.2 0 1 0 0 4.4a2.2 2.2 0 1 0 0-4.4M12 2.6v3.6M12 17.8v3.6M2.6 12h3.6M17.8 12h3.6M5.4 5.4l2.5 2.5M16.1 16.1l2.5 2.5M18.6 5.4l-2.5 2.5M7.9 16.1l-2.5 2.5",
  chip: "M9 9h6v6H9zM4 9h2M4 15h2M18 9h2M18 15h2M9 4v2M15 4v2M9 18v2M15 18v2M6 6h12v12H6z",
  box: "M21 8l-9-5-9 5 9 5 9-5zM3 8v8l9 5 9-5V8M12 13v8",
  gem: "M6 3h12l3 6-9 12L3 9l3-6zM3 9h18M9 3L6 9l6 12M15 3l3 6-6 12",
  ship: "M3 14l1.5 5h15L21 14M12 3v11M5 11l7-3 7 3M12 8V6h3M7 19l-1 2M17 19l1 2",
  close: "M6 6l12 12M18 6L6 18",
  check: "M5 13l4 4L19 7",
  chevR: "M9 6l6 6-6 6",
  chevD: "M6 9l6 6 6-6",
  link: "M10 13a5 5 0 0 0 7 0l2-2a5 5 0 0 0-7-7l-1 1M14 11a5 5 0 0 0-7 0l-2 2a5 5 0 0 0 7 7l1-1",
  layers: "M12 3l9 5-9 5-9-5 9-5zM3 13l9 5 9-5M3 17l9 5 9-5",
  doc: "M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5zM14 3v5h5M9 13h6M9 17h6",
  // value circles back into the vault - cashback
  trend: "M12 4.4a7.6 7.6 0 1 0 7.6 7.6M19.6 4.4v4h-4M19.6 4.4l-4.6 4.6M12 8.6v6M9.6 12.8l2.4 1.8 2.4-1.8",
  alert: "M12 9v4M12 17h.01M10.3 3.9L2 18a2 2 0 0 0 1.7 3h16.6a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z",
  logout: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9",
  user: "M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM4 21v-1a6 6 0 0 1 6-6h4a6 6 0 0 1 6 6v1",
  building: "M4 21V5a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1v16M15 21V9h4a1 1 0 0 1 1 1v11M8 7h3M8 11h3M8 15h3",
  moon: "M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z",
  sun: "M12 17a5 5 0 1 0 0-10 5 5 0 0 0 0 10zM12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4",
  filter: "M3 5h18M6 12h12M10 19h4",
  grid: "M3 3h8v8H3zM13 3h8v8h-8zM13 13h8v8h-8zM3 13h8v8H3z",
  list: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01",
  download: "M12 3v12M7 11l5 4 5-4M5 21h14",
  share: "M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7M16 6l-4-4-4 4M12 2v13",
  clock: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM12 7v5l3 2",
  // watchful eye with a dial pupil
  eye: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7zM12 9.4a2.6 2.6 0 1 0 0 5.2a2.6 2.6 0 1 0 0-5.2M12 11.95h.01",
  flag: "M4 21V4M4 5h13l-2.5 4L17 13H4",
  pause: "M9 5v14M15 5v14",
  // paperclip (attach a file to chat -> import to VaultLog)
  clip: "M21 11.5l-8.5 8.5a5.5 5.5 0 0 1-7.8-7.8l8.5-8.5a3.7 3.7 0 0 1 5.2 5.2l-8.5 8.5a1.8 1.8 0 0 1-2.6-2.6l7.8-7.8",
  // map pin (place / location) — the By-location view toggle + address fields
  map: "M20 10.2c0 5.8-8 11.8-8 11.8s-8-6-8-11.8a8 8 0 0 1 16 0zM12 7.6a2.6 2.6 0 1 0 0 5.2a2.6 2.6 0 1 0 0-5.2",
  // padlock (plan-gated feature)
  lock: "M6 11h12a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1zM8 11V7a4 4 0 0 1 8 0v4M12 15v2",
  // waste bin (delete asset)
  trash: "M4 7h16M10 7V5a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v2M6 7l1 13a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1l1-13M10 11v6M14 11v6",
  // pencil (rename / edit)
  edit: "M4 20h4L18.5 9.5a2 2 0 0 0-2.8-2.8L5 17.2 4 20zM14 7l3 3",
  // phone / device (electronics cover art)
  phone: "M7 2h10a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1zM10 18.5h4",
  // rising line chart (VaultLog growth-over-time view)
  growth: "M4 4v16h16M8 14l3-4 3 3 4-6M18 7h1.5v1.5",
};

function Icon({ name, size = 20, stroke = 1.7, className = "", style = {} }) {
  const d = ICON_PATHS[name];
  if (!d) return null;
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round"
      className={className} style={style} aria-hidden="true">
      {d.split("M").filter(Boolean).map((seg, i) => <path key={i} d={"M" + seg} />)}
    </svg>
  );
}

/* ---------------- Button ---------------- */
function Btn({ children, variant = "primary", size = "md", icon, iconR, onClick, style = {}, className = "", disabled, type = "button" }) {
  const [hover, setHover] = useState(false);
  const sizes = {
    sm: { padding: "8px 14px", fontSize: 13, gap: 7 },
    md: { padding: "11px 18px", fontSize: 14.5, gap: 8 },
    lg: { padding: "15px 26px", fontSize: 16, gap: 10 },
  }[size];
  const variants = {
    primary: { background: "var(--accent)", color: "var(--on-accent)", boxShadow: hover ? "var(--shadow-glow)" : "var(--shadow-sm)" },
    ghost: { background: hover ? "var(--surface-3)" : "transparent", color: "var(--text)" },
    soft: { background: hover ? "var(--accent-soft)" : "var(--surface-3)", color: "var(--accent-ink)" },
    outline: { background: hover ? "var(--surface-3)" : "transparent", color: "var(--text)", boxShadow: "inset 0 0 0 1px var(--border-strong)" },
    danger: { background: hover ? "var(--danger)" : "var(--danger-soft)", color: hover ? "#fff" : "var(--danger)" },
    dark: { background: "var(--text)", color: "var(--surface)" },
  }[variant];
  return (
    <button type={type} onClick={onClick} disabled={disabled}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      className={"vc-focus " + className}
      style={{
        display: "inline-flex", alignItems: "center", justifyContent: "center", gap: sizes.gap,
        padding: sizes.padding, fontSize: sizes.fontSize, fontWeight: 600, fontFamily: "var(--font-body)",
        borderRadius: 999, transition: "all .25s var(--ease)", whiteSpace: "nowrap",
        transform: hover && !disabled ? "translateY(-1px)" : "none",
        opacity: disabled ? 0.5 : 1, pointerEvents: disabled ? "none" : "auto",
        ...variants, ...style,
      }}>
      {icon && <Icon name={icon} size={sizes.fontSize + 3} />}
      {children}
      {iconR && <Icon name={iconR} size={sizes.fontSize + 3} />}
    </button>
  );
}

/* ---------------- Badge ---------------- */
function StatusBadge({ status, size = "md" }) {
  const m = window.VC_DATA.STATUS_META[status] || window.VC_DATA.STATUS_META.uninsured;
  const { t } = window.useT();
  const sm = size === "sm";
  return (
    <span className="vc-nowrap" style={{
      display: "inline-flex", alignItems: "center", gap: 6, padding: sm ? "3px 9px" : "5px 12px",
      borderRadius: 999, fontSize: sm ? 11 : 12.5, fontWeight: 700, letterSpacing: ".01em",
      background: m.soft, color: m.color,
    }}>
      <span style={{ width: 6, height: 6, borderRadius: 999, background: m.color, boxShadow: status === "lapsing" ? "0 0 0 0 " + m.color : "none", animation: status !== "insured" ? "vc-pulse-ring 2s infinite" : "none" }} />
      {t("status." + status)}
    </span>
  );
}

/* ---------------- Category thumb (striped placeholder) ---------------- */
// Which glyph best "reads" as each category on a cover. Falls back to the
// category's own glyph. Electronics leads with a phone, the most recognisable
// device, rather than the generic chip.
const COVER_GLYPH = { electronics: "phone" };
function coverGlyph(category) {
  const c = window.VC_DATA.CATEGORIES[category];
  return COVER_GLYPH[category] || (c && c.glyph) || "box";
}

function AssetThumb({ category, size = 48, radius = 12 }) {
  const c = window.VC_DATA.CATEGORIES[category] || { hue: 260 };
  return (
    <div style={{
      width: size, height: size, borderRadius: radius, flexShrink: 0, position: "relative", overflow: "hidden",
      background: `linear-gradient(150deg, oklch(0.7 0.13 ${c.hue}) 0%, oklch(0.5 0.14 ${c.hue}) 100%)`,
      display: "grid", placeItems: "center", color: "#fff",
    }}>
      <div style={{ position: "absolute", inset: 0, background: `radial-gradient(120% 100% at 15% 0%, rgba(255,255,255,.28), transparent 60%)` }} />
      <Icon name={coverGlyph(category)} size={size * 0.46} stroke={1.7} style={{ position: "relative", filter: "drop-shadow(0 1px 2px rgba(0,0,0,.3))" }} />
    </div>
  );
}

// Full-bleed animated cover for the asset detail hero when the user hasn't
// uploaded a photo yet. A soft gradient in the asset's own hue with a gently
// floating glyph that "reads" as the property type (house, car, phone, …),
// plus a faint oversized glyph drifting behind for depth. Honours
// prefers-reduced-motion (see styles.css).
function AssetCoverArt({ category, height = 180 }) {
  const c = window.VC_DATA.CATEGORIES[category] || { hue: 260 };
  const g = coverGlyph(category);
  return (
    <div style={{ position: "absolute", inset: 0, overflow: "hidden",
      background: `radial-gradient(130% 130% at 22% 0%, oklch(0.74 0.12 ${c.hue}) 0%, oklch(0.54 0.14 ${c.hue}) 52%, oklch(0.44 0.13 ${c.hue}) 100%)` }}>
      <Icon name={g} size={height * 1.7} stroke={1} className="vc-cover-drift" style={{ position: "absolute", right: -height * 0.42, top: -height * 0.34, color: "#fff", opacity: 0.1 }} />
      <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center" }}>
        <div className="vc-cover-float" style={{ display: "grid", placeItems: "center", width: height * 0.44, height: height * 0.44, borderRadius: 22, background: "rgba(255,255,255,.16)", border: "1px solid rgba(255,255,255,.28)", boxShadow: "0 12px 34px -10px rgba(0,0,0,.4)" }}>
          <Icon name={g} size={height * 0.24} stroke={1.6} style={{ color: "#fff", filter: "drop-shadow(0 2px 4px rgba(0,0,0,.3))" }} />
        </div>
      </div>
    </div>
  );
}

/* ---------------- Animated counter ---------------- */
function useCountUp(target, dur = 1100, deps = []) {
  const [val, setVal] = useState(0);
  useEffect(() => {
    let raf, start, done = false;
    const from = 0;
    const step = (t) => {
      if (!start) start = t;
      const p = Math.min(1, (t - start) / dur);
      const e = 1 - Math.pow(1 - p, 3);
      setVal(from + (target - from) * e);
      if (p < 1) raf = requestAnimationFrame(step); else done = true;
    };
    raf = requestAnimationFrame(step);
    // fallback: guarantee end state even if rAF is throttled (background iframe, reduced motion)
    const fb = setTimeout(() => { if (!done) setVal(target); }, dur + 150);
    return () => { cancelAnimationFrame(raf); clearTimeout(fb); };
  }, deps.length ? deps : [target]);
  return val;
}

/* ---------------- Ring / donut ---------------- */
function CoverageRing({ pct, size = 132, stroke = 13, label, sub }) {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const animPct = useCountUp(pct, 1300, [pct]);
  const off = c * (1 - animPct);
  const color = pct > 0.9 ? "var(--success)" : pct > 0.65 ? "var(--warning)" : "var(--danger)";
  return (
    <div style={{ position: "relative", width: size, height: size }}>
      <svg width={size} height={size} style={{ transform: "rotate(-90deg)" }}>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="var(--surface-3)" strokeWidth={stroke} />
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={color} strokeWidth={stroke}
          strokeLinecap="round" strokeDasharray={c} strokeDashoffset={off}
          style={{ transition: "stroke .4s", filter: `drop-shadow(0 2px 8px ${color})` }} />
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", textAlign: "center" }}>
        <div>
          <div className="display" style={{ fontSize: size * 0.24, lineHeight: 1, color }}>{Math.round(animPct * 100)}%</div>
          {sub && <div style={{ fontSize: 11, color: "var(--text-faint)", marginTop: 4, fontWeight: 600 }}>{sub}</div>}
        </div>
      </div>
    </div>
  );
}

/* ---------------- Modal / overlay ---------------- */
function Overlay({ open, onClose, children, width = 560, align = "center" }) {
  useEffect(() => {
    const h = (e) => e.key === "Escape" && onClose && onClose();
    if (open) window.addEventListener("keydown", h);
    return () => window.removeEventListener("keydown", h);
  }, [open, onClose]);
  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position: "absolute", inset: 0, zIndex: 200, display: "flex",
      alignItems: align === "center" ? "center" : "flex-end", justifyContent: "center",
      padding: align === "center" ? 28 : 0,
      background: "rgba(8,9,26,0.55)", backdropFilter: "blur(8px) saturate(140%)",
      WebkitBackdropFilter: "blur(8px) saturate(140%)", animation: "vc-fade-in .3s var(--ease) both",
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width, maxWidth: "94%", maxHeight: "92%", overflow: "auto", background: "var(--surface)",
        borderRadius: align === "center" ? "var(--radius-lg)" : "var(--radius-lg) var(--radius-lg) 0 0",
        boxShadow: "var(--shadow-xl)", border: "1px solid var(--border)",
        animation: (align === "center" ? "vc-scale-in" : "vc-fade-up") + " .4s var(--ease) both",
      }}>
        {children}
      </div>
    </div>
  );
}

/* ---------------- Toast host ---------------- */
function ToastHost({ toasts }) {
  return (
    <div style={{ position: "absolute", bottom: 24, left: "50%", transform: "translateX(-50%)", zIndex: 400, display: "flex", flexDirection: "column", gap: 10, alignItems: "center", pointerEvents: "none" }}>
      {toasts.map((t) => (
        <div key={t.id} style={{
          display: "flex", alignItems: "center", gap: 11, padding: "12px 18px", borderRadius: 14,
          background: "var(--text)", color: "var(--surface)", fontSize: 14, fontWeight: 600,
          boxShadow: "var(--shadow-lg)", animation: "vc-fade-up .4s var(--ease) both",
        }}>
          <span style={{ display: "grid", placeItems: "center", width: 22, height: 22, borderRadius: 999, background: "var(--accent)", color: "#fff" }}>
            <Icon name={t.icon || "check"} size={14} stroke={2.4} />
          </span>
          {t.msg}
        </div>
      ))}
    </div>
  );
}

/* ---------------- Bar (coverage gap) ---------------- */
function CoverageBar({ insured, replacement, height = 10 }) {
  const pct = replacement ? Math.min(1, insured / replacement) : 0;
  const w = useCountUp(pct, 1000, [pct, insured]);
  const color = pct > 0.9 ? "var(--success)" : pct > 0.65 ? "var(--warning)" : "var(--danger)";
  return (
    <div style={{ height, borderRadius: 999, background: "var(--surface-3)", overflow: "hidden", position: "relative" }}>
      <div style={{ height: "100%", width: (w * 100) + "%", background: color, borderRadius: 999, transition: "width .3s" }} />
    </div>
  );
}

/* ---------------- Avatar ---------------- */
// The account's face across the app (nav rail, settings, mobile tab bar).
// Three forms: an uploaded photo (data: URL), one of five brand presets
// ("av1".."av5"), or a fallback initial from the user's name.
const AVATAR_PRESETS = [
  { id: "av1", name: "Violet", grad: ["#7a5cff", "#5b3df5"], icon: "sparkle" },
  { id: "av2", name: "Ocean", grad: ["#4f8ff5", "#2a6fdb"], icon: "ship" },
  { id: "av3", name: "Forest", grad: ["#23c08a", "#0b9d6f"], icon: "home" },
  { id: "av4", name: "Sunset", grad: ["#f59e0b", "#dc6803"], icon: "sun" },
  { id: "av5", name: "Rose", grad: ["#f472b6", "#db2777"], icon: "shield" },
];

function Avatar({ avatar, name, size = 40, radius }) {
  const r = radius !== undefined ? radius : Math.round(size * 0.3);
  if (avatar && avatar.startsWith("data:image/")) {
    return <img src={avatar} alt={name || "Profile"} style={{ width: size, height: size, borderRadius: r, objectFit: "cover", display: "block", flexShrink: 0 }} />;
  }
  const preset = AVATAR_PRESETS.find((p) => p.id === avatar);
  if (preset) {
    return (
      <span style={{ display: "grid", placeItems: "center", width: size, height: size, borderRadius: r, background: `linear-gradient(135deg,${preset.grad[0]},${preset.grad[1]})`, color: "#fff", flexShrink: 0 }}>
        <window.Icon name={preset.icon} size={Math.round(size * 0.52)} />
      </span>
    );
  }
  const initial = (name || "?").trim()[0] || "?";
  return (
    <span className="display" style={{ display: "grid", placeItems: "center", width: size, height: size, borderRadius: r, background: "linear-gradient(135deg,var(--accent-2),var(--accent))", color: "#fff", fontWeight: 700, fontSize: Math.round(size * 0.42), flexShrink: 0 }}>
      {initial.toUpperCase()}
    </span>
  );
}


// What Cova can do for a visitor — rotated through the public chat placeholders
// so the empty input itself teaches the product (never static on one example).
const GUEST_PROMPTS = [
  "Add my Land Cruiser worth ₦92M…",
  "Value everything in my shop…",
  "Import my asset list from PDF, Excel or Word…",
  "Am I covered if my generator burns out?",
  "Insure my laptop & phone…",
  "What would insuring my home cost?",
  "I already have a policy, bring it in…",
];
function useRotatingPh(active = true, interval = 3400) {
  const [i, setI] = React.useState(0);
  React.useEffect(() => {
    if (!active) return undefined;
    const t = setInterval(() => setI((x) => (x + 1) % GUEST_PROMPTS.length), interval);
    return () => clearInterval(t);
  }, [active, interval]);
  return GUEST_PROMPTS[i];
}

// Paperclip for the PUBLIC site (hero + page CTAs): picking a file dispatches
// vc-guest-import — the app enters guest mode and imports it into the vault.
function GuestClipBtn({ size = 40, light = false }) {
  const ref = React.useRef(null);
  const pick = (e) => {
    const f = e.target.files && e.target.files[0];
    e.target.value = "";
    if (f) window.dispatchEvent(new CustomEvent("vc-guest-import", { detail: f }));
  };
  return (
    <>
      <input ref={ref} type="file" accept=".pdf,.csv,.txt,.xlsx,.xls,.docx,.doc,application/pdf,text/csv" style={{ display: "none" }} onChange={pick} />
      <button type="button" onClick={() => ref.current && ref.current.click()} title="Import your asset list (PDF, Excel, Word or CSV)"
        style={{ display: "grid", placeItems: "center", width: size, height: size, borderRadius: 999, flexShrink: 0, color: light ? "rgba(15,17,36,.5)" : "var(--text-muted)", transition: "color .2s" }}
        onMouseEnter={(e) => { e.currentTarget.style.color = "var(--accent)"; }} onMouseLeave={(e) => { e.currentTarget.style.color = light ? "rgba(15,17,36,.5)" : "var(--text-muted)"; }}>
        <window.Icon name="clip" size={19} />
      </button>
    </>
  );
}

Object.assign(window, { GUEST_PROMPTS, useRotatingPh, GuestClipBtn });

// Shown in place of a feature the user's plan doesn't include (ops curates which
// tiers get what in /admin/capabilities). Names the lowest plan that unlocks it
// and routes to billing. Keeps the feature discoverable — an upgrade, not a wall.
function UpgradeGate({ title, description, requiredTier, onUpgrade }) {
  const D = window.VC_DATA;
  const plan = (D && D.PLANS || []).find((p) => p.id === requiredTier);
  const planName = plan ? plan.name : "a paid plan";
  const price = plan && plan.price ? "₦" + plan.price.toLocaleString() + "/mo" : null;
  return (
    <div className="vc-page" style={{ padding: "48px 34px 60px", maxWidth: 620, margin: "0 auto", display: "grid", placeItems: "center", minHeight: "60%" }}>
      <div style={{ textAlign: "center", background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 24, padding: "40px 30px", boxShadow: "var(--shadow-sm)", width: "100%" }}>
        <span style={{ display: "inline-grid", placeItems: "center", width: 64, height: 64, borderRadius: 20, background: "var(--accent-soft)", color: "var(--accent)", marginBottom: 18 }}>
          <window.Icon name="lock" size={30} />
        </span>
        <h1 className="display" style={{ fontSize: 26, lineHeight: 1.15 }}>{title}</h1>
        <div style={{ display: "inline-flex", alignItems: "center", gap: 7, marginTop: 12, padding: "5px 13px", borderRadius: 999, background: "var(--accent-soft)", color: "var(--accent-ink)", fontSize: 12.5, fontWeight: 700 }}>
          <window.Icon name="sparkle" size={13} />Included from {planName}{price ? " · " + price : ""}
        </div>
        <p style={{ fontSize: 15, color: "var(--text-muted)", lineHeight: 1.6, margin: "16px auto 0", maxWidth: 420 }}>{description}</p>
        <window.Btn size="lg" iconR="arrowR" style={{ marginTop: 24, justifyContent: "center" }} onClick={onUpgrade}>Upgrade to {planName.replace("VaultCova ", "")}</window.Btn>
      </div>
    </div>
  );
}

Object.assign(window, { Icon, Btn, StatusBadge, AssetThumb, AssetCoverArt, useCountUp, CoverageRing, Overlay, ToastHost, CoverageBar, Avatar, UpgradeGate, VC_AVATAR_PRESETS: AVATAR_PRESETS });
