/* Calibrated Ascension HQ — shared components. Exports to window. */
const { useState, useRef, useEffect } = React;

/* ---------- Icons (simple line set) ---------- */
const I = {
  today: <svg className="icon" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="17" rx="2"/><path d="M3 9h18M8 2v4M16 2v4"/></svg>,
  agents: <svg className="icon" viewBox="0 0 24 24"><circle cx="9" cy="8" r="3"/><path d="M3 20c0-3 2.7-5 6-5s6 2 6 5"/><circle cx="17.5" cy="7" r="2.2"/><path d="M16 14.5c2.5.3 4.5 2.3 4.5 5"/></svg>,
  offers: <svg className="icon" viewBox="0 0 24 24"><path d="M20.6 13.4 13.4 20.6a2 2 0 0 1-2.8 0l-7-7A2 2 0 0 1 3 12.2V5a2 2 0 0 1 2-2h7.2a2 2 0 0 1 1.4.6l7 7a2 2 0 0 1 0 2.8Z"/><circle cx="7.5" cy="7.5" r="1.3"/></svg>,
  auto: <svg className="icon" viewBox="0 0 24 24"><path d="M12 2v3M12 19v3M4.2 4.2l2.1 2.1M17.7 17.7l2.1 2.1M2 12h3M19 12h3M4.2 19.8l2.1-2.1M17.7 6.3l2.1-2.1"/><circle cx="12" cy="12" r="3.2"/></svg>,
  traffic: <svg className="icon" viewBox="0 0 24 24"><path d="M3 17l5-5 4 3 7-8"/><path d="M21 7v5h-5"/></svg>,
  approvals: <svg className="icon" viewBox="0 0 24 24"><path d="M9 11l3 3 8-8"/><path d="M20 12v6a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h9"/></svg>,
  send: <svg className="icon" viewBox="0 0 24 24"><path d="M4 12 20 4l-6 16-3-7-7-1Z"/></svg>,
  bolt: <svg className="icon" viewBox="0 0 24 24"><path d="M13 2 4 14h6l-1 8 9-12h-6l1-8Z"/></svg>,
  warn: <svg className="icon" viewBox="0 0 24 24"><path d="M12 9v4M12 17h.01M10.3 3.9 2.4 18a2 2 0 0 0 1.7 3h15.8a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg>,
  check: <svg className="icon" viewBox="0 0 24 24"><path d="M5 12l4.5 4.5L19 7"/></svg>,
  drive: <svg className="icon" viewBox="0 0 24 24"><path d="M8 3h8l5 8-4 7H7l-4-7Z"/><path d="M8 3l4 7 5-7M3 11h18M9 18l3-7"/></svg>,
  flow: <svg className="icon" viewBox="0 0 24 24"><rect x="3" y="3" width="6" height="6" rx="1.5"/><rect x="15" y="15" width="6" height="6" rx="1.5"/><path d="M9 6h4a2 2 0 0 1 2 2v8"/></svg>,
  play: <svg className="icon" viewBox="0 0 24 24"><path d="M7 5v14l11-7z" fill="currentColor" stroke="none"/></svg>,
  doc: <svg className="icon" viewBox="0 0 24 24"><path d="M14 3v5h5"/><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/></svg>,
  money: <svg className="icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M14.5 9.5C14 8.6 13 8 12 8c-1.4 0-2.5.9-2.5 2s1.1 2 2.5 2 2.5.9 2.5 2-1.1 2-2.5 2c-1 0-2-.6-2.5-1.5M12 6.5v11"/></svg>,
  brain: <svg className="icon" viewBox="0 0 24 24"><path d="M9 4a3 3 0 0 0-3 3 3 3 0 0 0-1 5.8A3 3 0 0 0 8 18a3 3 0 0 0 4 1 3 3 0 0 0 4-1 3 3 0 0 0 3-5.2A3 3 0 0 0 18 7a3 3 0 0 0-3-3 3 3 0 0 0-3 1 3 3 0 0 0-3-1Z"/><path d="M12 5v14"/></svg>,
  mic: <svg className="icon" viewBox="0 0 24 24"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 11a7 7 0 0 0 14 0M12 18v3"/></svg>,
  plus: <svg className="icon" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></svg>,
  chevron: <svg className="icon" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg>,
  plug: <svg className="icon" viewBox="0 0 24 24"><path d="M9 2v6M15 2v6M7 8h10v3a5 5 0 0 1-10 0V8ZM12 16v6"/></svg>,
  mail: <svg className="icon" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></svg>,
  form: <svg className="icon" viewBox="0 0 24 24"><rect x="4" y="3" width="16" height="18" rx="2"/><path d="M8 8h8M8 12h8M8 16h5"/></svg>,
  spark: <svg className="icon" viewBox="0 0 24 24"><path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8L12 3Z"/></svg>,
  x: <svg className="icon" viewBox="0 0 24 24"><path d="M6 6l12 12M18 6 6 18"/></svg>,
  users: <svg className="icon" viewBox="0 0 24 24"><circle cx="9" cy="8" r="3"/><path d="M3 20c0-3 2.7-5 6-5s6 2 6 5"/><circle cx="17.5" cy="7" r="2.2"/><path d="M16 14.5c2.5.3 4.5 2.3 4.5 5"/></svg>,
  bell: <svg className="icon" viewBox="0 0 24 24"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg>,
  gear: <svg className="icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-2.7 1.1V21a2 2 0 0 1-4 0v-.1A1.6 1.6 0 0 0 7 19.4a1.6 1.6 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.6 1.6 0 0 0-1.1-2.7H1a2 2 0 0 1 0-4h.1A1.6 1.6 0 0 0 2.6 7a1.6 1.6 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1A1.6 1.6 0 0 0 7 2.6h.1A1.6 1.6 0 0 0 8 1.1V1a2 2 0 0 1 4 0v.1A1.6 1.6 0 0 0 14.9 2.6a1.6 1.6 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.6 1.6 0 0 0 1.1 2.7h.1a2 2 0 0 1 0 4h-.1a1.6 1.6 0 0 0-1.1 1.4Z"/></svg>,
  book: <svg className="icon" viewBox="0 0 24 24"><path d="M4 5a2 2 0 0 1 2-2h13v16H6a2 2 0 0 0-2 2z"/><path d="M19 17H6a2 2 0 0 0-2 2"/></svg>,
  shield: <svg className="icon" viewBox="0 0 24 24"><path d="M12 3l8 3v5c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10V6z"/><path d="M9 12l2 2 4-4"/></svg>,
  archive: <svg className="icon" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="4" rx="1"/><path d="M5 8v11a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8M10 12h4"/></svg>,
  seat: <svg className="icon" viewBox="0 0 24 24"><circle cx="12" cy="8" r="3.5"/><path d="M5 20c0-3.5 3-6 7-6s7 2.5 7 6"/></svg>,
  sun: <svg className="icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M2 12h2M20 12h2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M19.1 4.9l-1.4 1.4M6.3 17.7l-1.4 1.4"/></svg>,
  moon: <svg className="icon" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z"/></svg>,
  columns: <svg className="icon" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M9 4v16M15 4v16"/></svg>,
  focus: <svg className="icon" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M15 4v16"/></svg>,
  gitbranch: <svg className="icon" viewBox="0 0 24 24"><circle cx="6" cy="6" r="2.4"/><circle cx="6" cy="18" r="2.4"/><circle cx="18" cy="7" r="2.4"/><path d="M6 8.4v7.2M18 9.4c0 4-4 4.6-6 5.6"/></svg>,
  triangle: <svg className="icon" viewBox="0 0 24 24"><path d="M12 4l9 16H3z" fill="currentColor" stroke="none"/></svg>,
  repo: <svg className="icon" viewBox="0 0 24 24"><path d="M6 3h12a1 1 0 0 1 1 1v16l-7-3-7 3V4a1 1 0 0 1 1-1Z"/></svg>,
  rocket: <svg className="icon" viewBox="0 0 24 24"><path d="M5 15c-1.5 1.5-2 5-2 5s3.5-.5 5-2a3 3 0 0 0-3-3Z"/><path d="M9 12a13 13 0 0 1 9-9c1.5 0 3 0 3 0s0 1.5 0 3a13 13 0 0 1-9 9l-3-3Z"/><circle cx="15" cy="9" r="1.4"/></svg>,
  link: <svg className="icon" viewBox="0 0 24 24"><path d="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"/></svg>,
  lock: <svg className="icon" viewBox="0 0 24 24"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V8a4 4 0 0 1 8 0v3"/></svg>,
  globe: <svg className="icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 3 2.5 15 0 18M12 3c-2.5 3-2.5 15 0 18"/></svg>,
  chat: <svg className="icon" viewBox="0 0 24 24"><path d="M21 12a8 8 0 0 1-11.5 7.2L3 21l1.8-6.5A8 8 0 1 1 21 12Z"/></svg>,
  card: <svg className="icon" viewBox="0 0 24 24"><rect x="2" y="5" width="20" height="14" rx="2"/><path d="M2 10h20"/></svg>,
};

