/* Stratum Leads — Operations Console. Shared chrome: icon helper, formatters,
   sidebar, topbar, toasts, drawer shell, and status/tone maps used by every
   ops-*.jsx page. Exported on window.OpsShared so each Babel
   script (its own scope) can read it. */

/* Design-system components — each Babel script has its own scope, so every
   ops-*.jsx file must destructure what it needs from the namespace. */
const { NavItem, NavGroup, IconButton, WalletPill, Tabs, LeadCard, Button, Field, Card, Metric, StatusChip, TierBadge, ContactQuality, ProgressBar, StrataAmount, Checkbox, Switch, Avatar, AvatarStack, SegmentedControl, GateCard, EmptyState, Toast, Logo } = window.StratumLeadsDesignSystem_6367ed;

const SOD = window.STRATUM_OPS;
/* Wrap each icon in a React-owned <span> so lucide's DOM swap can never crash a
   re-render (matches the Product app's safer helper). Removing the inner <i>
   that lucide replaces is always safe because React still owns the span. */
const I = (n, style) => <span data-i="" style={{ display: "inline-flex", flexShrink: 0, lineHeight: 0, verticalAlign: "middle" }}><i data-lucide={n} style={style}></i></span>;

function refresh() {requestAnimationFrame(() => window.lucide && window.lucide.createIcons({ attrs: { "stroke-width": 1.9 } }));}

/* Auto-convert any newly-rendered <i data-lucide> icons so nothing appears
   "faceless" after a re-render inside a popover, drawer, or tab panel. */
(function () {
  if (typeof MutationObserver === "undefined" || typeof document === "undefined") return;
  let scheduled = false;
  const obs = new MutationObserver(() => {
    if (scheduled) return;
    scheduled = true;
    requestAnimationFrame(() => {
      scheduled = false;
      if (window.lucide && document.querySelector("[data-lucide]")) window.lucide.createIcons({ attrs: { "stroke-width": 1.9 } });
    });
  });
  const start = () => {if (document.body) obs.observe(document.body, { childList: true, subtree: true });};
  if (document.body) start();else document.addEventListener("DOMContentLoaded", start);
})();

