/* Design-system primitives. All reusable, all opinionated. */

const { useState, useRef, useEffect } = React;

/* ────────────────────────────────────────────────────────────────────────
   openExternalLink — robust outbound-link opener for sandboxed previews.
   ────────────────────────────────────────────────────────────────────────
   The page may run inside a preview iframe whose host frames new tabs
   in its own webview. LinkedIn (and other framing-restricted sites)
   refuse to render in any iframe → ERR_BLOCKED_BY_RESPONSE. As a
   robust fallback we always copy the URL to the clipboard FIRST so
   the user can paste it manually, then try window.open, and surface a
   small toast confirming the copy. */
function openExternalLink(url) {
  if (!url) return;
  /* Clipboard first — most reliable signal that something happened. */
  try { navigator.clipboard?.writeText(url); } catch (_) {}
  /* Best-effort native open. May be intercepted in sandboxes; that's
     fine because clipboard already has the URL. */
  try { window.open(url, "_blank", "noopener,noreferrer"); } catch (_) {}
  /* Toast — tells the user where the URL is if the popup was blocked. */
  if (window.__openLinkToast) window.__openLinkToast(url);
}
window.openExternalLink = openExternalLink;

/* Tiny imperative toast (no React state) — used by openExternalLink. A
   single in-flight node at a time; new toasts replace the previous. */
window.__openLinkToast = function (url) {
  const existing = document.getElementById("__bt-link-toast");
  if (existing) existing.remove();
  const el = document.createElement("div");
  el.id = "__bt-link-toast";
  el.style.cssText = [
    "position:fixed", "top:16px", "left:50%", "transform:translateX(-50%)",
    "z-index:99999",
    "padding:10px 14px", "border-radius:9px",
    "background:var(--surface)",
    "border:1px solid var(--line-strong)",
    "box-shadow:0 8px 24px rgba(0,0,0,0.16)",
    "font-size:12.5px", "color:var(--ink)",
    "display:inline-flex", "align-items:center", "gap:8px",
    "animation:fadein 180ms ease-out",
  ].join(";");
  const truncated = (url || "").length > 60 ? url.slice(0, 60) + "…" : url;
  el.innerHTML =
    '<span style="display:inline-flex;width:18px;height:18px;border-radius:999px;background:color-mix(in oklch,var(--accent) 16%,transparent);color:var(--accent);align-items:center;justify-content:center;">✓</span>' +
    '<span><b style="color:var(--ink)">Link copied</b> — paste in your browser if it doesn’t open</span>' +
    '<span style="font-family:ui-monospace,SFMono-Regular,monospace;font-size:11px;color:var(--ink-4);max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + truncated + '</span>';
  document.body.appendChild(el);
  setTimeout(() => { if (el.parentNode) el.remove(); }, 4500);
};

/* ────────────────────────────────────────────────────────────────────────
   Avatar — initials fallback with deterministic warm color.
   ──────────────────────────────────────────────────────────────────────── */