function Avatar({ agent, size = "md" }) {
  const palette = {
    atlas: "linear-gradient(150deg, oklch(0.5 0.13 162), oklch(0.42 0.13 162))",
    vera: "linear-gradient(150deg, oklch(0.58 0.12 300), oklch(0.5 0.12 300))",
    margo: "linear-gradient(150deg, oklch(0.62 0.13 25), oklch(0.54 0.13 25))",
    cole: "linear-gradient(150deg, oklch(0.58 0.11 200), oklch(0.5 0.11 200))",
    ravi: "linear-gradient(150deg, oklch(0.58 0.11 245), oklch(0.5 0.11 245))",
    juno: "linear-gradient(150deg, oklch(0.62 0.12 55), oklch(0.55 0.12 50))",
    sage: "linear-gradient(150deg, oklch(0.6 0.1 145), oklch(0.52 0.1 145))",
    quinn: "linear-gradient(150deg, oklch(0.52 0.04 90), oklch(0.44 0.04 90))",
    max: "linear-gradient(150deg, oklch(0.62 0.16 350), oklch(0.54 0.16 350))",
    soham: "linear-gradient(150deg, oklch(0.54 0.08 255), oklch(0.46 0.08 255))",
  };
  return (
    <div className={`avatar ${size}`} style={{ background: palette[agent.id] || "var(--ink-2)" }}>
      {agent.name[0]}
      <span className={`dot ${agent.status}`}></span>
    </div>
  );
}