const fmtStrata = (n) => Number(n || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
function fmtSigned(n) {
  const num = Number(n) || 0;
  return (num < 0 ? "\u2212" : "+") + fmtStrata(Math.abs(num));
}
function fmtDur(mins) {
  const m = Math.round(Math.abs(mins));
  const h = Math.floor(m / 60);
  const r = m % 60;
  return h > 0 ? `${h}h ${r}m` : `${r}m`;
}
function getCustomer(id) {
  return (SOD.CUSTOMERS || []).find((c) => c.id === id) || null;
}

/* ---------------- Status / tone vocabulary ---------------- */
const TICKET_STATUS_TONE = { New: "info", Open: "violet", Pending: "warning", Escalated: "danger", Closed: "neutral" };
const PRIORITY_TONE = { Low: "neutral", Medium: "info", High: "warning", Urgent: "danger" };
const HEALTH_TONE = { verified: "success", review: "warning", past_due: "danger", new: "info" };
const HEALTH_LABEL = { verified: "Verified", review: "Under review", past_due: "Payment failed", new: "New account" };
const LEADQ_TONE = { pending: "warning", verified: "success", rejected: "danger", published: "violet" };
const VAULT_TONE = { delivered: "success", pending: "warning" };
const CATEGORY_LABEL = { billing: "Billing", wallet: "Wallet", auction: "Auction", lead_access: "Lead access", account: "Account", technical: "Technical" };

const AGENT_STATUS_DOT = { Available: "var(--success)", Away: "var(--text-subtle)", Busy: "var(--danger)" };

function SlaBadge({ minutes }) {
  if (minutes === null || minutes === undefined) return <StatusChip tone="neutral">{"\u2014"}</StatusChip>;
  const breached = minutes < 0;
  return (
    <StatusChip tone={breached ? "danger" : minutes < 120 ? "warning" : "neutral"} dot={breached}>
      {breached ? `Breached ${fmtDur(minutes)} ago` : `${fmtDur(minutes)} left`}
    </StatusChip>);

}

/* ---------------- Spinner + small inline-SVG icons ----------------
   These are hand-drawn SVG (never lucide's <i data-lucide>) specifically
   because they sit in positions that swap identity across re-renders (busy
   state, sort direction, reply vs note). Lucide's auto-replace mutates the
   DOM outside React's tracking; swapping/removing a lucide-replaced node
   crashes React's reconciler. Pure-SVG elements React fully owns don't. */
function Spinner({ size = 14, style }) {
  return (
    <svg viewBox="0 0 24 24" width={size} height={size} style={{ animation: "sop-spin 0.8s linear infinite", flexShrink: 0, ...style }}>
      <circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeDasharray="40" strokeDashoffset="14" opacity="0.85" />
    </svg>);

}
function IconRefreshCw({ size = 14, style }) {
  return (
    <svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={style}>
      <path d="M21 12a9 9 0 1 1-2.64-6.36" />
      <polyline points="21 3 21 9 15 9" />
    </svg>);

}
function IconCheckCircle({ size = 15, style }) {
  return (
    <svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={style}>
      <circle cx="12" cy="12" r="10" />
      <polyline points="8 12.5 11 15.5 16 9" />
    </svg>);

}
function IconRotateCcw({ size = 14, style }) {
  return (
    <svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={style}>
      <path d="M3 12a9 9 0 1 0 2.64-6.36" />
      <polyline points="3 3 3 9 9 9" />
    </svg>);

}
function IconReply({ size = 15, style }) {
  return (
    <svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={style}>
      <polyline points="9 17 4 12 9 7" />
      <path d="M20 18v-2a4 4 0 0 0-4-4H4" />
    </svg>);

}
function IconNotePen({ size = 15, style }) {
  return (
    <svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={style}>
      <path d="M12 20h9" />
      <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4Z" />
    </svg>);

}
function IconTrendArrow({ dir = "up", size = 12, style }) {
  return (
    <svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" style={style}>
      {dir === "up" ?
      <React.Fragment><line x1="12" y1="19" x2="12" y2="5" /><polyline points="5 12 12 5 19 12" /></React.Fragment> :

      <React.Fragment><line x1="12" y1="5" x2="12" y2="19" /><polyline points="19 12 12 19 5 12" /></React.Fragment>
      }
    </svg>);

}

/* ---------------- Toasts ---------------- */
function ToastStack({ toasts, onDismiss }) {
  if (!toasts || toasts.length === 0) return null;
  return (
    <div style={{ position: "fixed", bottom: 20, right: 20, zIndex: 300, display: "grid", gap: 10, width: "min(360px, calc(100vw - 40px))" }}>
      {toasts.map((t) =>
      <Toast key={t.id} title={t.title} message={t.message} tone={t.tone} onClose={() => onDismiss(t.id)} />
      )}
    </div>);

}

/* ---------------- Profile drawer ---------------- */
function ProfileDrawer({ profile, onClose, onSave }) {
  const [name, setName] = React.useState(profile.name);
  const [role, setRole] = React.useState(profile.role);
  const [email, setEmail] = React.useState(profile.email || "");
  const [phone, setPhone] = React.useState(profile.phone || "");
  const [status, setStatus] = React.useState(profile.status || "Available");
  React.useEffect(refresh);

  const initials = (name.trim().split(/\s+/).filter(Boolean).map((w) => w[0]).slice(0, 2).join("") || "?").toUpperCase();

  return (
    <SideDrawer title="My Profile" subtitle="Visible to teammates across the Stratum Leads Operations Console." icon={I("user-round")} onClose={onClose} footer={
    <React.Fragment>
        <Button variant="secondary" onClick={onClose}>Cancel</Button>
        <Button variant="primary" icon={I("check", { width: 15 })} disabled={!name.trim()} onClick={() => onSave({ name: name.trim(), role: role.trim(), email: email.trim(), phone: phone.trim(), status, initials })}>Save Profile</Button>
      </React.Fragment>
    }>
      <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
        <Avatar name={name} initials={initials} size={56} style={{ fontWeight: 800, fontSize: 19 }} />
        <div style={{ minWidth: 0 }}>
          <strong style={{ display: "block", fontSize: 15, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{name || "Unnamed"}</strong>
          <span style={{ fontSize: 12.5, color: "var(--text-muted)" }}>{role}</span>
        </div>
      </div>
      <Field label="Full name" value={name} onChange={(e) => setName(e.target.value)} />
      <Field label="Role / title" value={role} onChange={(e) => setRole(e.target.value)} />
      <Field label="Email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
      <Field label="Phone" value={phone} onChange={(e) => setPhone(e.target.value)} />
      <div>
        <span style={{ display: "block", fontSize: 12.5, fontWeight: 700, marginBottom: 6 }}>Status</span>
        <SegmentedControl value={status} onChange={setStatus} options={[{ value: "Available", label: "Available" }, { value: "Away", label: "Away" }, { value: "Busy", label: "Busy" }]} />
      </div>
    </SideDrawer>);

}

/* ---------------- Generic right-side drawer ---------------- */
function SideDrawer({ title, subtitle, icon, width = 520, onClose, children, footer }) {
  React.useEffect(refresh);
  return (
    <React.Fragment>
      <div onClick={onClose} style={{ position: "fixed", inset: 0, zIndex: 60, background: "rgba(16,22,38,0.42)" }} />
      <aside style={{ position: "fixed", top: 0, right: 0, zIndex: 61, width: `min(${width}px,100vw)`, height: "100vh", background: "var(--surface)", borderLeft: "1px solid var(--border)", boxShadow: "var(--shadow-lg)", display: "flex", flexDirection: "column" }}>
        <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 12, padding: "18px 20px", borderBottom: "1px solid var(--border)" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, minWidth: 0 }}>
            {icon && <span style={{ flexShrink: 0, color: "var(--accent)" }}>{icon}</span>}
            <div style={{ minWidth: 0 }}>
              <h2 style={{ margin: 0, fontSize: 18, fontWeight: 800 }}>{title}</h2>
              {subtitle && <p style={{ margin: "3px 0 0", fontSize: 13, color: "var(--text-muted)" }}>{subtitle}</p>}
            </div>
          </div>
          <IconButton label="Close" variant="ghost" onClick={onClose}>{I("x")}</IconButton>
        </div>
        <div style={{ flex: 1, overflowY: "auto", padding: 20, display: "grid", gap: 18, alignContent: "start" }}>{children}</div>
        {footer &&
        <div style={{ marginTop: "auto", borderTop: "1px solid var(--border)", padding: "14px 20px", display: "flex", justifyContent: "flex-end", gap: 10, background: "var(--surface-subtle)" }}>
            {footer}
          </div>
        }
      </aside>
    </React.Fragment>);

}

/* ---------------- Page header ---------------- */
function PageHeader({ eyebrow, title, description, actions }) {
  return (
    <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 16, flexWrap: "wrap", marginBottom: 20 }}>
      <div>
        <p className="eyebrow">{eyebrow}</p>
        <h1 style={{ margin: 0, fontSize: "var(--title)", fontWeight: 800 }}>{title}</h1>
        {description && <p style={{ margin: "6px 0 0", fontSize: 14, color: "var(--text-muted)", maxWidth: 640 }}>{description}</p>}
      </div>
      {actions && <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>{actions}</div>}
    </div>);

}

