/* Page: Pipeline — Attio-style dense list + kanban drag-drop + lead detail.
   Role-aware: rep view scopes to my deals; mgr/owner see the full org. */
function PagePipeline({ role = "owner" }) {
  const { PIPELINE, REPS } = AppData;
  const repById = Object.fromEntries(REPS.map(r => [r.id, r]));
  const [view, setView] = React.useState("list");
  const [sel, setSel] = React.useState(new Set());
  const [filterOpen, setFilterOpen] = React.useState(false);
  const [newOpen, setNewOpen] = React.useState(false);
  const [filters, setFilters] = React.useState({ stage: "all", heat: "all", owner: "all", state: "all", source: "all", maxDays: 30 });
  const [newRow, setNewRow] = React.useState({ lead: "", age: 65, state: "TX", product: "Med Supp Plan G", source: "FB Lead Form", owner: REPS[0].id, phone: "", email: "" });
  const [extra, setExtra] = React.useState([]);
  const [overrides, setOverrides] = React.useState({}); // id -> { stage, owner }
  // When live data hydrates (initial load OR realtime tick), drop the local
  // optimistic layers — Supabase is now the source of truth.
  React.useEffect(() => {
    const onHydrate = () => { setExtra([]); setOverrides({}); };
    window.addEventListener("data:hydrated", onHydrate);
    return () => window.removeEventListener("data:hydrated", onHydrate);
  }, []);
  const [drag, setDrag] = React.useState(null);
  const [openLead, setOpenLead] = React.useState(null);
  const [bulkOpen, setBulkOpen] = React.useState(false);
  const [bulkAction, setBulkAction] = React.useState("stage");
  const [bulkValue, setBulkValue] = React.useState("Contacted");
  const [savedViews, setSavedViews] = React.useState(() => {
    try { return JSON.parse(localStorage.getItem("repflow.pipeline.views") || "[]"); } catch { return []; }
  });
  const [activeViewIdx, setActiveViewIdx] = React.useState(-1);
  const [csvOpen, setCsvOpen] = React.useState(false);

  const stages = ["New", "Contacted", "Quoted", "App In", "Issued"];
  const heats = ["fresh", "hot", "warm", "cold"];
  const sources = Array.from(new Set(PIPELINE.map(p => p.source)));
  const states = Array.from(new Set(PIPELINE.map(p => p.state)));
  const heatColor = (h) => h === "hot" ? "var(--accent-heat)" : h === "warm" ? "var(--state-warning)" : h === "fresh" ? "var(--accent-money)" : "var(--text-quaternary)";

  const meId = REPS[0].id;
  const all = [...extra, ...PIPELINE].map(p => overrides[p.id] ? { ...p, ...overrides[p.id] } : p);
  const scoped = role === "rep" ? all.filter(p => p.owner === meId) : all;
  const filtered = scoped.filter(p =>
    (filters.stage  === "all" || p.stage  === filters.stage) &&
    (filters.heat   === "all" || p.heat   === filters.heat) &&
    (filters.owner  === "all" || p.owner  === filters.owner) &&
    (filters.state  === "all" || p.state  === filters.state) &&
    (filters.source === "all" || p.source === filters.source) &&
    (p.days <= filters.maxDays)
  );

  const cols = "20px 1fr 90px 1fr 90px 60px 1fr 1fr 80px 30px";
  const submit = async () => {
    if (!newRow.lead.trim()) return;
    const localId = "tmp-" + Date.now();
    const row = {
      id: localId, lead: newRow.lead, age: +newRow.age, state: newRow.state,
      stage: "New", product: newRow.product, ap: 0, days: 0, last: "Just added",
      next: "First dial", source: newRow.source, owner: newRow.owner,
      consent: "verified", heat: "fresh",
      phone: (newRow.phone || "").trim() || null,
      email: (newRow.email || "").trim() || null,
    };
    try {
      await AppData.mutate.pipelineInsert(row);
      setExtra(e => [row, ...e]);
      setNewRow({ ...newRow, lead: "" });
      setNewOpen(false);
      window.toast && window.toast(`Added ${row.lead}${AppData.LIVE ? " · saved" : ""}`, "success");
    } catch (_e) { /* toast already fired by mutate */ }
  };
  const moveTo = async (id, stage) => {
    setOverrides(o => ({ ...o, [id]: { ...(o[id] || {}), stage } }));
    window.toast && window.toast(`Moved to ${stage}${AppData.LIVE ? " · saved" : ""}`, "success");
    try { await AppData.mutate.pipelineStage(id, stage); }
    catch (_e) { setOverrides(o => { const n = { ...o }; if (n[id]) delete n[id].stage; return n; }); }
  };
  const reassign = async (id, owner) => {
    setOverrides(o => ({ ...o, [id]: { ...(o[id] || {}), owner } }));
    const r = REPS.find(x => x.id === owner);
    window.toast && window.toast(`Reassigned to ${r?.name || owner}${AppData.LIVE ? " · saved" : ""}`, "success");
    try { await AppData.mutate.pipelineOwner(id, owner); }
    catch (_e) { setOverrides(o => { const n = { ...o }; if (n[id]) delete n[id].owner; return n; }); }
  };

  const applyBulk = async () => {
    const ids = Array.from(sel);
    setOverrides(o => {
      const next = { ...o };
      ids.forEach(id => { next[id] = { ...(next[id] || {}), [bulkAction === "stage" ? "stage" : "owner"]: bulkValue }; });
      return next;
    });
    setBulkOpen(false);
    window.toast && window.toast(`Updating ${ids.length} leads${AppData.LIVE ? "..." : ""}`, "info");
    setSel(new Set());
    if (AppData.LIVE) {
      try {
        await Promise.all(ids.map(id => bulkAction === "stage"
          ? AppData.mutate.pipelineStage(id, bulkValue)
          : AppData.mutate.pipelineOwner(id, bulkValue)));
        window.toast && window.toast(`Saved ${ids.length} leads`, "success");
      } catch (_e) { /* per-row toast fired */ }
    }
  };

  const saveView = () => {
    const name = prompt("Name this view:");
    if (!name) return;
    const view = { name, filters: { ...filters } };
    const next = [...savedViews, view];
    setSavedViews(next);
    localStorage.setItem("repflow.pipeline.views", JSON.stringify(next));
    setActiveViewIdx(next.length - 1);
  };
  const loadView = (idx) => {
    if (idx < 0 || !savedViews[idx]) return;
    setFilters(savedViews[idx].filters);
    setActiveViewIdx(idx);
  };
  const deleteView = (idx) => {
    const next = savedViews.filter((_, i) => i !== idx);
    setSavedViews(next);
    localStorage.setItem("repflow.pipeline.views", JSON.stringify(next));
    if (activeViewIdx === idx) setActiveViewIdx(-1);
  };
  const activeFilters = Object.entries(filters).filter(([k, v]) => v !== "all" && k !== "maxDays").length + (filters.maxDays < 30 ? 1 : 0);

  const subtitle = role === "rep"
    ? `My pipeline · ${filtered.length} active · ${filtered.filter(p => p.stage === "App In").length} in app stage`
    : `All contacts · ${filtered.length} active · ${filtered.filter(p => p.stage === "App In").length} in app stage`;

  return (
    <div className="page-pad">
      <div className="page-h">
        <div>
          <div className="page-title">Pipeline</div>
          <div className="page-sub">{subtitle}</div>
        </div>
        <div style={{ marginLeft: "auto", display: "flex", gap: 8, flexWrap: "wrap" }}>
          <div style={{ display: "flex", background: "var(--bg-elevated)", border: "1px solid var(--border-subtle)", borderRadius: 6, padding: 2 }}>
            {[["list","List"],["kanban","Kanban"],["sequences","Sequences"]].map(([k,l]) => (
              <button key={k} onClick={() => setView(k)} className={view === k ? "btn" : "btn btn-ghost"} style={{ padding: "3px 10px", border: 0, background: view === k ? "var(--bg-raised)" : "transparent" }}>{l}</button>
            ))}
          </div>
          <button className="btn" onClick={() => setFilterOpen(true)}>
            <Icons.Filter size={13}/> Filter
            {activeFilters > 0 && <span className="chip chip-info" style={{ fontSize: 10, marginLeft: 4 }}>{activeFilters}</span>}
          </button>
          <button className="btn" onClick={() => setCsvOpen(true)}><Icons.ArrowUpRight size={13}/> Import CSV</button>
          <button className="btn" onClick={() => {
            const headers = ["lead","age","state","stage","product","ap","days","source","owner"];
            const rows = filtered.map(p => Object.fromEntries(headers.map(h => [h, p[h]])));
            window.exportCSV && window.exportCSV(rows, "pipeline.csv");
          }}><Icons.ArrowUpRight size={13}/> Export</button>
          {window.PipelineAutoDialButton && (() => { const B = window.PipelineAutoDialButton; return <B leads={filtered}/>; })()}
          <button className="btn btn-primary" onClick={() => setNewOpen(true)}><Icons.Plus size={13}/> New lead</button>
        </div>
      </div>

      {(savedViews.length > 0 || sel.size > 0) && (
        <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "8px 0", flexWrap: "wrap" }}>
          {savedViews.map((v, i) => (
            <span key={i} className={`chip ${activeViewIdx === i ? "chip-money" : ""}`} style={{ cursor: "pointer", display: "inline-flex", gap: 4 }} onClick={() => loadView(i)}>
              <span style={{ fontWeight: 500 }}>{v.name}</span>
              <button className="icon-btn" style={{ width: 14, height: 14, padding: 0, color: "var(--text-quaternary)" }} onClick={(e) => { e.stopPropagation(); deleteView(i); }}><Icons.X size={9}/></button>
            </span>
          ))}
          <button className="btn btn-ghost" style={{ padding: "3px 10px", fontSize: 11 }} onClick={saveView}><Icons.Plus size={11}/> Save view</button>
          {sel.size > 0 && (
            <span style={{ marginLeft: "auto", display: "flex", gap: 6, alignItems: "center" }}>
              <span style={{ fontSize: 11.5, color: "var(--text-tertiary)" }}>{sel.size} selected</span>
              <button className="btn" onClick={() => setBulkOpen(true)}><Icons.Workflow size={11}/> Bulk action</button>
              <button className="btn btn-ghost" onClick={() => setSel(new Set())}>Clear</button>
            </span>
          )}
        </div>
      )}

      {view === "list" && (
        <div className="panel">
          <div className="list">
            <div className="list-h" style={{ gridTemplateColumns: cols }}>
              <div></div>
              <div>Lead</div>
              <div>Age/St</div>
              <div>Stage</div>
              <div style={{ textAlign: "right" }}>AP</div>
              <div className="tabular" style={{ textAlign: "right" }}>Days</div>
              <div>Last touch</div>
              <div>Next action</div>
              <div>Owner</div>
              <div></div>
            </div>
            {filtered.map(p => (
              <div key={p.id} className={`row ${sel.has(p.id) ? "sel" : ""}`} style={{ gridTemplateColumns: cols }} onClick={(e) => {
                if (e.shiftKey || e.metaKey || e.ctrlKey) {
                  const n = new Set(sel); n.has(p.id) ? n.delete(p.id) : n.add(p.id); setSel(n);
                } else {
                  setOpenLead(p);
                }
              }}>
                <span className="dot" style={{ background: heatColor(p.heat) }}></span>
                <div className="cell-truncate" style={{ display: "flex", alignItems: "center", gap: 6, fontWeight: 500 }}>{p.lead}<span style={{ color: "var(--text-quaternary)", fontWeight: 400, fontSize: 11 }}>· {p.product}</span></div>
                <div className="cell-truncate tabular" style={{ color: "var(--text-tertiary)" }}>{p.age} · {p.state}</div>
                <div><span className={`chip ${
                  p.stage === "Issued" ? "chip-money" :
                  p.stage === "App In" ? "chip-info" :
                  p.stage === "Quoted" ? "chip-status" : ""
                }`}>{p.stage}</span></div>
                <div className="tabular" style={{ textAlign: "right", color: p.ap ? "var(--text-primary)" : "var(--text-quaternary)" }}>{p.ap ? `$${p.ap.toLocaleString()}` : "—"}</div>
                <div className="tabular" style={{ textAlign: "right", color: p.days > 5 ? "var(--state-danger)" : "var(--text-tertiary)" }}>{p.days}d</div>
                <div className="cell-truncate" style={{ color: "var(--text-tertiary)" }}>{p.last}</div>
                <div className="cell-truncate" style={{ color: "var(--text-secondary)" }}>{p.next}</div>
                <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                  {repById[p.owner]
                    ? <>
                        <Shared.Avatar rep={repById[p.owner]} size={18}/>
                        <span style={{ fontSize: 11.5, color: "var(--text-tertiary)" }}>{repById[p.owner].name.split(" ")[0]}</span>
                      </>
                    : <span style={{ fontSize: 11.5, color: "var(--text-quaternary)" }}>unassigned</span>}
                </div>
                <button className="icon-btn" onClick={(e) => { e.stopPropagation(); const n = new Set(sel); n.has(p.id) ? n.delete(p.id) : n.add(p.id); setSel(n); }}><Icons.Dots size={13}/></button>
              </div>
            ))}
            {filtered.length === 0 && <div style={{ padding: 36, textAlign: "center", color: "var(--text-tertiary)", fontSize: 12.5 }}>No leads match these filters.</div>}
          </div>
        </div>
      )}

      {view === "kanban" && (
        <div className="kanban-grid" style={{ display: "grid", gridTemplateColumns: `repeat(${stages.length}, 1fr)`, gap: 10 }}>
          {stages.map(s => {
            const items = filtered.filter(p => p.stage === s);
            const sum = items.reduce((a, b) => a + (b.ap || 0), 0);
            return (
              <div key={s} className="panel"
                onDragOver={(e) => { e.preventDefault(); }}
                onDrop={(e) => { e.preventDefault(); if (drag != null) { moveTo(drag, s); setDrag(null); } }}>
                <div className="panel-h">
                  <h3>{s}</h3>
                  <span className="meta tabular">{items.length} · ${sum.toLocaleString()}</span>
                </div>
                <div style={{ padding: 8, display: "flex", flexDirection: "column", gap: 6, minHeight: 220 }}>
                  {items.map(p => (
                    <div key={p.id}
                      draggable
                      onDragStart={() => setDrag(p.id)}
                      onDragEnd={() => setDrag(null)}
                      onClick={() => setOpenLead(p)}
                      style={{ background: drag === p.id ? "var(--bg-overlay)" : "var(--bg-raised)", border: "1px solid var(--border-subtle)", borderRadius: 6, padding: 10, cursor: "grab", opacity: drag === p.id ? 0.5 : 1 }}>
                      <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12.5, fontWeight: 500 }}>
                        <span className="dot" style={{ background: heatColor(p.heat) }}></span>
                        {p.lead}
                      </div>
                      <div style={{ fontSize: 11, color: "var(--text-tertiary)", marginTop: 4 }}>{p.product}</div>
                      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 8 }}>
                        <span className="tabular" style={{ fontSize: 12, fontWeight: 500 }}>{p.ap ? `$${p.ap.toLocaleString()}` : "—"}</span>
                        <Shared.Avatar rep={repById[p.owner]} size={16}/>
                      </div>
                    </div>
                  ))}
                  {items.length === 0 && drag != null && (
                    <div style={{ padding: 14, border: "1px dashed var(--border-strong)", borderRadius: 6, color: "var(--text-tertiary)", fontSize: 11.5, textAlign: "center" }}>Drop to move to {s}</div>
                  )}
                </div>
              </div>
            );
          })}
        </div>
      )}

      {view === "sequences" && (() => { const PS = window.PipelineSequences; return PS ? <PS role={role}/> : null; })()}

      {filterOpen && (
        <Shared.Modal title="Filter pipeline" onClose={() => setFilterOpen(false)} actions={
          <>
            <button className="btn btn-ghost" onClick={() => setFilters({ stage: "all", heat: "all", owner: "all", state: "all", source: "all", maxDays: 30 })}>Reset</button>
            <button className="btn btn-primary" onClick={() => setFilterOpen(false)}>Apply</button>
          </>
        }>
          <Shared.Field label="Stage"><Shared.Select value={filters.stage} onChange={(v) => setFilters({ ...filters, stage: v })} options={[{ v: "all", l: "All stages" }, ...stages.map(s => ({ v: s, l: s }))]}/></Shared.Field>
          <Shared.Field label="Heat"><Shared.Select value={filters.heat} onChange={(v) => setFilters({ ...filters, heat: v })} options={[{ v: "all", l: "Any heat" }, ...heats.map(h => ({ v: h, l: h }))]}/></Shared.Field>
          {role !== "rep" && <Shared.Field label="Owner"><Shared.Select value={filters.owner} onChange={(v) => setFilters({ ...filters, owner: v })} options={[{ v: "all", l: "Any rep" }, ...REPS.map(r => ({ v: r.id, l: r.name }))]}/></Shared.Field>}
          <Shared.Field label="State"><Shared.Select value={filters.state} onChange={(v) => setFilters({ ...filters, state: v })} options={[{ v: "all", l: "Any state" }, ...states.map(s => ({ v: s, l: s }))]}/></Shared.Field>
          <Shared.Field label="Source"><Shared.Select value={filters.source} onChange={(v) => setFilters({ ...filters, source: v })} options={[{ v: "all", l: "Any source" }, ...sources.map(s => ({ v: s, l: s }))]}/></Shared.Field>
          <Shared.Field label={`Max age in stage · ${filters.maxDays}d`}>
            <input type="range" min={1} max={30} value={filters.maxDays} onChange={(e) => setFilters({ ...filters, maxDays: +e.target.value })} style={{ width: "100%" }}/>
          </Shared.Field>
        </Shared.Modal>
      )}

      {newOpen && (
        <Shared.Modal title="New lead" onClose={() => setNewOpen(false)} actions={
          <>
            <button className="btn btn-ghost" onClick={() => setNewOpen(false)}>Cancel</button>
            <button className="btn btn-primary" onClick={submit} disabled={!newRow.lead.trim()}><Icons.Plus size={12}/> Add to pipeline</button>
          </>
        }>
          <Shared.Field label="Name"><input className="text-input" value={newRow.lead} onChange={(e) => setNewRow({ ...newRow, lead: e.target.value })} placeholder="Cheryl Hampton" autoFocus/></Shared.Field>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
            <Shared.Field label="Phone"><input className="text-input" type="tel" value={newRow.phone} onChange={(e) => setNewRow({ ...newRow, phone: e.target.value })} placeholder="+1 512 555 1234" inputMode="tel"/></Shared.Field>
            <Shared.Field label="Email (optional)"><input className="text-input" type="email" value={newRow.email} onChange={(e) => setNewRow({ ...newRow, email: e.target.value })} placeholder="lead@example.com"/></Shared.Field>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
            <Shared.Field label="Age"><input className="text-input" type="number" min={18} max={110} value={newRow.age} onChange={(e) => setNewRow({ ...newRow, age: e.target.value })}/></Shared.Field>
            <Shared.Field label="State"><Shared.Select value={newRow.state} onChange={(v) => setNewRow({ ...newRow, state: v })} options={["TX","FL","CA","NY","GA","NV","AZ","OH","PA","MI","NC","WI","WA"].map(s => ({ v: s, l: s }))}/></Shared.Field>
          </div>
          <Shared.Field label="Product"><Shared.Select value={newRow.product} onChange={(v) => setNewRow({ ...newRow, product: v })} options={["Med Supp Plan G","Med Supp Plan N","Final Expense $10K","Final Expense $15K","Final Expense $20K","Final Expense $25K","Annuity $50K"].map(s => ({ v: s, l: s }))}/></Shared.Field>
          <Shared.Field label="Source"><Shared.Select value={newRow.source} onChange={(v) => setNewRow({ ...newRow, source: v })} options={["FB Lead Form","Inbound call","T65 list","Referral","Cross-sell"].map(s => ({ v: s, l: s }))}/></Shared.Field>
          {role !== "rep" && <Shared.Field label="Owner"><Shared.Select value={newRow.owner} onChange={(v) => setNewRow({ ...newRow, owner: v })} options={REPS.map(r => ({ v: r.id, l: r.name }))}/></Shared.Field>}
          <div style={{ marginTop: 8, padding: 8, background: "var(--bg-raised)", borderRadius: 6, fontSize: 11.5, color: "var(--text-tertiary)", lineHeight: 1.5 }}>
            Phone is optional but required to dial / SMS this lead. You can add it later from the lead detail drawer.
          </div>
        </Shared.Modal>
      )}

      {openLead && <LeadDetail lead={openLead} role={role} onClose={() => setOpenLead(null)} onMove={(stage) => moveTo(openLead.id, stage)} onReassign={(o) => reassign(openLead.id, o)}/>}

      {csvOpen && (() => { const C = window.CSVImport; return C ? <C onClose={() => setCsvOpen(false)}/> : null; })()}

      {bulkOpen && (
        <Shared.Modal title={`Bulk action · ${sel.size} leads`} onClose={() => setBulkOpen(false)} actions={
          <>
            <button className="btn btn-ghost" onClick={() => setBulkOpen(false)}>Cancel</button>
            <button className="btn btn-primary" onClick={applyBulk}><Icons.Check size={11}/> Apply to {sel.size}</button>
          </>
        }>
          <Shared.Field label="Action">
            <Shared.Select value={bulkAction} onChange={(v) => { setBulkAction(v); setBulkValue(v === "stage" ? "Contacted" : REPS[0].id); }} options={[{ v: "stage", l: "Move to stage" }, { v: "owner", l: "Reassign to producer" }]}/>
          </Shared.Field>
          <Shared.Field label="Value">
            <Shared.Select value={bulkValue} onChange={setBulkValue} options={bulkAction === "stage" ? stages.map(s => ({ v: s, l: s })) : REPS.map(r => ({ v: r.id, l: r.name }))}/>
          </Shared.Field>
        </Shared.Modal>
      )}
    </div>
  );
}