/* ---------- Sidebar ---------- */
function Sidebar({ data, clients, view, setView, client, setClient, onAgent, counts, cpOpen, setCpOpen, onAddClient, onSeeAll, onEscalations, onSettings, escCount, theme, layout, onTheme, onLayout }) {
  const nav = [
    { id: "today", label: "Command center", ico: I.today, count: counts.decisions },
    { id: "agents", label: "Agent center", ico: I.agents },
    { id: "offers", label: "Offers", ico: I.offers },
    { id: "automation", label: "Automations", ico: I.auto },
    { id: "traffic", label: "Traffic", ico: I.traffic },
    { id: "approvals", label: "Approvals", ico: I.approvals, count: counts.decisions },
  ];
  const agency = clients.find((c) => c.agency);
  const allClients = clients.filter((c) => !c.agency);
  const pinned = allClients.filter((c) => c.pinned).slice(0, 3);
  const clientRow = (c) => (
    <button key={c.id} className="client" data-active={client === c.id} data-dormant={c.dormant ? "1" : null} onClick={() => setClient(c.id)}>
      <span className="client-badge" style={{ background: c.color }}>{c.initials}</span>
      <span style={{ flex: 1, minWidth: 0 }}>
        <span className="client-name">{c.name}</span>
        <span className="client-kind">{c.dormant ? "Dormant" : c.kind}{!c.dormant && !c.connected ? " · connect" : ""}</span>
      </span>
      {!c.agency && <span className={`conn-dot ${c.dormant ? "off" : c.connected ? "on" : "off"}`}></span>}
    </button>
  );
  return (
    <div className="col sidebar">
      <div className="brand">
        <div className="brand-mark">
          <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
            <line x1="12" y1="6.4" x2="6.2" y2="17" stroke="#fff" strokeWidth="1.3" strokeOpacity="0.8" />
            <line x1="12" y1="6.4" x2="17.8" y2="17" stroke="#fff" strokeWidth="1.3" strokeOpacity="0.8" />
            <line x1="6.2" y1="17" x2="17.8" y2="17" stroke="#fff" strokeWidth="1.3" strokeOpacity="0.55" />
            <circle cx="12" cy="6.4" r="2.7" fill="#fff" />
            <circle cx="6.2" cy="17" r="2.1" fill="#fff" fillOpacity="0.9" />
            <circle cx="17.8" cy="17" r="2.1" fill="#fff" fillOpacity="0.9" />
          </svg>
        </div>
        <div>
          <div className="brand-name">Calibrated<br/>Ascension HQ</div>
          <div className="brand-sub">Master brain · v1</div>
        </div>
      </div>

      <div className="sb-util">
        <button className="util-btn" onClick={onEscalations}>{I.bell}<span>Needs you</span>{escCount > 0 && <span className="util-badge">{escCount}</span>}</button>
        <button className="util-btn icon-only" onClick={onSettings} title="Settings">{I.gear}</button>
      </div>

      <div className="sb-toggles">
        <div className="seg chrome" role="group" aria-label="Theme">
          <button className="seg-opt" data-active={theme === "light"} onClick={() => onTheme("light")} title="Light mode">{I.sun}</button>
          <button className="seg-opt" data-active={theme === "dark"} onClick={() => onTheme("dark")} title="Dark mode">{I.moon}</button>
        </div>
        <div className="seg chrome" role="group" aria-label="Layout">
          <button className="seg-opt" data-active={layout === "command"} data-tone="accent" onClick={() => onLayout("command")} title="Command — full workspace">{I.columns}</button>
          <button className="seg-opt" data-active={layout === "focus"} data-tone="accent" onClick={() => onLayout("focus")} title="Focus — hide right rail">{I.focus}</button>
        </div>
      </div>

      <div className="sb-section">
        <div className="sb-label">Your agency</div>
        {agency && clientRow(agency)}
      </div>

      <div className="sb-section">
        <button className="cp-head" data-open={cpOpen} onClick={() => setCpOpen((o) => !o)}>
          <span className="chev">{I.chevron}</span>
          Client profiles
          <span className="cp-n">{allClients.length}</span>
        </button>
        {cpOpen && (
          <React.Fragment>
            {pinned.map(clientRow)}
            <div className="cp-actions">
              <button className="sb-link" onClick={onSeeAll}>{I.users} See all clients</button>
              <button className="sb-link muted" onClick={onAddClient}>{I.plus} Add client</button>
            </div>
          </React.Fragment>
        )}
      </div>

      <div className="sb-section">
        <div className="sb-label">Command</div>
        {nav.map((n) => (
          <button key={n.id} className="nav-item" data-active={view === n.id} onClick={() => setView(n.id)}>
            <span className="nav-ico">{n.ico}</span>
            {n.label}
            {n.count > 0 && <span className="nav-count">{n.count}</span>}
          </button>
        ))}
      </div>
    </div>
  );
}