/* ---------------- Sidebar ----------------
   Information architecture = operation families (see
   docs/OPERATIONS_CONSOLE_UI_PLAN.md). Home is standalone; every other section
   lives under a family NavGroup so new Lead-Operations surfaces (Intelligence,
   Enrichment, Playbooks, Publishing) drop into their family without reshaping
   the nav. `route` ids are unchanged from the flat version — no behavior change.
   Leaves whose surface is not built yet carry `soon:true` and render disabled. */
const NAV_FAMILIES = [
  { kind: "item", id: "home", icon: "layout-dashboard", label: "Home" },
  { kind: "group", key: "lead", label: "Lead Operations", items: [
    { id: "intel", icon: "radar", label: "Lead Intelligence" },
    { id: "enrich", icon: "sparkles", label: "Lead Enrichment" },
    { id: "playbooks", icon: "book-open", label: "Source Playbooks" },
    { id: "leadops", icon: "list-checks", label: "Lead Review" },
    { id: "publishing", icon: "upload-cloud", label: "Publishing Queue" }] },
  { kind: "group", key: "support", label: "Support", items: [
    { id: "inbox", icon: "headphones", label: "Inbox" }] },
  { kind: "group", key: "customer", label: "Customer Operations", items: [
    { id: "customers", icon: "building-2", label: "Customers" }] },
  { kind: "group", key: "finance", label: "Financial Operations", items: [
    { id: "walletops", icon: "wallet-cards", label: "Wallet & Billing" }] },
  { kind: "group", key: "auctions", label: "Auctions", items: [
    { id: "auctions", icon: "gavel", label: "Auction Oversight" }] },
  { kind: "group", key: "governance", label: "Governance", items: [
    { id: "gatereview", icon: "shield-check", label: "Gate Review" },
    { id: "auditlog", icon: "history", label: "Audit Log" }] },
  { kind: "group", key: "it", label: "IT Operations", items: [
    { id: "systemhealth", icon: "activity", label: "System Health" }] }];