function LeadDetail({ lead, role, onClose, onMove, onReassign }) {
  const { REPS } = AppData;
  const repById = Object.fromEntries(REPS.map(r => [r.id, r]));
  const owner = repById[lead.owner];
  const stages = ["New", "Contacted", "Quoted", "App In", "Issued"];
  const heatColor = lead.heat === "hot" ? "var(--accent-heat)" : lead.heat === "warm" ? "var(--state-warning)" : lead.heat === "fresh" ? "var(--accent-money)" : "var(--text-quaternary)";
  const initials = lead.lead.split(" ").map(s => s[0]).join("");

  // Inline-editable phone + email — debounce-saves to pipelineContact mutator
  const [phone, setPhone] = React.useState(lead.phone || "");
  const [email, setEmail] = React.useState(lead.email || "");
  React.useEffect(() => { setPhone(lead.phone || ""); setEmail(lead.email || ""); }, [lead.id]);
  const savePhone = async () => {
    const next = phone.trim() || null;
    if (next === (lead.phone || null)) return;
    try { await AppData.mutate.pipelineContact(lead.id, { phone: next }); window.toast && window.toast(next ? "Phone saved" : "Phone cleared", "success"); }
    catch (_e) {}
  };
  const saveEmail = async () => {
    const next = email.trim() || null;
    if (next === (lead.email || null)) return;
    try { await AppData.mutate.pipelineContact(lead.id, { email: next }); window.toast && window.toast(next ? "Email saved" : "Email cleared", "success"); }
    catch (_e) {}
  };

  return (
    <div className="slideout-overlay" onClick={onClose}>
      <aside className="slideout" onClick={(e) => e.stopPropagation()}>
        <div className="slideout-h">
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <div className="avatar-xs" style={{ width: 36, height: 36, fontSize: 13, background: owner?.color || "linear-gradient(135deg,#5b86e5,#36d1dc)" }}>{initials}</div>
            <div>
              <div style={{ fontSize: 16, fontWeight: 600, fontFamily: "var(--font-display)" }}>{lead.lead}</div>
              <div style={{ fontSize: 12, color: "var(--text-tertiary)" }}>{lead.age} · {lead.state} · {lead.source}</div>
            </div>
          </div>
          <button className="icon-btn" onClick={onClose}><Icons.X size={14}/></button>
        </div>

        <div className="slideout-body">
          <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
            <span className="chip" style={{ background: "color-mix(in oklch, " + heatColor + " 14%, transparent)", color: heatColor, borderColor: "color-mix(in oklch, " + heatColor + " 30%, transparent)" }}>
              <Icons.Flame size={11}/> {lead.heat}
            </span>
            <span className="chip">{lead.product}</span>
            <span className={`chip ${lead.consent === "verified" ? "chip-money" : "chip-status"}`}>Consent {lead.consent}</span>
          </div>

          <div className="divider"></div>

          <div className="field-l">Contact</div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginTop: 6 }}>
            <Shared.Field label="Phone">
              <input className="text-input" type="tel" value={phone}
                onChange={(e) => setPhone(e.target.value)}
                onBlur={savePhone}
                onKeyDown={(e) => { if (e.key === "Enter") { e.target.blur(); } }}
                placeholder="+1 512 555 1234" inputMode="tel"/>
            </Shared.Field>
            <Shared.Field label="Email">
              <input className="text-input" type="email" value={email}
                onChange={(e) => setEmail(e.target.value)}
                onBlur={saveEmail}
                onKeyDown={(e) => { if (e.key === "Enter") { e.target.blur(); } }}
                placeholder="lead@example.com"/>
            </Shared.Field>
          </div>
          {!phone.trim() && (
            <div style={{ marginTop: 6, fontSize: 11, color: "var(--state-warning)" }}>
              No phone on file — dial / SMS / autodial will skip this lead until you add one.
            </div>
          )}

          <div className="divider"></div>

          <div className="field-l">Stage</div>
          <div style={{ display: "flex", gap: 4, marginTop: 4, flexWrap: "wrap" }}>
            {stages.map(s => (
              <button key={s} className={`btn ${lead.stage === s ? "btn-primary" : "btn-ghost"}`} style={{ padding: "4px 10px", fontSize: 11.5 }} onClick={() => onMove(s)}>{s}</button>
            ))}
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginTop: 12 }}>
            <Shared.Field label="AP"><div className="tabular" style={{ fontSize: 16, fontWeight: 500 }}>{lead.ap ? `$${lead.ap.toLocaleString()}` : "—"}</div></Shared.Field>
            <Shared.Field label="Age in stage"><div className="tabular" style={{ fontSize: 16, fontWeight: 500, color: lead.days > 5 ? "var(--state-danger)" : "var(--text-primary)" }}>{lead.days}d</div></Shared.Field>
            <Shared.Field label="Last touch"><div style={{ fontSize: 13 }}>{lead.last}</div></Shared.Field>
            <Shared.Field label="Next action"><div style={{ fontSize: 13 }}>{lead.next}</div></Shared.Field>
          </div>

          <div className="divider"></div>

          <div className="field-l">Sequences</div>
          <div style={{ marginTop: 6, display: "flex", flexDirection: "column", gap: 6 }}>
            {((window.PIPELINE_SEQUENCES || []).slice(0, 3)).map(s => (
              <div key={s.id} style={{ display: "flex", alignItems: "center", gap: 8, padding: 8, background: "var(--bg-raised)", borderRadius: 6 }}>
                <Icons.Workflow size={12} style={{ color: "var(--text-tertiary)" }}/>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 500 }}>{s.name}</div>
                  <div style={{ fontSize: 10.5, color: "var(--text-tertiary)" }}>{s.steps.length} steps · {s.days}d · {s.channel}</div>
                </div>
                <button className="btn btn-ghost" style={{ padding: "3px 8px", fontSize: 11 }} onClick={async () => {
                  try {
                    await AppData.mutate.sequenceEnroll(lead.id, s.id, lead.owner);
                    window.toast && window.toast(`Enrolled ${lead.lead} in ${s.name}${AppData.LIVE ? " · saved" : ""}`, "success");
                  } catch (_e) {}
                }}>Enroll</button>
              </div>
            ))}
            {(window.PIPELINE_SEQUENCES || []).length === 0 && (
              <div style={{ padding: 10, fontSize: 11.5, color: "var(--text-tertiary)", textAlign: "center", border: "1px dashed var(--border-subtle)", borderRadius: 6 }}>
                No sequences yet. <a href="#" onClick={(e) => { e.preventDefault(); window.dispatchEvent(new CustomEvent("nav:goto", { detail: { page: "pipeline" }})); }} style={{ color: "var(--accent-money)" }}>Build one</a> in Pipeline → Sequences.
              </div>
            )}
          </div>

          <div className="divider"></div>

          {role !== "rep" && (
            <Shared.Field label="Owner">
              <Shared.Select value={lead.owner} onChange={onReassign} options={REPS.map(r => ({ v: r.id, l: r.name }))}/>
            </Shared.Field>
          )}

          <div className="divider"></div>

          <div className="field-l">Compliance</div>
          <div className="panel" style={{ padding: 12, marginTop: 6 }}>
            <div style={{ display: "flex", justifyContent: "space-between", padding: "6px 0", borderBottom: "1px solid var(--border-subtle)", fontSize: 12.5 }}>
              <span style={{ color: "var(--text-secondary)" }}>Consent</span>
              <span className={`chip ${lead.consent === "verified" ? "chip-money" : "chip-status"}`}>{lead.consent || "—"}</span>
            </div>
            <div style={{ display: "flex", justifyContent: "space-between", padding: "6px 0", borderBottom: "1px solid var(--border-subtle)", fontSize: 12.5 }}>
              <span style={{ color: "var(--text-secondary)" }}>LeadiD / TrustedForm</span>
              <span className="meta" style={{ fontSize: 11 }}>{
                lead.consent === "verified"
                  ? "captured at form fill"
                  : "not captured — wire Jornaya/TrustedForm in Connections"
              }</span>
            </div>
            <div style={{ display: "flex", justifyContent: "space-between", padding: "6px 0", fontSize: 12.5 }}>
              <span style={{ color: "var(--text-secondary)" }}>SOA</span>
              <span className={`chip ${lead.stage === "App In" || lead.stage === "Issued" ? "chip-money" : "chip-status"}`}>{
                lead.stage === "Issued" ? "captured" : lead.stage === "App In" ? "in app" : "before quote"
              }</span>
            </div>
          </div>

          <div className="divider"></div>

          <div className="field-l">Activity</div>
          <div style={{ marginTop: 8, display: "flex", flexDirection: "column", gap: 8 }}>
            {[
              lead.last && lead.next        ? { d: lead.last, t: "Last touch", s: lead.next } : null,
              lead.days != null && lead.source ? { d: (lead.days || 0) + "d ago", t: "Form filled", s: [lead.source, lead.state].filter(Boolean).join(" · ") } : null,
            ].filter(Boolean).map((a, i) => (
              <div key={i} style={{ display: "grid", gridTemplateColumns: "100px 1fr", gap: 8, fontSize: 12.5 }}>
                <span style={{ color: "var(--text-tertiary)" }}>{a.d}</span>
                <div><strong>{a.t}</strong><div style={{ color: "var(--text-tertiary)", fontSize: 11.5 }}>{a.s}</div></div>
              </div>
            ))}
            {!lead.last && !lead.source && (
              <div style={{ fontSize: 11.5, color: "var(--text-tertiary)" }}>No activity logged yet.</div>
            )}
          </div>
        </div>

        <div className="slideout-foot">
          <button className="btn" disabled={!email.trim()}
            title={email.trim() ? `Email ${email}` : "Add email first"}
            onClick={() => email.trim() && (window.location.href = `mailto:${email.trim()}?subject=Following up on your ${lead.product || "policy"} quote`)}>
            <Icons.Mail size={12}/> Email
          </button>
          <button className="btn" disabled={!phone.trim()}
            title={phone.trim() ? `SMS ${phone}` : "Add phone first"}
            onClick={() => phone.trim() && window.smsCompose && window.smsCompose(lead, phone.trim())}>
            <Icons.MessageSquare size={12}/> SMS
          </button>
          <button className="btn" onClick={() => {
            const meIdent = (typeof window !== "undefined" && window.me && window.me()) || null;
            const agencyName = meIdent?.agency_name || "your agency";
            window.generateSOAPdf && window.generateSOAPdf(lead, agencyName);
          }}><Icons.Shield size={12}/> SOA</button>
          <button className="btn btn-primary" disabled={!phone.trim()}
            title={phone.trim() ? `Call ${phone}` : "Add phone first"}
            onClick={() => phone.trim() && window.repflowCall && window.repflowCall(phone.trim(), lead.lead)}>
            <Icons.Phone size={12}/> Call now
          </button>
        </div>
      </aside>
    </div>
  );
}

window.PagePipeline = PagePipeline;