/* ---------- Per-agent chat ---------- */
const RUN = "__run__"; // sentinel: reply offers to open the live automation
const AGENT_CHAT = {
  atlas: {
    sub: "co-piloting with Sage (CMO)",
    suggest: ["What needs me right now?", "Run the Community Call automation", "What broke overnight?", "Brief me on Nova"],
    replies: [
      { k: ["run", "community", "call", "replay", "automation"], t: RUN },
      { k: ["need", "approv", "priorit"], t: "Three things, in priority: (1) Odin's Nova pricing test — biggest upside, +$14.2k/mo. (2) Iris's community recap. (3) Noah's $400/day ad shift. Want me to open Approvals?" },
      { k: ["broke", "risk", "wrong", "down"], t: "Two flags: Apex's blog publish is blocked on a stale Vercel token (needs your login), and Lumen's calendar webhook misfired overnight — the fallback caught the bookings and Alma has a fix queued for 9am." },
      { k: ["nova", "launch"], t: "Nova's day 2 of 7. $14.2k pipeline, CPL down 18%. The only blocker is your approval on Odin's pricing test — greenlight it and Alma builds it tonight." },
    ],
    fallback: "On it — I'll route this to the right specialist and put it in tomorrow's brief. Want me to draft it now, or just queue it?",
  },
  vera: {
    sub: "Chief Marketing Officer",
    suggest: ["What's the content calendar?", "How are the launches tracking?", "Pitch me a campaign angle", "What should we cut?"],
    replies: [
      { k: ["calendar", "content", "schedule"], t: "Q3 calendar is mapped to the two live launches — Nova and the workshop funnel. ~9 assets/week, weighted to Reels since that's where CPL is winning." },
      { k: ["launch", "track", "going"], t: "Nova is pacing to ~$14.2k; the workshop funnel is warming. I flagged 2 hooks that outperformed — want to see them?" },
      { k: ["angle", "campaign", "idea", "pitch"], t: "Lead with your 'three pillars' framework as a diagnostic — offers, automation, traffic. It positions you as the operator, not just the creator. I'll have Iris script a hook set." },
      { k: ["cut", "stop", "kill"], t: "Kill the cold-audience ad set — its CPL is ~2x the winner. Move that budget to Hook B. Noah's ready the moment you sign off." },
    ],
    fallback: "Good — I'll fold that into the campaign plan and brief the specialists. Want a one-pager first?",
  },
  margo: {
    sub: "Offers Strategist",
    suggest: ["Walk me through the Nova pricing test", "Where's the most upside?", "Should we add an order bump?", "Model a price increase"],
    replies: [
      { k: ["nova", "pricing", "test", "tier"], t: "3 tiers: $47 core, $97 with the templates bundle, $197 with a group call. Modeled +$14.2k/mo at current traffic. It's waiting on your approval before Alma builds it in GHL." },
      { k: ["upside", "revenue", "money", "lever"], t: "Biggest lever is the order bump on the core offer — ~$14.2k/mo for one checkbox. Second is a win-back offer for Lumen's paused members." },
      { k: ["bump", "upsell"], t: "Yes — a $27 order bump on the Nova checkout. Low friction, high take-rate at this price point. I'll draft the copy if you greenlight it." },
      { k: ["increase", "raise", "price"], t: "At a 12% raise I model minimal churn given current LTV — but I'd test it on new buyers first. Want me to set it up as a split?" },
    ],
    fallback: "I'll model it and bring you numbers, not opinions. Give me the offer and the audience.",
  },
  cole: {
    sub: "Automation Engineer · GoHighLevel",
    suggest: ["Run the Community Call automation", "What's the webhook issue?", "Build me a new workflow", "Health-check everything"],
    replies: [
      { k: ["run", "community", "call", "replay", "automation"], t: RUN },
      { k: ["webhook", "lumen", "broke", "flaky"], t: "Lumen's calendar webhook misfired twice overnight — the fallback caught every booking, so nothing was lost. Root cause is a timeout on their end; retry + alert patch is queued for 9am." },
      { k: ["build", "new", "workflow", "make"], t: "Give me the trigger and the outcome. Recent builds: abandoned-checkout recovery, post-purchase nurture, the call-replay flow. I'll draft it in GHL and QA before it goes live." },
      { k: ["health", "check", "status", "broken"], t: "31 workflows active, 30 healthy. Only Lumen's calendar booking is flaky. Everything else is green across all four accounts." },
    ],
    fallback: "I can automate that. Tell me the trigger, the steps, and which account it lives in.",
  },
  ravi: {
    sub: "Traffic & Growth",
    suggest: ["How's CPL trending?", "Which hook is winning?", "Where should we spend more?", "What's ranking?"],
    replies: [
      { k: ["cpl", "cost", "trend"], t: "Blended CPL is $2.14, down 22% week over week. Hook B is carrying it — that's where I want to move the cold-audience budget." },
      { k: ["hook", "winning", "creative", "ad"], t: "Hook B (the 'three pillars leak' angle) is beating the rest by 22% on CPL. I've got 2 variants ready to test alongside it." },
      { k: ["spend", "budget", "scale", "more"], t: "Shift $400/day from the cold set to Hook B and its variants — that's the approval in your queue. Expected impact: another ~10% off CPL." },
      { k: ["rank", "seo", "blog", "organic"], t: "Two cluster pages just hit page one; two more publish tomorrow once Apex's token is fixed. Organic is ~30% of new pipeline now." },
    ],
    fallback: "I'll test it small, kill the losers fast, and scale what works. What's the goal — reach, leads, or sales?",
  },
  juno: {
    sub: "Content Producer",
    suggest: ["Show me the recap post", "Turn the call into shorts", "Write me 5 hooks", "Repurpose my best video"],
    replies: [
      { k: ["recap", "post", "call", "replay"], t: "It's drafted — title, a one-line transformation summary, and a question to ponder, formatted for the Replays channel. It's in your approval queue and in the live automation on the right." },
      { k: ["short", "clip", "reel"], t: "I pulled 4 shorts from the last call — the strongest is the 'find the leak' segment at 12:30. Want them captioned in your style?" },
      { k: ["hook", "headline", "idea", "5"], t: "Three to start: 'The leak that drains launches', 'One metric tells you which pillar to fix', 'Why more traffic won't save a broken offer'. Want the full set of five?" },
      { k: ["repurpose", "video", "long"], t: "Send me the long-form and I'll spin 9 assets — shorts, carousel, email, blog, and 3 captions. Last one did exactly that across 4 channels." },
    ],
    fallback: "Send me the idea or the raw asset — I draft, you approve, we publish. That's the loop.",
  },
  sage: {
    sub: "Community Manager",
    suggest: ["What's the community mood?", "Post the latest replay", "Anything escalated to me?", "Schedule the next call"],
    replies: [
      { k: ["mood", "engagement", "community", "sentiment"], t: "Engagement's healthy — 41 threads answered today, sentiment positive. Pricing questions are clustering, which usually means people are ready to buy." },
      { k: ["replay", "post", "publish"], t: "The June 2 Recalibration Call replay is drafted and waiting on your approval. Say go and it posts to the Replays channel with the link set to anyone-with-the-link." },
      { k: ["escalat", "flag", "issue", "need"], t: "Two threads need you: a refund-request judgment call and a member asking for a custom plan. Both are in your queue with my recommended response." },
      { k: ["schedule", "next", "call", "announce"], t: "Next Recalibration Call is on the calendar. Want me to draft the announcement post and the reminder sequence?" },
    ],
    fallback: "I'll handle it in the community and keep the tone on-brand. Want me to post it, or hold for your approval?",
  },
  quinn: {
    sub: "Finance & Ops",
    suggest: ["What's our cash position?", "How's MRR trending?", "Margin by client", "Any failed charges?"],
    replies: [
      { k: ["cash", "position", "runway"], t: "Healthy. The 6am snapshot shows positive net across all accounts. Nova's launch week adds a one-time bump — I've modeled it so you don't over-spend ads against it." },
      { k: ["mrr", "recurring", "subscription", "trend"], t: "MRR is up across the board — Lumen +4%, Apex steady high-ticket. Watch Lumen's paused members; a win-back offer recovers most of them." },
      { k: ["margin", "profit", "client"], t: "Apex is your best margin — high-ticket, low fulfillment. Nova is volume-driven, thinner per sale but scaling. I'll send the per-client breakdown." },
      { k: ["fail", "charge", "dunning", "refund"], t: "One failed client charge last night — already recovered via the dunning sequence. No action needed; I only flag the ones that don't recover." },
    ],
    fallback: "I'll pull the numbers and tell you what they mean for the decision in front of you.",
  },
  max: {
    sub: "Messenger & DM · works with Iris",
    suggest: ["Show me DMs needing a reply", "Draft a reply to this lead", "What's converting in DMs?", "Hand a hot lead to sales"],
    replies: [
      { k: ["dm", "message", "reply", "inbox", "respond"], t: "23 DMs in the queue — I've drafted replies for the 4 that look like buyers and flagged them for you. The rest I can auto-handle in your voice. Want to review the 4?" },
      { k: ["draft", "write", "craft", "lead"], t: "Send me the thread and the goal — book a call, answer an objection, or nurture — and I'll write 2–3 reply options in your tone. Iris and I keep the copy on-brand." },
      { k: ["convert", "working", "best", "perform"], t: "DM-to-call is climbing — the 'quick question?' opener converts best right now. Anything that goes hot, I pass straight to the booking flow." },
      { k: ["hand", "hot", "sales", "book"], t: "Done — I'll hand the qualified threads to the booking flow with a summary attached so nothing gets re-asked." },
    ],
    fallback: "I live in the inbox. Give me the platform and the thread and I'll craft the response — Iris backs me up on the copy.",
  },
  soham: {
    sub: "Dev Lead · works with Alma",
    suggest: ["What's broken right now?", "Status of the webhook fix", "Build a custom integration", "Run a security pass"],
    replies: [
      { k: ["broke", "broken", "break", "error", "down", "bug", "wrong"], t: "Two things on my board: Lumen's calendar webhook (root cause was a timeout — patch deploys at 9am) and the Apex publish token (fixed, awaiting your login to rotate). I hotfix first, then root-cause so it doesn't recur." },
      { k: ["webhook", "fix", "status", "deploy"], t: "The Lumen webhook fix is staged on Vercel — added retry + alert logic. I'll smoke-test against a live booking before I promote it; Alma's workflow stays on the fallback until then." },
      { k: ["build", "integration", "api", "custom", "connect"], t: "Tell me the systems and the data flow. I handle the code + deploys (GitHub → Vercel → Supabase), Alma wires the GoHighLevel side. What are we connecting?" },
      { k: ["security", "pass", "audit", "token", "rotate"], t: "Weekly dependency + secret-rotation pass is clean except the Apex token — I'll add it to the rotation so it never goes stale again." },
    ],
    fallback: "If it's code, deploys, or something that broke, it's mine. Drop the error or the repo and I'll dig in with Alma.",
  },
};