function NavFamily({ node, route, setRoute, collapsed, toggle, badgeFor }) {
  const hasActive = node.items.some((it) => it.id === route);
  const open = hasActive || !collapsed[node.key];
  return (
    <NavGroup label={node.label} active={hasActive} open={open} onToggle={() => toggle(node.key)}>
      {node.items.map((it) =>
      it.soon ?
      <NavItem key={it.id} nested icon={I(it.icon, { width: 16 })} disabled title="Coming in Phase 1" style={{ opacity: 0.5, cursor: "default" }} badge={"Soon"}>{it.label}</NavItem> :

      <NavItem key={it.id} nested icon={I(it.icon, { width: 16 })} active={route === it.id} badge={badgeFor(it.id)} onClick={() => setRoute(it.id)}>{it.label}</NavItem>
      )}
    </NavGroup>);

}

function OpsSidebar({ route, setRoute, counts, profile }) {
  const [collapsed, setCollapsed] = React.useState({});
  const toggle = (k) => setCollapsed((c) => ({ ...c, [k]: !c[k] }));

  const badgeFor = (id) => {
    if (id === "inbox") return counts.openTickets || undefined;
    if (id === "leadops") return counts.pendingLeads || undefined;
    if (id === "auctions") return counts.liveAuctions || undefined;
    return undefined;
  };
  return (
    <React.Fragment>
    <style>{"@keyframes sop-spin { to { transform: rotate(360deg); } }"}</style>
    <aside style={{ position: "sticky", top: 0, height: "100vh", overflowY: "auto", flex: "0 0 var(--sidebar-w)", width: "var(--sidebar-w)", borderRight: "1px solid var(--border)", background: "var(--surface)", padding: "18px 16px", display: "flex", flexDirection: "column", gap: 4 }}>
      <div style={{ padding: "4px 8px 2px" }}>
        <Logo variant="stack" size={20} style={{ alignItems: "flex-start" }} />
      </div>
      <div style={{ padding: "0 8px 14px" }}><span className="eyebrow" style={{ margin: 0 }}>Operations Console</span></div>
      {NAV_FAMILIES.map((node) =>
      node.kind === "item" ?
      <NavItem key={node.id} icon={I(node.icon)} active={route === node.id} badge={badgeFor(node.id)} onClick={() => setRoute(node.id)}>{node.label}</NavItem> :

      <NavFamily key={node.key} node={node} route={route} setRoute={setRoute} collapsed={collapsed} toggle={toggle} badgeFor={badgeFor} />
      )}
      <div style={{ marginTop: "auto", paddingTop: 12 }}>
        <div style={{ border: "1px solid var(--border)", borderRadius: "var(--radius-md)", background: "var(--surface-subtle)", padding: 14, display: "grid", gap: 10 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <Avatar name={profile.name} initials={profile.initials} size={34} style={{ fontWeight: 800, fontSize: 13 }} />
            <div style={{ minWidth: 0 }}>
              <strong style={{ fontSize: 13, display: "block", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{profile.name}</strong>
              <span style={{ fontSize: 11.5, color: "var(--text-muted)" }}>{profile.role}</span>
            </div>
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 11.5, color: AGENT_STATUS_DOT[profile.status] || "var(--success)", fontWeight: 600 }}>
            <span style={{ width: 7, height: 7, borderRadius: "50%", background: AGENT_STATUS_DOT[profile.status] || "var(--success)" }} />{profile.status || "Available"}
          </div>
        </div>
      </div>
    </aside>
    </React.Fragment>);

}

/* ---------------- Topbar ---------------- */
function OpsTopbar({ onOpenTicket, onOpenCustomer, onOpenLead, onNewTicket, tickets, customers, leadQueue, ledger, profile, onOpenProfile, localities, locality, onLocalityChange }) {
  const [query, setQuery] = React.useState("");
  const [open, setOpen] = React.useState(false);
  const [alertsOpen, setAlertsOpen] = React.useState(false);
  React.useEffect(refresh);

  const q = query.trim().toLowerCase();
  const matches = q.length > 0 ? [
  ...tickets.filter((t) => t.subject.toLowerCase().includes(q) || t.id.toLowerCase().includes(q)).slice(0, 4).map((t) => ({ kind: "ticket", id: t.id, label: t.subject, sub: `${t.id} · ${getCustomer(t.customerId)?.name || ""}` })),
  ...customers.filter((c) => c.name.toLowerCase().includes(q) || c.id.toLowerCase().includes(q)).slice(0, 4).map((c) => ({ kind: "customer", id: c.id, label: c.name, sub: c.id }))].
  slice(0, 8) : [];

  const breached = tickets.filter((t) => t.slaMinutesLeft !== null && t.slaMinutesLeft !== undefined && t.slaMinutesLeft < 0);
  const failedPayments = ledger.filter((l) => l.status === "failed");
  const pendingLeads = leadQueue.filter((l) => l.status === "pending");
  const alertItems = [
  ...breached.map((t) => ({ icon: "alarm-clock", text: `SLA breached — ${t.subject}`, go: () => onOpenTicket(t.id) })),
  ...failedPayments.map((l) => ({ icon: "credit-card", text: `Failed payment — ${getCustomer(l.customerId)?.name || l.customerId}`, go: () => onOpenCustomer(l.customerId) })),
  ...pendingLeads.slice(0, 3).map((l) => ({ icon: "radar", text: `Lead awaiting review — ${l.address}`, go: () => onOpenLead(l.id) }))];


  const select = (m) => {
    if (m.kind === "ticket") onOpenTicket(m.id);else onOpenCustomer(m.id);
    setQuery("");setOpen(false);
  };

  return (
    <header style={{ position: "sticky", top: 0, zIndex: 25, minHeight: "var(--topbar-h)", display: "grid", gridTemplateColumns: "minmax(220px,1fr) auto", gap: 16, alignItems: "center", padding: "12px 24px", borderBottom: "1px solid var(--border)", background: "color-mix(in srgb, var(--bg) 86%, transparent)", backdropFilter: "blur(12px)" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, minWidth: 0 }}>
        {localities && localities.length > 0 &&
        <div style={{ display: "flex", alignItems: "center", gap: 6, flexShrink: 0, height: 38, padding: "0 10px", border: "1px solid var(--border)", borderRadius: "var(--radius-sm)", background: "var(--surface)" }} title="Locality — scopes leads / intel (one locality for now)">
            {I("map-pin", { width: 14, color: "var(--text-subtle)" })}
            <select value={locality || ""} onChange={(e) => onLocalityChange && onLocalityChange(e.target.value)} style={{ border: "none", background: "transparent", color: "var(--text)", fontSize: 12.5, fontWeight: 600, cursor: "pointer", fontFamily: "var(--font-sans)", outline: "none", maxWidth: 170 }}>
              {localities.map((l) => <option key={l.id} value={l.id}>{l.display_name}</option>)}
            </select>
          </div>
        }
        <div style={{ maxWidth: 480, position: "relative", flex: 1, minWidth: 0 }}>
          <Field icon={I("search")} placeholder="Search tickets, customers, leads..." value={query} onChange={(e) => {setQuery(e.target.value);setOpen(true);}} onFocus={() => setOpen(true)} onBlur={() => setTimeout(() => setOpen(false), 150)} />
          {open && matches.length > 0 &&
          <div style={{ position: "absolute", top: "calc(100% + 4px)", left: 0, right: 0, background: "var(--surface)", border: "1px solid var(--border)", borderRadius: "var(--radius-sm)", boxShadow: "var(--shadow-md)", zIndex: 30, maxHeight: 280, overflowY: "auto" }}>
              {matches.map((m, i) =>
            <button key={i} onMouseDown={() => select(m)} style={{ display: "block", width: "100%", textAlign: "left", padding: "10px 12px", border: "none", borderBottom: i < matches.length - 1 ? "1px solid var(--border-subtle)" : "none", background: "transparent", cursor: "pointer" }}>
                  <span style={{ display: "flex", alignItems: "center", gap: 8 }}>
                    {I(m.kind === "ticket" ? "ticket" : "building-2", { width: 13, color: "var(--text-subtle)" })}
                    <strong style={{ fontSize: 13 }}>{m.label}</strong>
                  </span>
                  <span style={{ fontSize: 11, color: "var(--text-muted)", paddingLeft: 21 }}>{m.sub}</span>
                </button>
            )}
            </div>
          }
        </div>
      </div>
      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
        {onNewTicket && <Button variant="secondary" size="sm" icon={I("ticket-plus", { width: 15 })} onClick={onNewTicket}>New Ticket</Button>}
        <div style={{ position: "relative" }}>
          <IconButton label="Alerts" count={alertItems.length || null} onClick={() => setAlertsOpen((o) => !o)}>{I("bell")}</IconButton>
          {alertsOpen &&
          <React.Fragment>
              <div onClick={() => setAlertsOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 29 }} />
              <div style={{ position: "absolute", top: "calc(100% + 8px)", right: 0, width: 340, maxHeight: 360, overflowY: "auto", background: "var(--surface)", border: "1px solid var(--border)", borderRadius: "var(--radius-md)", boxShadow: "var(--shadow-lg)", zIndex: 30 }}>
                <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--border)", fontWeight: 700, fontSize: 13 }}>Needs attention</div>
                {alertItems.length === 0 && <div style={{ padding: 20, textAlign: "center", fontSize: 13, color: "var(--text-muted)" }}>Nothing urgent right now</div>}
                {alertItems.map((a, i) =>
              <button key={i} onClick={() => {a.go();setAlertsOpen(false);}} style={{ display: "flex", gap: 10, width: "100%", textAlign: "left", padding: "12px 16px", border: "none", borderBottom: i < alertItems.length - 1 ? "1px solid var(--border-subtle)" : "none", background: "transparent", cursor: "pointer" }}>
                    <span style={{ width: 28, height: 28, flexShrink: 0, display: "grid", placeItems: "center", borderRadius: "50%", background: "var(--danger-soft)", color: "var(--danger)" }}>{I(a.icon, { width: 14 })}</span>
                    <span style={{ fontSize: 13 }}>{a.text}</span>
                  </button>
              )}
              </div>
            </React.Fragment>
          }
        </div>
        <button onClick={onOpenProfile} style={{ padding: 0, border: "none", background: "transparent", cursor: "pointer", borderRadius: "50%", lineHeight: 0 }} title={`${profile.name} — ${profile.role} — view profile`}>
          <Avatar name={profile.name} initials={profile.initials} size={36} status={AGENT_STATUS_DOT[profile.status] || "var(--success)"} style={{ border: "1px solid var(--border)", fontWeight: 800, fontSize: 12.5 }} />
        </button>
      </div>
    </header>);

}