function Avatar({ name, src, srcs, size = 28, square = false, className = "", title }) {
  /* Either a single `src` or an ordered `srcs` array. The array form
     advances to the next source when an image fails to load, falling
     through to initials when all sources are exhausted. */
  const sources = React.useMemo(() => {
    if (Array.isArray(srcs)) return srcs.filter(Boolean);
    return src ? [src] : [];
  }, [src, Array.isArray(srcs) ? srcs.join("|") : null]);
  const [idx, setIdx] = useState(0);
  /* Reset when the sources change (e.g. company prop changes). */
  React.useEffect(() => { setIdx(0); }, [sources.join("|")]);
  const currentSrc = sources[idx];
  const show = !!currentSrc;
  const bg = window.colorFromString(name || "");
  return (
    <div
      title={title || name}
      className={className}
      style={{
        width: size, height: size,
        borderRadius: square ? Math.max(4, size * 0.18) : "50%",
        background: show ? "var(--inset)" : bg,
        color: "rgba(20,20,20,0.85)",
        fontSize: Math.max(9, size * 0.38),
        fontWeight: 600,
        display: "inline-flex", alignItems: "center", justifyContent: "center",
        flexShrink: 0, overflow: "hidden", letterSpacing: 0,
        boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.06)",
        userSelect: "none",
      }}
    >
      {show
        ? <img
            key={currentSrc}
            src={currentSrc}
            alt=""
            referrerPolicy="no-referrer"
            onError={() => setIdx(i => i + 1)}
            style={{ width: "100%", height: "100%", objectFit: "cover" }}
          />
        : window.initials(name || "")}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Company logo block — square avatar tuned for company brand initial.
   ────────────────────────────────────────────────────────────────────────
   Source chain: backend `logo_url` first (when present + still valid),
   then a domain-icon fallback (DuckDuckGo's free favicon endpoint), then
   initials. The LinkedIn CDN URLs the backend supplies are signed and
   expire — the domain fallback rescues those records. */
function CompanyLogo({ company, size = 32 }) {
  if (!company) return null;
  const domain = company.domain || (company.api && (company.api.domain || (company.api.website || "").replace(/^https?:\/\/(www\.)?/, "").split("/")[0])) || null;
  const srcs = [
    company.logoUrl,
    domain ? `https://icons.duckduckgo.com/ip3/${domain}.ico` : null,
  ].filter(Boolean);
  return <Avatar name={company.name} srcs={srcs} size={size} square title={company.name} />;
}

/* ────────────────────────────────────────────────────────────────────────
   Badge — solid filled tag with tone presets.
   ──────────────────────────────────────────────────────────────────────── */
const TONES = {
  neutral: { bg: "var(--inset)",        fg: "var(--ink-2)",     border: "var(--line)" },
  violet:  { bg: "var(--accent-soft)",  fg: "var(--accent-ink)", border: "color-mix(in oklch, var(--accent) 25%, transparent)" },
  blue:    { bg: "var(--blue-soft)",    fg: "var(--blue-ink)",  border: "color-mix(in oklch, var(--blue) 25%, transparent)" },
  green:   { bg: "var(--green-soft)",   fg: "var(--green-ink)", border: "color-mix(in oklch, var(--green) 25%, transparent)" },
  warm:    { bg: "var(--warm-soft)",    fg: "var(--warm-ink)",  border: "color-mix(in oklch, var(--warm) 30%, transparent)" },
  red:     { bg: "var(--red-soft)",     fg: "var(--red-ink)",   border: "color-mix(in oklch, var(--red) 25%, transparent)" },
};
function Badge({ children, tone = "neutral", dot = false, size = "sm", style = {}, icon, className = "" }) {
  const t = TONES[tone] || TONES.neutral;
  const padY = size === "sm" ? 2 : 3;
  const padX = size === "sm" ? 7 : 9;
  return (
    <span className={className} style={{
      display: "inline-flex", alignItems: "center", gap: 4,
      padding: `${padY}px ${padX}px`,
      background: t.bg, color: t.fg,
      border: `1px solid ${t.border}`,
      borderRadius: 999, fontSize: size === "sm" ? 11 : 12, fontWeight: 500,
      letterSpacing: 0, whiteSpace: "nowrap", lineHeight: 1.2,
      ...style,
    }}>
      {dot && <span style={{ width: 6, height: 6, borderRadius: "50%", background: t.fg }} />}
      {icon && <Icon name={icon} size={12} />}
      {children}
    </span>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Button — primary / secondary / ghost / subtle.
   ──────────────────────────────────────────────────────────────────────── */
function Button({ children, kind = "secondary", size = "md", icon, iconRight, onClick, disabled, style = {}, title, type = "button" }) {
  const PADS = { sm: "4px 8px", md: "6px 12px", lg: "8px 16px" };
  const HEIGHT = { sm: 26, md: 30, lg: 36 };
  const FONT = { sm: 12, md: 13, lg: 14 };
  const styles = {
    primary: {
      background: "var(--ink)", color: "#fff",
      border: "1px solid var(--ink)",
      boxShadow: "inset 0 1px 0 rgba(255,255,255,0.08)",
    },
    accent: {
      background: "var(--accent)", color: "#fff",
      border: "1px solid var(--accent)",
      boxShadow: "inset 0 1px 0 rgba(255,255,255,0.12)",
    },
    secondary: {
      background: "var(--surface)", color: "var(--ink)",
      border: "1px solid var(--line-strong)",
      boxShadow: "var(--shadow-1)",
    },
    ghost: {
      background: "transparent", color: "var(--ink-2)",
      border: "1px solid transparent",
    },
    subtle: {
      background: "var(--inset)", color: "var(--ink-2)",
      border: "1px solid transparent",
    },
    danger: {
      background: "var(--surface)", color: "var(--red-ink)",
      border: "1px solid color-mix(in oklch, var(--red) 30%, transparent)",
    },
  };
  return (
    <button
      type={type}
      onClick={onClick}
      disabled={disabled}
      title={title}
      style={{
        display: "inline-flex", alignItems: "center", gap: 6,
        padding: PADS[size], height: HEIGHT[size], fontSize: FONT[size],
        fontWeight: 500, letterSpacing: "-0.005em",
        borderRadius: 7, cursor: disabled ? "not-allowed" : "pointer",
        opacity: disabled ? 0.5 : 1,
        transition: "background 120ms, border-color 120ms, transform 60ms",
        ...styles[kind], ...style,
      }}
      onMouseDown={(e) => !disabled && (e.currentTarget.style.transform = "translateY(0.5px)")}
      onMouseUp={(e) => (e.currentTarget.style.transform = "")}
      onMouseLeave={(e) => (e.currentTarget.style.transform = "")}
    >
      {icon && <Icon name={icon} size={size === "sm" ? 12 : 14} />}
      {children}
      {iconRight && <Icon name={iconRight} size={size === "sm" ? 12 : 14} />}
    </button>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Card / Surface
   ──────────────────────────────────────────────────────────────────────── */
function Card({ children, style = {}, padding = 16, className = "" }) {
  return (
    <div className={className} style={{
      background: "var(--surface)",
      border: "1px solid var(--line)",
      borderRadius: 12,
      padding,
      boxShadow: "var(--shadow-1)",
      ...style,
    }}>
      {children}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Stat ring — small circular score chart, 0..100.
   ──────────────────────────────────────────────────────────────────────── */
function ScoreRing({ value, size = 36, stroke = 3.5, label, tone = "violet" }) {
  const v = Math.max(0, Math.min(100, value || 0));
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const off = c * (1 - v / 100);
  const color = ({
    violet: "var(--accent)", blue: "var(--blue)",
    green: "var(--green)", warm: "var(--warm)",
    red: "var(--red)", neutral: "var(--ink-3)",
  })[tone] || "var(--accent)";
  return (
    <div style={{ width: size, height: size, position: "relative", flexShrink: 0 }} title={label}>
      <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{ transform: "rotate(-90deg)" }}>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="var(--line)" strokeWidth={stroke} />
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={color} strokeWidth={stroke}
          strokeDasharray={c} strokeDashoffset={off} strokeLinecap="round" />
      </svg>
      <div style={{
        position: "absolute", inset: 0,
        display: "flex", alignItems: "center", justifyContent: "center",
        fontSize: Math.max(9, size * 0.28),
        fontWeight: 600, fontVariantNumeric: "tabular-nums",
        color: "var(--ink)",
      }} className="mono">
        {Math.round(v)}
      </div>
    </div>
  );
}

/* Horizontal score bar */
function ScoreBar({ value, max = 100, label, tone = "violet", showValue = true }) {
  const v = Math.max(0, Math.min(max, value || 0));
  const pct = (v / max) * 100;
  const color = ({
    violet: "var(--accent)", blue: "var(--blue)",
    green: "var(--green)", warm: "var(--warm)",
    red: "var(--red)", neutral: "var(--ink-3)",
  })[tone] || "var(--accent)";
  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr auto", alignItems: "center", gap: 10 }}>
      <div>
        {label && <div style={{ fontSize: 11, color: "var(--ink-3)", marginBottom: 4, letterSpacing: 0.1, textTransform: "uppercase", fontWeight: 500 }}>{label}</div>}
        <div style={{ height: 4, background: "var(--inset)", borderRadius: 4, overflow: "hidden" }}>
          <div style={{ width: `${pct}%`, height: "100%", background: color, borderRadius: 4, transition: "width 300ms" }} />
        </div>
      </div>
      {showValue && <div className="mono num" style={{ fontSize: 12, fontWeight: 600, minWidth: 26, textAlign: "right" }}>{Math.round(v)}</div>}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   IconButton — square borderless action.
   ──────────────────────────────────────────────────────────────────────── */
function IconButton({ icon, onClick, title, active = false, size = 28, iconSize = 14 }) {
  return (
    <button
      onClick={onClick}
      title={title}
      style={{
        width: size, height: size, borderRadius: 6,
        display: "inline-flex", alignItems: "center", justifyContent: "center",
        color: active ? "var(--ink)" : "var(--ink-3)",
        background: active ? "var(--inset)" : "transparent",
        transition: "background 120ms, color 120ms",
      }}
      onMouseEnter={(e) => { e.currentTarget.style.background = "var(--inset)"; e.currentTarget.style.color = "var(--ink)"; }}
      onMouseLeave={(e) => { e.currentTarget.style.background = active ? "var(--inset)" : "transparent"; e.currentTarget.style.color = active ? "var(--ink)" : "var(--ink-3)"; }}
    >
      <Icon name={icon} size={iconSize} />
    </button>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Input
   ──────────────────────────────────────────────────────────────────────── */
function TextField({ icon, placeholder, value, onChange, kbd, style = {}, size = "md" }) {
  const H = { sm: 28, md: 32, lg: 36 };
  const controlled = onChange != null;
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 8,
      height: H[size], padding: "0 10px",
      background: "var(--surface)",
      border: "1px solid var(--line-strong)",
      borderRadius: 7,
      transition: "border-color 120ms, box-shadow 120ms",
      ...style,
    }}>
      {icon && <Icon name={icon} size={14} style={{ color: "var(--ink-3)" }} />}
      <input
        {...(controlled
          ? { value: value || "", onChange }
          : { defaultValue: value || "" })}
        placeholder={placeholder}
        style={{
          flex: 1, border: "none", outline: "none", background: "transparent",
          fontSize: 13, color: "var(--ink)",
        }}
      />
      {kbd && <Kbd>{kbd}</Kbd>}
    </div>
  );
}

function Kbd({ children }) {
  return (
    <kbd className="mono" style={{
      display: "inline-flex", alignItems: "center", justifyContent: "center",
      padding: "1px 5px",
      background: "var(--inset)", color: "var(--ink-3)",
      border: "1px solid var(--line)", borderRadius: 4,
      fontSize: 10.5, fontWeight: 500, lineHeight: 1.4,
    }}>{children}</kbd>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Section header — small caps overline.
   ──────────────────────────────────────────────────────────────────────── */
function SectionTitle({ children, right, style = {} }) {
  return (
    <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 10, ...style }}>
      <div style={{
        fontSize: 11, fontWeight: 600, letterSpacing: "0.06em",
        color: "var(--ink-3)", textTransform: "uppercase",
      }}>{children}</div>
      {right}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   AI chip — branded marker for AI-authored content.
   ──────────────────────────────────────────────────────────────────────── */
function AIChip({ children = "AI", style = {} }) {
  return (
    <span className="mono" style={{
      display: "inline-flex", alignItems: "center", gap: 3,
      padding: "1px 6px", borderRadius: 999,
      background: "var(--accent-soft)", color: "var(--accent-ink)",
      fontSize: 10, fontWeight: 600, letterSpacing: 0.2,
      border: "1px solid color-mix(in oklch, var(--accent) 22%, transparent)",
      ...style,
    }}>
      <Icon name="sparkles" size={9} />
      {children}
    </span>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Page header — for sections (title + meta + actions on right)
   ──────────────────────────────────────────────────────────────────────── */
/* PageHeader — keeps the original API (title, subtitle, kicker, actions, tabs)
   but portals title + subtitle + actions into the sticky Topbar instead of
   rendering its own band. Tabs (if any) render below the topbar as their own
   sticky strip. */
function PageHeader({ title, subtitle, actions, tabs, kicker }) {
  const [titleEl,   setTitleEl]   = useState(null);
  const [actionsEl, setActionsEl] = useState(null);
  useEffect(() => {
    setTitleEl(document.getElementById("__page-title-slot"));
    setActionsEl(document.getElementById("__page-actions-slot"));
  }, []);

  const titleContent = (
    <>
      {kicker && (
        <span style={{
          fontSize: 10.5, fontWeight: 600, color: "var(--ink-3)",
          letterSpacing: "0.05em", textTransform: "uppercase",
          flexShrink: 0,
        }}>{kicker}</span>
      )}
      <h1 style={{
        margin: 0,
        fontSize: "clamp(15px, 1.4vw, 18px)",
        fontWeight: 600, letterSpacing: "-0.015em", color: "var(--ink)",
        overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
        minWidth: 0,
        display: "inline-flex", alignItems: "center", gap: 12,
      }}>{title}</h1>
      {subtitle && (
        <>
          <span style={{ width: 1, height: 16, background: "var(--line)", flexShrink: 0 }} />
          <div style={{
            fontSize: 12.5, color: "var(--ink-3)",
            overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
            minWidth: 0,
          }}>{subtitle}</div>
        </>
      )}
    </>
  );

  return (
    <>
      {titleEl   && ReactDOM.createPortal(titleContent,    titleEl)}
      {actionsEl && ReactDOM.createPortal(<>{actions}</>,  actionsEl)}
      {tabs && (
        <div className="page-pad" style={{
          borderBottom: "1px solid var(--line)",
          background: "var(--bg)",
          flexShrink: 0,
          position: "sticky", top: 0, zIndex: 20,
        }}>
          <div className="ph-tabs" style={{ marginTop: 0, marginBottom: -1 }}>
            {tabs}
          </div>
        </div>
      )}
    </>
  );
}

function Tab({ active, children, onClick, badge }) {
  return (
    <button
      onClick={onClick}
      style={{
        padding: "8px 12px", fontSize: 13, fontWeight: 500,
        color: active ? "var(--ink)" : "var(--ink-3)",
        borderBottom: active ? "1.5px solid var(--ink)" : "1.5px solid transparent",
        marginBottom: -1,
        display: "inline-flex", alignItems: "center", gap: 6,
        transition: "color 120ms, border-color 120ms",
      }}
    >
      {children}
      {badge != null && (
        <span className="mono num" style={{
          minWidth: 18, height: 18, padding: "0 5px",
          background: active ? "var(--ink)" : "var(--inset)",
          color: active ? "#fff" : "var(--ink-3)",
          borderRadius: 9, fontSize: 10, fontWeight: 600,
          display: "inline-flex", alignItems: "center", justifyContent: "center",
        }}>{badge}</span>
      )}
    </button>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Empty state.
   ──────────────────────────────────────────────────────────────────────── */
function EmptyState({ icon = "inbox", title, body, action }) {
  return (
    <div style={{
      display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center",
      padding: "48px 24px", textAlign: "center", color: "var(--ink-3)",
    }}>
      <div style={{
        width: 44, height: 44, borderRadius: 12,
        background: "var(--inset)", color: "var(--ink-3)",
        display: "flex", alignItems: "center", justifyContent: "center",
        marginBottom: 14,
      }}>
        <Icon name={icon} size={20} />
      </div>
      <div style={{ fontSize: 14, fontWeight: 600, color: "var(--ink)" }}>{title}</div>
      {body && <div style={{ fontSize: 13, marginTop: 4, maxWidth: 360 }}>{body}</div>}
      {action && <div style={{ marginTop: 14 }}>{action}</div>}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Channel icon (uses CHANNEL_META map)
   ──────────────────────────────────────────────────────────────────────── */
function ChannelIcon({ channel, size = 14 }) {
  const meta = window.CHANNEL_META[channel];
  if (!meta) return null;
  const colorMap = {
    blue: "var(--blue)", green: "var(--green)", warm: "var(--warm)",
    violet: "var(--accent)", neutral: "var(--ink-3)",
  };
  return <Icon name={meta.icon} size={size} style={{ color: colorMap[meta.color] }} />;
}

/* Status dot — small pulsing presence indicator */
function StatusDot({ tone = "green", pulse = false, size = 8 }) {
  const c = ({ green: "var(--green)", warm: "var(--warm)", red: "var(--red)", neutral: "var(--ink-4)" })[tone];
  return (
    <span style={{
      width: size, height: size, borderRadius: "50%", background: c, display: "inline-block",
      boxShadow: pulse ? `0 0 0 0 ${c}` : "none",
      animation: pulse ? "pulse 2s infinite" : "none",
    }} />
  );
}

/* Lifecycle pill — for company stage */
const LIFECYCLE_TONES = {
  sourced: "neutral", target: "blue", engaged: "violet",
  opportunity: "warm", customer: "green", expansion: "green",
};
function LifecyclePill({ stage }) {
  const label = window.LIFECYCLE_LABEL[stage] || stage;
  return <Badge tone={LIFECYCLE_TONES[stage] || "neutral"} dot>{label}</Badge>;
}

/* tiny meta line */
function MetaLine({ items }) {
  const list = items.filter(Boolean);
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 0, flexWrap: "wrap", fontSize: 12, color: "var(--ink-3)" }}>
      {list.map((m, i) => (
        <React.Fragment key={i}>
          {i > 0 && <span style={{ margin: "0 8px", color: "var(--ink-5)" }}>·</span>}
          <span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>{m}</span>
        </React.Fragment>
      ))}
    </div>
  );
}

/* CSS for keyframes */
const _kfStyle = document.createElement("style");
_kfStyle.textContent = `
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 currentColor; }
  70%  { box-shadow: 0 0 0 6px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@keyframes fadein { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@keyframes modal-in { from { opacity: 0; transform: translateY(-12px) scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes slidein { from { transform: translateX(100%); } to { transform: none; } }
@keyframes toast-in { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: none; } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes shimmer {
  0% { background-position: -400px 0; }
  100% { background-position: 400px 0; }
}
.shimmer {
  background: linear-gradient(90deg, var(--inset) 0%, var(--surface-2) 50%, var(--inset) 100%);
  background-size: 800px 100%;
  animation: shimmer 1.4s linear infinite;
  border-radius: 6px;
}
`;
document.head.appendChild(_kfStyle);

/* Card section header — title + bottom rule + optional right slot */
function SectionHeader({ title, right }) {
  return (
    <div style={{
      padding: "12px 18px",
      borderBottom: "1px solid var(--line)",
      display: "flex", alignItems: "center", justifyContent: "space-between",
      gap: 10,
    }}>
      <div style={{ fontSize: 13, fontWeight: 600, color: "var(--ink)" }}>{title}</div>
      {right}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Modal — portal-rendered dialog with backdrop + Esc to close.
   ──────────────────────────────────────────────────────────────────────── */
function Modal({ open, onClose, title, subtitle, icon, children, footer, width = 480 }) {
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose?.(); };
    document.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = ""; };
  }, [open, onClose]);
  if (!open) return null;
  const node = (
    <div
      onClick={onClose}
      style={{
        position: "fixed", inset: 0, zIndex: 200,
        background: "color-mix(in oklch, oklch(0.2 0 0) 32%, transparent)",
        backdropFilter: "blur(4px)",
        display: "flex", alignItems: "flex-start", justifyContent: "center",
        padding: "min(10vh, 80px) 16px 16px",
        animation: "fadein 160ms ease",
      }}
    >
      <div
        onClick={(e) => e.stopPropagation()}
        style={{
          width: `min(${width}px, 100%)`,
          background: "var(--surface)",
          border: "1px solid var(--line)",
          borderRadius: 14,
          boxShadow: "0 24px 64px oklch(0.2 0 0 / 0.18), 0 6px 18px oklch(0.2 0 0 / 0.10)",
          overflow: "hidden",
          animation: "modal-in 220ms cubic-bezier(0.2, 0.7, 0.2, 1)",
          maxHeight: "calc(100vh - 120px)",
          display: "flex", flexDirection: "column",
        }}
      >
        {(title || icon) && (
          <div style={{
            padding: "14px 18px", borderBottom: "1px solid var(--line)",
            display: "flex", alignItems: "center", gap: 12,
            background: "var(--surface-2)",
          }}>
            {icon && (
              <div style={{
                width: 30, height: 30, borderRadius: 7,
                background: "var(--accent-soft)", color: "var(--accent-ink)",
                display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
              }}><Icon name={icon} size={14} /></div>
            )}
            <div style={{ minWidth: 0, flex: 1 }}>
              <div style={{ fontSize: 14, fontWeight: 600, color: "var(--ink)" }}>{title}</div>
              {subtitle && <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 1 }}>{subtitle}</div>}
            </div>
            <IconButton icon="x" onClick={onClose} title="Close" />
          </div>
        )}
        <div style={{ padding: 18, overflowY: "auto" }}>
          {children}
        </div>
        {footer && (
          <div style={{
            padding: 14, borderTop: "1px solid var(--line)",
            background: "var(--surface-2)",
            display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 8,
          }}>{footer}</div>
        )}
      </div>
    </div>
  );
  return ReactDOM.createPortal(node, document.body);
}

/* ────────────────────────────────────────────────────────────────────────
   Slide-over — portal-rendered right-edge drawer.
   ──────────────────────────────────────────────────────────────────────── */
function SlideOver({ open, onClose, title, subtitle, icon, children, footer, width = 420 }) {
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose?.(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [open, onClose]);
  if (!open) return null;
  const node = (
    <div onClick={onClose} style={{
      position: "fixed", inset: 0, zIndex: 200,
      background: "color-mix(in oklch, oklch(0.2 0 0) 25%, transparent)",
      animation: "fadein 160ms ease",
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        position: "absolute", top: 0, right: 0, bottom: 0,
        width: `min(${width}px, 100%)`,
        background: "var(--surface)",
        borderLeft: "1px solid var(--line)",
        boxShadow: "-12px 0 36px oklch(0.2 0 0 / 0.12)",
        display: "flex", flexDirection: "column",
        animation: "slidein 240ms cubic-bezier(0.2, 0.7, 0.2, 1)",
      }}>
        <div style={{
          padding: "14px 18px", borderBottom: "1px solid var(--line)",
          display: "flex", alignItems: "center", gap: 12,
          background: "var(--surface-2)",
        }}>
          {icon && <Icon name={icon} size={16} style={{ color: "var(--accent)" }} />}
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ fontSize: 14, fontWeight: 600, color: "var(--ink)" }}>{title}</div>
            {subtitle && <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 1 }}>{subtitle}</div>}
          </div>
          <IconButton icon="x" onClick={onClose} title="Close" />
        </div>
        <div style={{ flex: 1, overflowY: "auto" }}>{children}</div>
        {footer && (
          <div style={{
            padding: 14, borderTop: "1px solid var(--line)", background: "var(--surface-2)",
            display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 8,
          }}>{footer}</div>
        )}
      </div>
    </div>
  );
  return ReactDOM.createPortal(node, document.body);
}

/* ────────────────────────────────────────────────────────────────────────
   Toaster — global. Use window.toast({title, body, tone, icon, duration})
   ──────────────────────────────────────────────────────────────────────── */
function Toaster() {
  const [toasts, setToasts] = React.useState([]);
  React.useEffect(() => {
    window.toast = (opts) => {
      const id = Math.random().toString(36).slice(2);
      const t = { id, tone: "neutral", duration: 3200, ...opts };
      setToasts(prev => [...prev, t]);
      setTimeout(() => setToasts(prev => prev.filter(x => x.id !== id)), t.duration);
    };
    return () => { delete window.toast; };
  }, []);

  const toneCfg = {
    neutral: { icon: "circle",       color: "var(--ink-3)" },
    success: { icon: "circle-check", color: "var(--green)" },
    warn:    { icon: "alert",        color: "var(--warm)" },
    error:   { icon: "alert-circle", color: "var(--red)" },
    ai:      { icon: "sparkles",     color: "var(--accent)" },
  };
  return ReactDOM.createPortal(
    <div style={{
      position: "fixed", bottom: 88, right: 22,
      display: "flex", flexDirection: "column", gap: 8,
      zIndex: 250,
      pointerEvents: "none",
    }}>
      {toasts.map(t => {
        const cfg = toneCfg[t.tone] || toneCfg.neutral;
        return (
          <div key={t.id} style={{
            background: "var(--surface)",
            border: "1px solid var(--line-strong)",
            borderRadius: 10,
            boxShadow: "0 16px 40px oklch(0.2 0 0 / 0.14), 0 2px 6px oklch(0.2 0 0 / 0.06)",
            padding: "12px 14px",
            minWidth: 280, maxWidth: 360,
            display: "flex", alignItems: "flex-start", gap: 10,
            pointerEvents: "auto",
            animation: "toast-in 200ms cubic-bezier(0.2, 0.7, 0.2, 1)",
          }}>
            <Icon name={t.icon || cfg.icon} size={15} style={{ color: cfg.color, marginTop: 1, flexShrink: 0 }} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13, fontWeight: 600, color: "var(--ink)" }}>{t.title}</div>
              {t.body && <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 1, lineHeight: 1.45 }}>{t.body}</div>}
            </div>
          </div>
        );
      })}
    </div>,
    document.body
  );
}