function getReply(agentId, text) {
  const conf = AGENT_CHAT[agentId] || AGENT_CHAT.atlas;
  const t = text.toLowerCase();
  const hit = conf.replies.find((r) => r.k.some((w) => t.includes(w)));
  if (hit && hit.t === RUN) {
    return { text: "Starting the Community Call → Replays run — I'll find the recording, verify it's a Recalibration Call or Creator Workshop (never a private call), set the link to anyone-with-the-link, and draft the post. You approve the title, summary and question before it posts.", action: "open-auto" };
  }
  return { text: hit ? hit.t : conf.fallback };
}

function ChatDock({ agent, ctx, allAgents, onSwitch, onOpenAuto, onProfile }) {
  const conf = AGENT_CHAT[agent.id] || AGENT_CHAT.atlas;
  const intro = agent.id === "atlas"
    ? ctx.greeting + " Ask me anything, or tell me to run something."
    : `${agent.name} here — ${conf.sub}. ${agent.now} What do you need?`;
  const [log, setLog] = useState([{ who: agent.name, role: "bot", text: intro }]);
  const [val, setVal] = useState("");
  const logRef = useRef(null);
  useEffect(() => { if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight; }, [log]);

  function send(text) {
    const msg = (text ?? val).trim();
    if (!msg) return;
    setLog((l) => [...l, { who: "You", role: "me", text: msg }]);
    setVal("");
    const reply = getReply(agent.id, msg);
    setTimeout(() => setLog((l) => [...l, { who: agent.name, role: "bot", text: reply.text, action: reply.action }]), 460);
  }

  return (
    <div className="chat-wrap">
      <div className="chat-head">
        <Avatar agent={agent} size="md" />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="nm">{agent.name}</div>
          <div className="st">{agent.role}{agent.lead ? " · lead" : ""}</div>
        </div>
        <button className="chat-profile" onClick={() => onProfile && onProfile(agent)} title="Knowledge & instructions">{I.book}</button>
      </div>

      <div className="chat-switch" title="Switch who you're talking to">
        {allAgents.map((a) => (
          <button key={a.id} className="cs-btn" data-active={a.id === agent.id} onClick={() => onSwitch(a)} title={`Chat with ${a.name} · ${a.role}`}>
            <Avatar agent={a} size="sm" />
          </button>
        ))}
      </div>

      <div className="chat-log" ref={logRef}>
        {log.map((m, i) => (
          <div key={i} className={`msg ${m.role}`}>
            {m.role === "bot" && <div className="who">{m.who}</div>}
            {m.text}
            {m.action === "open-auto" && (
              <button className="msg-action" onClick={onOpenAuto}>{I.flow} Open the live run →</button>
            )}
          </div>
        ))}
      </div>
      <div className="chat-suggest">
        {conf.suggest.map((s) => <button key={s} className="sugg" onClick={() => send(s)}>{s}</button>)}
      </div>
      <div className="chat-input">
        <textarea rows="1" value={val} placeholder={`Message ${agent.name}…`}
          onChange={(e) => setVal(e.target.value)}
          onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } }} />
        <button className="send" onClick={() => send()}>{I.send}</button>
      </div>
    </div>
  );
}