/* ---------------- Reveal (opening intro for charts/tables/panels) ---------------- */
function Reveal({ children, delay = 0, y = 16, once = true, style }) {
  const ref = React.useRef(null);
  const [shown, setShown] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let done = false;
    const reveal = () => { if (done) return; done = true; setShown(true); stop(); };
    const check = () => {
      if (done) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight || document.documentElement.clientHeight;
      if (r.top < vh * 0.92 && r.bottom > 4) reveal();
    };
    const iv = setInterval(check, 180);
    const stop = () => { clearInterval(iv); window.removeEventListener("resize", check); };
    window.addEventListener("resize", check);
    const raf = requestAnimationFrame(() => requestAnimationFrame(check));
    return () => { stop(); cancelAnimationFrame(raf); };
  }, []);
  const content = typeof children === "function" ? children(shown) : children;
  return (
    <div ref={ref} style={{ ...style, opacity: shown ? 1 : 0, transform: shown ? "none" : `translateY(${y}px)`, transition: "opacity 0.5s ease, transform 0.6s cubic-bezier(.22,.7,.2,1)", transitionDelay: shown ? `${delay}ms` : "0ms", willChange: "opacity, transform" }}>
      {content}
    </div>
  );
}

/* ---------------- Pagination (standard for support tables) ---------------- */
function usePaged(items, key, defaultPer) {
  var storeKey = "sop-perpage-" + (key && key.k ? key.k : "default");
  var _p = React.useState(function () { var v = parseInt(typeof localStorage !== "undefined" && localStorage.getItem(storeKey), 10); return v && v > 0 ? v : (defaultPer || 25); });
  var perPage = _p[0], setPerPageRaw = _p[1];
  var setPerPage = function (v) { setPerPageRaw(v); try { localStorage.setItem(storeKey, String(v)); } catch (e) {} };
  var _pg = React.useState(1); var page = _pg[0], setPage = _pg[1];
  var dep = key && key.dep !== undefined ? key.dep : key;
  React.useEffect(function () { setPage(1); }, [dep, perPage]);
  var total = items.length;
  var isAll = perPage === "all" || perPage >= 1e9;
  var per = isAll ? Math.max(1, total) : perPage;
  var pageCount = Math.max(1, Math.ceil(total / per));
  var cur = Math.min(page, pageCount);
  var start = (cur - 1) * per;
  var slice = isAll ? items : items.slice(start, start + per);
  return { slice: slice, page: cur, setPage: setPage, perPage: perPage, setPerPage: setPerPage, pageCount: pageCount, total: total, start: start, isAll: isAll };
}