/* ────────────────────────────────────────────────────────────────────────
   Menu — small popover anchored to children with items list.
   ──────────────────────────────────────────────────────────────────────── */
function Menu({ trigger, items, align = "start", width = 200 }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const onDown = (e) => { if (!ref.current?.contains(e.target)) setOpen(false); };
    const onKey  = (e) => { if (e.key === "Escape") setOpen(false); };
    document.addEventListener("mousedown", onDown);
    document.addEventListener("keydown", onKey);
    return () => { document.removeEventListener("mousedown", onDown); document.removeEventListener("keydown", onKey); };
  }, [open]);
  return (
    <span ref={ref} style={{ position: "relative", display: "inline-flex" }}>
      <span onClick={() => setOpen(o => !o)}>{trigger}</span>
      {open && (
        <div style={{
          position: "absolute", top: "calc(100% + 6px)", [align === "end" ? "right" : "left"]: 0,
          minWidth: width, zIndex: 60,
          background: "var(--surface)",
          border: "1px solid var(--line-strong)",
          borderRadius: 8,
          boxShadow: "0 12px 32px oklch(0.2 0 0 / 0.14), 0 2px 6px oklch(0.2 0 0 / 0.06)",
          padding: 4, animation: "fadein 140ms ease",
        }}>
          {items.map((it, i) => {
            if (it === "---") return <div key={i} style={{ height: 1, background: "var(--line)", margin: "4px 0" }} />;
            if (it.heading) return <div key={i} style={{ padding: "8px 10px 4px", fontSize: 10.5, fontWeight: 600, color: "var(--ink-3)", textTransform: "uppercase", letterSpacing: "0.05em" }}>{it.heading}</div>;
            return (
              <button key={i} onClick={() => { it.onClick?.(); setOpen(false); }}
                style={{
                  display: "flex", alignItems: "center", gap: 8,
                  padding: "7px 10px", width: "100%", textAlign: "left",
                  borderRadius: 5, fontSize: 12.5,
                  color: it.tone === "danger" ? "var(--red-ink)" : "var(--ink)",
                  transition: "background 120ms",
                }}
                onMouseEnter={(e) => e.currentTarget.style.background = "var(--surface-2)"}
                onMouseLeave={(e) => e.currentTarget.style.background = "transparent"}
              >
                {it.icon && <Icon name={it.icon} size={12} style={{ color: it.tone === "danger" ? "var(--red)" : "var(--ink-3)" }} />}
                <span style={{ flex: 1 }}>{it.label}</span>
                {it.kbd && <Kbd>{it.kbd}</Kbd>}
                {it.active && <Icon name="check" size={11} stroke={2.4} />}
              </button>
            );
          })}
        </div>
      )}
    </span>
  );
}