/* ---------- Live automation panel (right rail) ---------- */
function AutomationRunner({ auto, agents, autoPublish }) {
  const [running, setRunning] = useState(false);
  const [active, setActive] = useState(-1);   // index currently processing
  const [done, setDone] = useState([]);       // indices done
  const [gated, setGated] = useState(false);
  const [post, setPost] = useState(auto.post);
  const timers = useRef([]);

  function reset() {
    timers.current.forEach(clearTimeout); timers.current = [];
    setActive(-1); setDone([]); setGated(false); setRunning(false); setPost(auto.post);
  }
  const editPost = (k, v) => setPost((p) => ({ ...p, [k]: v }));
  function start() {
    reset();
    setRunning(true);
    let delay = 300;
    for (let i = 0; i < auto.steps.length; i++) {
      const step = auto.steps[i];
      const isGate = step.gate && !autoPublish;
      if (isGate) {
        // Pause here — approveGate() resumes scheduling the rest.
        timers.current.push(setTimeout(() => { setActive(i); setGated(true); }, delay));
        break;
      }
      const dur = step.gate ? 700 : step.dur; // auto-publish gate gets a brief beat
      timers.current.push(setTimeout(() => setActive(i), delay));
      delay += 350;
      timers.current.push(setTimeout(() => setDone((d) => [...d, i]), delay + dur));
      delay += dur;
    }
  }
  function approveGate() {
    const gi = auto.steps.findIndex((s) => s.gate);
    setGated(false);
    setDone((d) => [...d, gi]);
    let delay = 250;
    for (let i = gi + 1; i < auto.steps.length; i++) {
      const step = auto.steps[i];
      timers.current.push(setTimeout(() => setActive(i), delay));
      delay += 350;
      timers.current.push(setTimeout(() => setDone((d) => [...d, i]), delay + step.dur));
      delay += step.dur;
    }
  }
  useEffect(() => () => timers.current.forEach(clearTimeout), []);

  const allDone = done.length === auto.steps.length;
  function stepState(i) {
    if (done.includes(i)) return "done";
    if (gated && auto.steps[i].gate && active === i) return "gate";
    if (active === i && running) return "active";
    if (active >= i || done.length > 0) return active > i ? "done" : "pending";
    return "pending";
  }

  return (
    <div className="auto-panel">
      <div className="auto-card">
        <div style={{ display: "flex", alignItems: "flex-start", gap: 11 }}>
          <div className="risk-ico" style={{ background: "var(--accent-soft)", color: "var(--accent-ink)" }}>{I.flow}</div>
          <div style={{ flex: 1 }}>
            <div className="auto-title">{auto.name}</div>
            <div className="auto-cad">{auto.cadence}</div>
          </div>
        </div>

        <div className="steps">
          {auto.steps.map((s, i) => {
            const st = stepState(i);
            return (
              <div key={s.id} className="step" data-state={st}>
                <div className="step-line"></div>
                <div className="step-node">
                  {st === "done" ? I.check : st === "active" ? <span className="spin"></span> : st === "gate" ? "!" : i + 1}
                </div>
                <div style={{ minWidth: 0, flex: 1 }}>
                  <div className="stt">{s.title}</div>
                  <div className="std">{s.detail}</div>
                  <div className="sts">{s.source}{st === "active" ? " · working…" : st === "gate" ? " · awaiting you" : ""}</div>

                  {/* Verification result, shown once the verify step completes */}
                  {i === 1 && (done.includes(1) || st === "done") && auto.detected && (
                    <div className="verify">
                      <div className="vr"><b>{auto.detected.type}</b></div>
                      <div className="vr">{auto.detected.date} · {auto.detected.time} · {auto.detected.duration} · {auto.detected.file}</div>
                      <div className="vnote">{I.check} {auto.detected.note}</div>
                    </div>
                  )}

                  {/* Editable post preview at the approval gate */}
                  {st === "gate" && (
                    <div className="post-edit">
                      <div className="post-badges">
                        <span className="pbadge ch">{I.flow} {post.channel} channel</span>
                        <span className="pbadge sh">{post.sharing}</span>
                      </div>
                      <div className="post-flabel">Title</div>
                      <input value={post.title} onChange={(e) => editPost("title", e.target.value)} />
                      <div className="post-flabel">What they'll learn</div>
                      <textarea rows="3" value={post.description} onChange={(e) => editPost("description", e.target.value)} />
                      <div className="post-flabel">A question to ponder</div>
                      <textarea className="post-q" rows="3" value={post.question} onChange={(e) => editPost("question", e.target.value)} />
                      <div className="actions">
                        <button className="btn btn-primary btn-sm" onClick={approveGate}>{I.check} Approve & post to Replays</button>
                      </div>
                    </div>
                  )}
                </div>
              </div>
            );
          })}
        </div>

        <div style={{ marginTop: 16, display: "flex", gap: 8 }}>
          {!running && !allDone && <button className="btn btn-primary btn-sm" onClick={start}>{I.play} Run now</button>}
          {(running || allDone) && <button className="btn btn-ghost btn-sm" onClick={reset}>Reset</button>}
          {allDone && <span style={{ fontSize: 12, color: "var(--accent-ink)", alignSelf: "center", fontWeight: 600 }}>✓ Posted to Replays</span>}
        </div>
      </div>

      <p style={{ fontSize: 11.5, color: "var(--ink-3)", lineHeight: 1.5, marginTop: 16 }}>
        Anubis finds the call, then <b style={{ color: "var(--ink-2)" }}>verifies it's a Recalibration Call or Creator Workshop</b> by date &amp; time — private calls are skipped. It opens link sharing, Iris drafts the post, and it publishes to the Replays channel only after you approve. Toggle auto-publish in Tweaks to skip the gate.
      </p>
    </div>
  );
}

Object.assign(window, { I, Avatar, Sidebar, ChatDock, AutomationRunner });