function Pager(props) {
  var page = props.page, setPage = props.setPage, perPage = props.perPage, setPerPage = props.setPerPage, pageCount = props.pageCount, total = props.total, start = props.start, isAll = props.isAll;
  var noun = props.noun || "items", options = props.options || [10, 25, 50, 100];
  if (total === 0) return null;
  var per = isAll ? total : perPage;
  var end = Math.min(total, start + per);
  var nums = [];
  if (pageCount <= 7) { for (var n = 1; n <= pageCount; n++) nums.push(n); }
  else { nums.push(1); var lo = Math.max(2, page - 1), hi = Math.min(pageCount - 1, page + 1); if (lo > 2) nums.push("\u2026"); for (var k = lo; k <= hi; k++) nums.push(k); if (hi < pageCount - 1) nums.push("\u2026"); nums.push(pageCount); }
  var navBtn = { minWidth: 32, height: 32, padding: "0 8px", borderRadius: "var(--radius-sm)", border: "1px solid var(--border)", background: "var(--surface)", color: "var(--text)", fontSize: 13, fontWeight: 600, cursor: "pointer", fontFamily: "var(--font-sans)" };
  return React.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, flexWrap: "wrap", marginTop: 14, paddingTop: 14, borderTop: "1px solid var(--border-subtle)" } },
    React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, fontSize: 12.5, color: "var(--text-muted)" } },
      React.createElement("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 } }, "Rows per page",
        React.createElement("select", { value: isAll ? "all" : perPage, onChange: function (e) { setPerPage(e.target.value === "all" ? "all" : parseInt(e.target.value, 10)); }, style: { height: 32, padding: "0 8px", borderRadius: "var(--radius-sm)", border: "1px solid var(--border)", background: "var(--surface)", color: "var(--text)", fontSize: 12.5, fontWeight: 600, cursor: "pointer", fontFamily: "var(--font-sans)" } },
          options.map(function (o) { return React.createElement("option", { key: o, value: o }, o); }).concat([React.createElement("option", { key: "all", value: "all" }, "All")]))),
      React.createElement("span", { style: { fontVariantNumeric: "tabular-nums" } }, (start + 1) + "\u2013" + end + " of " + total + " " + noun)),
    pageCount > 1 && React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 4 } },
      React.createElement("button", { "aria-label": "Previous page", disabled: page <= 1, onClick: function () { setPage(Math.max(1, page - 1)); }, style: Object.assign({}, navBtn, { opacity: page <= 1 ? 0.4 : 1, cursor: page <= 1 ? "default" : "pointer", display: "inline-grid", placeItems: "center" }) }, I("chevron-left", { width: 16 })),
      nums.map(function (nn, idx) { return nn === "\u2026" ? React.createElement("span", { key: "e" + idx, style: { minWidth: 20, textAlign: "center", color: "var(--text-subtle)", fontSize: 13 } }, "\u2026") : React.createElement("button", { key: nn, onClick: function () { setPage(nn); }, style: Object.assign({}, navBtn, { background: nn === page ? "var(--accent)" : "var(--surface)", color: nn === page ? "var(--on-accent, #fff)" : "var(--text)", borderColor: nn === page ? "var(--accent)" : "var(--border)" }) }, nn); }),
      React.createElement("button", { "aria-label": "Next page", disabled: page >= pageCount, onClick: function () { setPage(Math.min(pageCount, page + 1)); }, style: Object.assign({}, navBtn, { opacity: page >= pageCount ? 0.4 : 1, cursor: page >= pageCount ? "default" : "pointer", display: "inline-grid", placeItems: "center" }) }, I("chevron-right", { width: 16 }))));
}

Object.assign(window, {
  OpsShared: {
    I, refresh, fmtStrata, fmtSigned, fmtDur, getCustomer,
    TICKET_STATUS_TONE, PRIORITY_TONE, HEALTH_TONE, HEALTH_LABEL, LEADQ_TONE, VAULT_TONE, CATEGORY_LABEL,
    SlaBadge, Spinner, IconRefreshCw, IconCheckCircle, IconRotateCcw, IconReply, IconNotePen, IconTrendArrow, ToastStack, SideDrawer, ProfileDrawer, PageHeader, OpsSidebar, OpsTopbar, Reveal, usePaged, Pager
  }
});