/* Spinner */
function Spinner({ size = 14, color = "currentColor" }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" style={{ animation: "spin 800ms linear infinite", flexShrink: 0 }}>
      <circle cx="12" cy="12" r="9" fill="none" stroke={color} strokeOpacity={0.18} strokeWidth={2.6} />
      <path d="M21 12a9 9 0 0 0-9-9" fill="none" stroke={color} strokeWidth={2.6} strokeLinecap="round" />
    </svg>
  );
}

/* Form field — label + input/textarea/select. Used in modals. */
function FormField({ label, hint, children, optional, span = 1 }) {
  return (
    <div style={{ gridColumn: `span ${span}` }}>
      <div style={{ display: "flex", alignItems: "baseline", gap: 6, marginBottom: 5 }}>
        <span style={{ fontSize: 11, fontWeight: 600, color: "var(--ink-2)", textTransform: "uppercase", letterSpacing: "0.04em" }}>{label}</span>
        {optional && <span style={{ fontSize: 10.5, color: "var(--ink-4)", fontWeight: 500 }}>Optional</span>}
      </div>
      {children}
      {hint && <div style={{ fontSize: 11, color: "var(--ink-3)", marginTop: 4 }}>{hint}</div>}
    </div>
  );
}
function FormInput(props) {
  return <input {...props} style={{
    width: "100%", padding: "8px 10px", fontSize: 13,
    background: "var(--surface)", border: "1px solid var(--line-strong)", borderRadius: 7,
    color: "var(--ink)", outline: "none",
    ...props.style,
  }} />;
}
function FormTextarea(props) {
  return <textarea {...props} style={{
    width: "100%", padding: 10, fontSize: 13, lineHeight: 1.5,
    minHeight: 80, resize: "vertical", fontFamily: "inherit",
    background: "var(--surface)", border: "1px solid var(--line-strong)", borderRadius: 7,
    color: "var(--ink)", outline: "none",
    ...props.style,
  }} />;
}
function FormSelect({ value, onChange, options }) {
  return (
    <select value={value} onChange={onChange} style={{
      width: "100%", padding: "8px 10px", fontSize: 13,
      background: "var(--surface)", border: "1px solid var(--line-strong)", borderRadius: 7,
      color: "var(--ink)", outline: "none", appearance: "none",
      backgroundImage: "url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2' stroke-linecap='round'><path d='M6 9l6 6 6-6'/></svg>\")",
      backgroundRepeat: "no-repeat",
      backgroundPosition: "right 8px center",
      paddingRight: 28,
    }}>
      {options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
    </select>
  );
}

Object.assign(window, {
  Avatar, CompanyLogo, Badge, Button, Card, ScoreRing, ScoreBar, IconButton,
  TextField, Kbd, SectionTitle, SectionHeader, AIChip, PageHeader, Tab, EmptyState,
  ChannelIcon, StatusDot, LifecyclePill, MetaLine,
  Modal, SlideOver, Toaster, Menu, Spinner,
  FormField, FormInput, FormTextarea, FormSelect,
});
