/* ============================================================
   Sourcing — node canvas that wires SOURCE → AGENT → DESTINATION.
   Deliberately simple, robust model so nothing can get tangled:

     • Every node belongs to one category: source | agent | dest
     • Ports are fixed and singular:
         source = one OUTPUT  (right)
         agent  = one INPUT (left) + one OUTPUT (right)
         dest   = one INPUT  (left)
     • An edge is just { id, from: nodeId, to: nodeId } (out→in implied).
     • nodes + edges live in ONE state object, so edges always resolve
       against the current nodes (no cross-render mismatch).

   This mirrors the campaign FlowBuilder's interactions (drag to move,
   drag a port to wire, click a wire to delete, click a node to edit)
   but with a far smaller surface area.
   ============================================================ */

const SRC_LM = () => window.WTC.LANE_META;

// ---- Node kinds. cat drives ports + color. fields drive the inspector. ----
function srcKinds() {
  const eventNames = window.WTC.events.filter((e) => e.type === "event").map((e) => e.name);
  const travelNames = window.WTC.events.filter((e) => e.type === "travel").map((e) => e.name);
  return {
    src_event:     { cat: "source", label: "Event roster",  icon: "calendar", fields: [{ type: "select", key: "event", label: "Event", opts: eventNames }], sum: (d) => d.event || "pick an event", desc: "Pulls the brands activating at an event + the agencies behind them." },
    src_travel:    { cat: "source", label: "Travel window", icon: "globe",    fields: [{ type: "select", key: "region", label: "Region", opts: travelNames }], sum: (d) => d.region || "pick a region", desc: "Pulls design-forward properties that fit the comped-stay stack." },
    src_lookalike: { cat: "source", label: "Win lookalike", icon: "trend",    fields: [{ type: "select", key: "basis", label: "Based on win", opts: ["Poppi", "Vita Coco", "Red Bull", "Rocket Mortgage"] }], sum: (d) => "like " + (d.basis || "a win"), desc: "Finds brands that resemble a past win." },
    src_agency:    { cat: "source", label: "Agency unlock", icon: "link",     fields: [{ type: "select", key: "agency", label: "Agency", opts: ["Jack Morton", "Momentum Worldwide", "160over90", "GMR Marketing"] }], sum: (d) => d.agency || "pick an agency", desc: "Pulls the brands an agency builds for." },
    src_csv:       { cat: "source", label: "CSV import",    icon: "upload",   fields: [{ type: "text", key: "file", label: "File", placeholder: "e.g. hotels.csv" }], sum: (d) => d.file || "upload a file", desc: "Imports a list you already have." },

    ag_prospect:   { cat: "agent",  label: "Prospector",    icon: "radar",    fields: [{ type: "slider", key: "minFit", label: "Min fit score", min: 50, max: 95, def: 75 }, { type: "chips", key: "lanes", label: "Lanes", opts: ["Brands", "Agencies", "Events", "Travel"], def: ["Brands"] }], sum: (d) => "fit ≥ " + (d.minFit || 75), desc: "Researches each candidate and keeps the ones worth reaching." },
    ag_research:   { cat: "agent",  label: "Researcher",    icon: "search",   fields: [{ type: "chips", key: "collect", label: "Collect", opts: ["Email", "Decision-maker", "Socials", "Past activations"], def: ["Email", "Decision-maker"] }], sum: () => "enrich intel", desc: "Adds the data each contact needs before outreach." },
    ag_enrich:     { cat: "agent",  label: "Enrichment",    icon: "upload",   fields: [{ type: "select", key: "provider", label: "Provider", opts: ["Clay", "Apollo"] }], sum: (d) => d.provider || "Clay", desc: "Round-trips contacts for verified emails." },
    ag_filter:     { cat: "agent",  label: "Filter",        icon: "filter",   fields: [{ type: "slider", key: "minFit", label: "Keep fit ≥", min: 50, max: 95, def: 80 }], sum: (d) => "fit ≥ " + (d.minFit || 80), desc: "Drops anything below the bar before it reaches a list." },
    ag_wait:       { cat: "agent",  label: "Wait / delay",  icon: "clock",    fields: [{ type: "slider", key: "days", label: "Wait", min: 0, max: 60, def: 7 }], sum: (d) => "wait " + (d.days ?? 7) + "d", desc: "Throttles the flow — holds candidates this many days before the next step (e.g. let a domain warm up, or stagger volume)." },
    split:         { cat: "agent",  label: "A/B split",     icon: "branch",   ports: [{ id: "in", side: "in", dy: 0.5 }, { id: "a", side: "out", dy: 0.36, label: "A" }, { id: "b", side: "out", dy: 0.72, label: "B" }], fields: [{ type: "slider", key: "ratio", label: "Split A", min: 10, max: 90, def: 50 }], sum: (d) => (d.ratio ?? 50) + "% / " + (100 - (d.ratio ?? 50)) + "%", desc: "Splits sourced contacts down two paths — A/B test which source, agent chain, or destination performs. Wire path A and path B to different next nodes." },

    dest_list:     { cat: "dest", label: "List",          icon: "contacts", ports: [{ id: "in", side: "in", dy: 0.5 }, { id: "out", side: "out", dy: 0.5 }], fields: [{ type: "text", key: "name", label: "List name", placeholder: "e.g. SB sponsors" }], sum: (d) => d.name || "new list", desc: "A holding list — review arrivals here, then wire it into a Follow-up flow." },
    dest_flow:     { cat: "dest", label: "Follow-up flow", icon: "sequence", fields: [{ type: "lane", key: "lane", label: "Enroll into lane flow" }], sum: (d) => SRC_LM()[d.lane || "brand"].label + " flow", desc: "Enrolls arrivals into the lane's follow-up flow. The flow's first step is your initial outreach — the follow-ups only begin after it sends, and pause the moment they reply." },
  };
}
const SRC_CAT = {
  source: { label: "Source", color: "var(--accent)" },
  agent:  { label: "Agent",  color: "var(--lane-agency)" },
  dest:   { label: "Output", color: "var(--green)" },
};
const SRC_PALETTE = {
  Sources: ["src_event", "src_travel", "src_lookalike", "src_agency", "src_csv"],
  Agents: ["ag_prospect", "ag_research", "ag_enrich", "ag_filter", "ag_wait"],
  Logic: ["split"],
  Outputs: ["dest_list", "dest_flow"],
};

const SN_W = 222, SN_H = 96;
// normalize ports to descriptors {id, side, dy, label}
function portList(K, kind) {
  const m = K[kind];
  if (m.ports) return m.ports;
  if (m.cat === "source") return [{ id: "out", side: "out", dy: 0.5 }];
  if (m.cat === "dest") return [{ id: "in", side: "in", dy: 0.5 }];
  return [{ id: "in", side: "in", dy: 0.5 }, { id: "out", side: "out", dy: 0.5 }];
}
const portById = (K, kind, pid) => portList(K, kind).find((p) => p.id === pid);
const portXY = (K, node, pid) => { const p = portById(K, node.kind, pid) || { side: "out", dy: 0.5 }; return { x: node.x + (p.side === "out" ? SN_W : 0), y: node.y + SN_H * (p.dy ?? 0.5) }; };

let _snid = 0;
const snid = (p) => p + (++_snid) + "_" + Math.random().toString(36).slice(2, 5);

function srcDefaults(kind, K) {
  const data = {};
  K[kind].fields.forEach((f) => { if (f.def !== undefined) data[f.key] = Array.isArray(f.def) ? [...f.def] : f.def; if (f.type === "lane" && data[f.key] === undefined) data[f.key] = "brand"; if (f.type === "select" && data[f.key] === undefined && f.opts[0]) data[f.key] = f.opts[0]; });
  return data;
}

function buildSourcingSeed(K) {
  const mk = (kind, x, y) => ({ id: snid("n"), kind, x, y, data: srcDefaults(kind, K) });
  const a = mk("src_event", 40, 90); a.data.event = (window.WTC.events.find((e) => e.type === "event") || {}).name || a.data.event;
  const b = mk("ag_prospect", 330, 90);
  const c = mk("ag_research", 620, 90);
  const d = mk("dest_flow", 910, 90); d.data.lane = "brand";
  const e = mk("src_agency", 40, 250);
  const f = mk("dest_flow", 910, 250); f.data.lane = "agency";
  const nodes = [a, b, c, d, e, f];
  const E = (from, to) => ({ id: snid("e"), from, to });
  const edges = [E(a.id, b.id), E(b.id, c.id), E(c.id, d.id), E(e.id, b.id)];
  return { nodes, edges };
}

function srcPath(p1, p2) { const dx = Math.max(40, Math.abs(p2.x - p1.x) * 0.5); return `M ${p1.x} ${p1.y} C ${p1.x + dx} ${p1.y}, ${p2.x - dx} ${p2.y}, ${p2.x} ${p2.y}`; }

function SrcField({ field, value, onChange }) {
  if (field.type === "slider") return (
    <div className="ag-field"><div className="ag-slider-head"><span className="ag-flabel">{field.label}</span><span className="num ag-sval">{value}</span></div>
      <input type="range" className="ag-range" min={field.min} max={field.max} value={value} onChange={(e) => onChange(+e.target.value)} /></div>);
  if (field.type === "select") return (
    <div className="ag-field"><span className="ag-flabel">{field.label}</span>
      <select className="ag-select" value={value || ""} onChange={(e) => onChange(e.target.value)}>{field.opts.map((o) => <option key={o} value={o}>{o}</option>)}</select></div>);
  if (field.type === "chips") return (
    <div className="ag-field"><span className="ag-flabel">{field.label}</span>
      <div className="facet-chips" style={{ marginTop: 8 }}>{field.opts.map((o) => <button key={o} className="facet-chip" data-on={(value || []).includes(o) || undefined} onClick={() => onChange((value || []).includes(o) ? value.filter((x) => x !== o) : [...(value || []), o])}>{o}</button>)}</div></div>);
  if (field.type === "lane") return (
    <div className="ag-field"><span className="ag-flabel">{field.label}</span>
      <div className="facet-chips" style={{ marginTop: 8 }}>{["brand", "agency", "event", "travel"].map((l) => <button key={l} className="facet-chip" data-on={value === l || undefined} onClick={() => onChange(l)}><span className="chip-dot" style={{ background: `var(--lane-${l})` }}></span>{SRC_LM()[l].label}</button>)}</div></div>);
  return (
    <div className="ag-field"><span className="ag-flabel">{field.label}</span>
      <input className="input" style={{ marginTop: 7 }} value={value || ""} placeholder={field.placeholder} onChange={(e) => onChange(e.target.value)} /></div>);
}

function SrcNodeView({ node, K, selected, onDown, onSelect, onWireStart, onWireEnd }) {
  const k = K[node.kind], cat = SRC_CAT[k.cat];
  const ports = portList(K, node.kind);
  return (
    <div className={"flow-node flow-node--" + k.cat + (selected ? " is-selected" : "")} style={{ left: node.x, top: node.y, width: SN_W }}
      onMouseDown={(e) => onDown(e, node.id)} onClick={(e) => { e.stopPropagation(); onSelect(node.id); }}>
      <div className="node-head" style={{ color: cat.color }}><Icon name={k.icon} size={14} /> {k.label}<span className="src-cat" style={{ color: cat.color, background: cat.color + "1f" }}>{cat.label}</span></div>
      <div className="node-pad"><div className="src-sum">{k.sum(node.data)}</div></div>
      {ports.map((p) => (
        <div key={p.id} className={"node-port node-port--" + p.side} style={{ left: p.side === "in" ? -7 : undefined, right: p.side === "out" ? -7 : undefined, top: SN_H * (p.dy ?? 0.5) }}>
          {p.side === "out" && p.label && <span className="port-label">{p.label}</span>}
          <span className="port-dot port-dot--live" style={{ background: cat.color, boxShadow: `0 0 0 3px ${cat.color}22` }}
            onMouseDown={p.side === "out" ? (e) => onWireStart(e, node.id, p.id) : undefined}
            onMouseUp={p.side === "in" ? () => onWireEnd(node.id) : undefined}></span>
        </div>
      ))}
    </div>
  );
}

function SrcInspector({ node, K, onUpdate, onDelete, onClose }) {
  if (!node) return null;
  const k = K[node.kind], cat = SRC_CAT[k.cat];
  return (
    <div className="inspector">
      <div className="insp-head"><span className="insp-title" style={{ color: cat.color, display: "inline-flex", alignItems: "center", gap: 7 }}><Icon name={k.icon} size={15} /> {k.label}</span><button className="btn btn--ghost btn--icon btn--sm" onClick={onClose}><Icon name="x" size={16} /></button></div>
      <div className="insp-body">
        <div className="insp-step-meta" style={{ color: cat.color }}>{cat.label} node</div>
        <div className="insp-note" style={{ marginBottom: 14 }}>{k.desc}</div>
        {k.fields.map((f) => <SrcField key={f.key} field={f} value={node.data[f.key]} onChange={(v) => onUpdate(node.id, { [f.key]: v })} />)}
      </div>
      <div className="insp-foot"><button className="btn btn--sm btn--danger" onClick={() => onDelete(node.id)} style={{ width: "100%" }}><Icon name="trash" size={14} /> Remove node</button></div>
    </div>
  );
}

// ---- Pipeline catalogue (each opens the node builder, like Campaigns) ----
const SRC_PIPELINES = [
  { id: "sp_sb",     name: "Super Bowl sponsors",   out: "brand",  status: "active", found: 12, updated: "today",
    chain: ["src_event", "ag_prospect", "ag_research", "dest_flow"], srcData: { event: "Super Bowl LX" } },
  { id: "sp_f1",     name: "F1 Vegas roster",       out: "brand",  status: "active", found: 7, updated: "2 days ago",
    chain: ["src_event", "ag_prospect", "dest_flow"], srcData: { event: "F1 Las Vegas GP" } },
  { id: "sp_fest",   name: "Festival circuit",      out: "brand",  status: "active", found: 9, updated: "today",
    chain: ["src_event", "ag_prospect", "ag_filter", "dest_flow"], srcData: { event: "Coachella 2026" } },
  { id: "sp_agency", name: "Activation agencies",   out: "agency", status: "active", found: 5, updated: "1 day ago",
    chain: ["src_agency", "ag_prospect", "ag_research", "dest_flow"], srcData: { agency: "Jack Morton" } },
  { id: "sp_travel", name: "Design-forward travel", out: "travel", status: "draft", found: 4, updated: "5 days ago",
    chain: ["src_travel", "ag_prospect", "dest_flow"], srcData: {} },
  { id: "sp_look",   name: "Win lookalikes",        out: "brand",  status: "active", found: 8, updated: "today",
    chain: ["src_lookalike", "ag_prospect", "ag_filter", "dest_flow"], srcData: { basis: "Poppi" } },
];
const SP_STATUS = { active: { label: "Active", color: "var(--green)" }, draft: { label: "Draft", color: "var(--text-2)" }, paused: { label: "Paused", color: "var(--amber)" } };

function chainSeed(K, pipe) {
  const nodes = []; let x = 40; const y = 150;
  pipe.chain.forEach((kind, i) => {
    const data = srcDefaults(kind, K);
    if (i === 0 && pipe.srcData) Object.assign(data, pipe.srcData);
    if (kind === "dest_flow") data.lane = pipe.out;
    nodes.push({ id: snid("n"), kind, x, y, data });
    x += SN_W + 70;
  });
  const edges = [];
  for (let i = 0; i < nodes.length - 1; i++) edges.push({ id: snid("e"), from: nodes[i].id, to: nodes[i + 1].id });
  return { nodes, edges };
}

function SourcingCanvas({ seed }) {
  const K = React.useMemo(srcKinds, []);
  const [graph, setGraph] = React.useState(() => seed || buildSourcingSeed(K));
  const [selId, setSelId] = React.useState(null);
  const [selEdge, setSelEdge] = React.useState(null);
  const [offset, setOffset] = React.useState({ x: 30, y: 40 });
  const [zoom, setZoom] = React.useState(0.85);
  const [palette, setPalette] = React.useState(false);
  const [wirePt, setWirePt] = React.useState(null);
  const drag = React.useRef(null);
  const wrapRef = React.useRef(null);

  const nodes = graph.nodes, edges = graph.edges;
  const nodeById = (id) => nodes.find((n) => n.id === id);

  const fitTo = React.useCallback((list) => {
    const el = wrapRef.current; if (!el) return; const r = el.getBoundingClientRect(); if (!r.width) return;
    let a = Infinity, b = Infinity, c = -Infinity, d = -Infinity;
    list.forEach((n) => { a = Math.min(a, n.x); b = Math.min(b, n.y); c = Math.max(c, n.x + SN_W); d = Math.max(d, n.y + SN_H); });
    const pad = 60, z = Math.max(0.5, Math.min(r.width / ((c - a) + pad * 2), r.height / ((d - b) + pad * 2), 1));
    setZoom(z); setOffset({ x: (r.width - (c - a) * z) / 2 - a * z, y: (r.height - (d - b) * z) / 2 - b * z });
  }, []);
  React.useEffect(() => { const t = setTimeout(() => fitTo(graph.nodes), 30); return () => clearTimeout(t); }, []);

  React.useEffect(() => {
    function move(e) {
      const dd = drag.current; if (!dd) return;
      if (dd.type === "pan") setOffset({ x: dd.ox + (e.clientX - dd.sx), y: dd.oy + (e.clientY - dd.sy) });
      else if (dd.type === "node") { const dx = (e.clientX - dd.sx) / dd.zoom, dy = (e.clientY - dd.sy) / dd.zoom; setGraph((g) => ({ ...g, nodes: g.nodes.map((n) => n.id === dd.id ? { ...n, x: dd.nx + dx, y: dd.ny + dy } : n) })); }
      else if (dd.type === "wire") setWirePt({ x: (e.clientX - dd.rl - dd.ox) / dd.zoom, y: (e.clientY - dd.rt - dd.oy) / dd.zoom });
    }
    function up() { if (drag.current && drag.current.type === "wire") setWirePt(null); drag.current = null; document.body.style.cursor = ""; }
    window.addEventListener("mousemove", move); window.addEventListener("mouseup", up);
    return () => { window.removeEventListener("mousemove", move); window.removeEventListener("mouseup", up); };
  }, []);
  React.useEffect(() => { const el = wrapRef.current; if (!el) return; function wheel(e) { e.preventDefault(); setZoom((z) => Math.min(1.5, Math.max(0.4, z * (e.deltaY < 0 ? 1.08 : 0.93)))); } el.addEventListener("wheel", wheel, { passive: false }); return () => el.removeEventListener("wheel", wheel); }, []);

  const onCanvasDown = (e) => {
    if (e.target.closest(".flow-node") || e.target.closest(".inspector") || e.target.closest(".flow-toolbar") || e.target.closest(".flow-controls") || e.target.closest(".edge-hit")) return;
    setSelId(null); setSelEdge(null); setPalette(false);
    drag.current = { type: "pan", sx: e.clientX, sy: e.clientY, ox: offset.x, oy: offset.y }; document.body.style.cursor = "grabbing";
  };
  const onNodeDown = (e, id) => { if (e.target.closest(".port-dot")) return; e.stopPropagation(); const n = nodeById(id); drag.current = { type: "node", id, sx: e.clientX, sy: e.clientY, nx: n.x, ny: n.y, zoom }; };
  const onWireStart = (e, fromId, fromPort) => { e.stopPropagation(); const r = wrapRef.current.getBoundingClientRect(); drag.current = { type: "wire", from: fromId, fromPort: fromPort || "out", rl: r.left, rt: r.top, ox: offset.x, oy: offset.y, zoom }; setWirePt(portXY(K, nodeById(fromId), fromPort || "out")); };
  const onWireEnd = (toId) => {
    const dd = drag.current; if (!dd || dd.type !== "wire") return;
    const from = dd.from, fromPort = dd.fromPort || "out";
    setGraph((g) => {
      if (from === toId) return g;
      const toNode = g.nodes.find((n) => n.id === toId); if (!toNode || !portList(K, toNode.kind).some((p) => p.side === "in")) return g;
      if (g.edges.some((ed) => ed.from === from && (ed.fromPort || "out") === fromPort && ed.to === toId)) return g;
      return { ...g, edges: [...g.edges, { id: snid("e"), from, fromPort, to: toId }] };
    });
    setWirePt(null);
  };

  const updateNode = (id, patch) => setGraph((g) => ({ ...g, nodes: g.nodes.map((n) => n.id === id ? { ...n, data: { ...n.data, ...patch } } : n) }));
  const deleteNode = (id) => { setGraph((g) => ({ nodes: g.nodes.filter((n) => n.id !== id), edges: g.edges.filter((e) => e.from !== id && e.to !== id) })); setSelId(null); };
  const deleteEdge = (id) => { setGraph((g) => ({ ...g, edges: g.edges.filter((e) => e.id !== id) })); setSelEdge(null); };
  const addNode = (kind) => {
    const ref = nodeById(selId);
    const node = { id: snid("n"), kind, x: ref ? ref.x + SN_W + 70 : 200, y: ref ? ref.y : 160, data: srcDefaults(kind, K) };
    setGraph((g) => ({ ...g, nodes: [...g.nodes, node] }));
    setSelId(node.id); setPalette(false);
  };
  const fit = () => fitTo(nodes);
  const selNode = nodeById(selId);

  return (
    <div className="flow-wrap" ref={wrapRef} onMouseDown={onCanvasDown}>
      <div className="flow-world" style={{ transform: `translate(${offset.x}px, ${offset.y}px) scale(${zoom})` }}>
        <svg className="flow-edges" width="3000" height="1200">
          {edges.map((e) => {
            const fn = nodeById(e.from), tn = nodeById(e.to); if (!fn || !tn) return null;
            const p1 = portXY(K, fn, e.fromPort || "out"), p2 = portXY(K, tn, "in"), dpath = srcPath(p1, p2), on = selEdge === e.id;
            return (<g key={e.id}>
              <path d={dpath} fill="none" stroke="var(--accent)" strokeWidth={on ? 3 : 2} opacity="0.9" />
              <path className="edge-hit" d={dpath} fill="none" stroke="transparent" strokeWidth="16" style={{ cursor: "pointer", pointerEvents: "stroke" }} onMouseDown={(ev) => { ev.stopPropagation(); setSelEdge(e.id); setSelId(null); }} />
              <circle cx={p2.x} cy={p2.y} r="3" fill="var(--accent)" />
              {on && <g style={{ cursor: "pointer", pointerEvents: "auto" }} onMouseDown={(ev) => { ev.stopPropagation(); deleteEdge(e.id); }}>
                <circle cx={(p1.x + p2.x) / 2} cy={(p1.y + p2.y) / 2} r="11" fill="var(--red)" />
                <path d={`M ${(p1.x + p2.x) / 2 - 4} ${(p1.y + p2.y) / 2 - 4} l 8 8 M ${(p1.x + p2.x) / 2 + 4} ${(p1.y + p2.y) / 2 - 4} l -8 8`} stroke="#fff" strokeWidth="1.8" strokeLinecap="round" style={{ pointerEvents: "none" }} />
              </g>}
            </g>);
          })}
          {wirePt && drag.current && drag.current.type === "wire" && (() => { const p1 = portXY(K, nodeById(drag.current.from), drag.current.fromPort || "out"); return <path d={srcPath(p1, wirePt)} fill="none" stroke="var(--accent)" strokeWidth="2.5" strokeDasharray="4 4" />; })()}
        </svg>
        {nodes.map((n) => <SrcNodeView key={n.id} node={n} K={K} selected={selId === n.id} onDown={onNodeDown} onSelect={(id) => { setSelId(id); setSelEdge(null); }} onWireStart={onWireStart} onWireEnd={onWireEnd} />)}
      </div>

      <div className="flow-toolbar">
        <button className="btn btn--primary btn--sm" onClick={() => setPalette((p) => !p)}><Icon name="plus" size={15} /> Add node</button>
        <span className="flow-hint">source → agent → output</span>
        {palette && (<div className="palette">
          {Object.keys(SRC_PALETTE).map((grp) => (
            <React.Fragment key={grp}>
              <div className="menu-label">{grp}</div>
              {SRC_PALETTE[grp].map((kk) => { const m = K[kk], c = SRC_CAT[m.cat]; return <button key={kk} className="palette-item" onClick={() => addNode(kk)}><span className="palette-ico" style={{ color: c.color }}><Icon name={m.icon} size={15} /></span>{m.label}</button>; })}
            </React.Fragment>
          ))}
        </div>)}
      </div>

      <div className="flow-controls">
        <button className="flow-ctrl" onClick={() => setZoom((z) => Math.min(1.5, z + 0.12))} title="Zoom in"><Icon name="plus" size={16} /></button>
        <button className="flow-ctrl" onClick={() => setZoom((z) => Math.max(0.4, z - 0.12))} title="Zoom out"><div style={{ width: 14, height: 2, background: "currentColor", borderRadius: 2 }}></div></button>
        <button className="flow-ctrl" onClick={fit} title="Fit"><Icon name="grid" size={15} /></button>
        <div className="flow-zoom num">{Math.round(zoom * 100)}%</div>
      </div>

      <div className="flow-legend">
        <span><span className="lg-dot" style={{ background: "var(--accent)" }}></span> Source</span>
        <span><span className="lg-dot" style={{ background: "var(--lane-agency)" }}></span> Agent</span>
        <span><span className="lg-dot" style={{ background: "var(--green)" }}></span> Output</span>
        <span className="flow-legend-hint">drag a port to wire · click a connector to delete · click a node to configure</span>
      </div>

      {selNode && <SrcInspector node={selNode} K={K} onUpdate={updateNode} onDelete={deleteNode} onClose={() => setSelId(null)} />}
    </div>
  );
}
const SRC_SOURCE_OPTS = [
  { kind: "src_event", label: "Event roster" },
  { kind: "src_travel", label: "Travel window" },
  { kind: "src_lookalike", label: "Win lookalike" },
  { kind: "src_agency", label: "Agency unlock" },
  { kind: "src_csv", label: "CSV import" },
];

function NewPipelineModal({ onClose, onCreate }) {
  const LM = window.WTC.LANE_META;
  const [name, setName] = React.useState("");
  const [src, setSrc] = React.useState("src_event");
  const [out, setOut] = React.useState("brand");
  const [research, setResearch] = React.useState(true);
  const create = () => {
    const chain = [src, "ag_prospect"];
    if (research) chain.push("ag_research");
    chain.push("dest_flow");
    onCreate({ name: name.trim() || (LM[out].label + " sourcing"), out, chain });
  };
  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal" style={{ width: "min(520px, 100%)" }} onClick={(e) => e.stopPropagation()}>
        <div className="modal-head">
          <span style={{ fontSize: 15.5, fontWeight: 680 }}>New sourcing pipeline</span>
          <button className="btn btn--ghost btn--icon" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body" style={{ display: "flex", flexDirection: "column", gap: 15 }}>
          <div className="brief-field"><label className="brief-label">Pipeline name</label>
            <input className="input" autoFocus value={name} onChange={(e) => setName(e.target.value)} placeholder="e.g. Super Bowl sponsors" /></div>
          <div className="brief-field"><label className="brief-label">Source</label>
            <div className="facet-chips">{SRC_SOURCE_OPTS.map((s) => (
              <button key={s.kind} className="facet-chip" data-on={src === s.kind || undefined} onClick={() => setSrc(s.kind)}>{s.label}</button>
            ))}</div></div>
          <div className="brief-field"><label className="brief-label">Outputs into flow</label>
            <div className="facet-chips">{["brand", "agency", "event", "travel"].map((l) => (
              <button key={l} className="facet-chip" data-on={out === l || undefined} onClick={() => setOut(l)}><span className="chip-dot" style={{ background: `var(--lane-${l})` }}></span>{LM[l].label}</button>
            ))}</div></div>
          <div className="ag-field ag-field--row" style={{ marginBottom: 0 }}>
            <span className="ag-flabel">Add a Researcher step</span>
            <button className={"agent-toggle" + (research ? " on" : "")} onClick={() => setResearch((r) => !r)}><span className="agent-knob"></span></button>
          </div>
          <div className="insp-hint" style={{ marginTop: -6 }}>Builds Source → Prospector{research ? " → Researcher" : ""} → Follow-up flow. Edit every node in the builder.</div>
        </div>
        <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, padding: "0 22px 20px" }}>
          <button className="btn" onClick={onClose}>Cancel</button>
          <button className="btn btn--primary" onClick={create}><Icon name="plus" size={15} /> Create & open builder</button>
        </div>
      </div>
    </div>
  );
}

function SourcingList({ onOpen, onNew }) {
  const [tab, setTab] = React.useState("Pipelines");
  return (
    <div className="surface-scroll">
      <div className="today-head">
        <div>
          <h1 className="page-title">Scouting</h1>
          <p className="page-sub">{tab === "Pipelines" ? "Sourcing pipelines — each wires a source → agents → a list/tag." : "Events & travel windows you track — open one to add its sponsors and their agencies."}</p>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <Segmented value={tab} onChange={setTab} options={["Pipelines", "Event rosters"]} />
          {tab === "Pipelines" && <button className="btn btn--primary" onClick={onNew}><Icon name="plus" size={15} /> New pipeline</button>}
        </div>
      </div>
      {tab === "Event rosters" ? <Events lane="all" /> : (
      <div className="campaign-grid">
        {SRC_PIPELINES.map((p) => {
          const st = SP_STATUS[p.status];
          return (
            <button key={p.id} className="campaign-card lift" onClick={() => onOpen(p.id)}>
              <div className="cc-top">
                <LaneChip lane={p.out} />
                <span className="cc-status" style={{ color: st.color }}><span className="stagedot" style={{ background: st.color }}></span>{st.label}</span>
              </div>
              <div className="cc-name">{p.name}</div>
              <div className="cc-goal">Outputs into the {window.WTC.LANE_META[p.out].label} follow-up flow.</div>
              <div className="cc-channels">
                {p.chain.map((k, i) => { const m = window.SRC_KINDS_REF[k]; const c = window.SRC_CAT_REF[m.cat]; return <span key={i} className="cc-chan" style={{ color: c.color }} title={m.label}><Icon name={m.icon} size={13} /></span>; })}
                <span className="cc-steps num">{p.chain.length} nodes</span>
              </div>
              <div className="cc-stats">
                <div className="cc-stat"><span className="num cc-stat-num" style={{ color: "var(--accent)" }}>{p.found}</span><span className="cc-stat-lbl">Sourced</span></div>
                <div className="cc-stat"><span className="num cc-stat-num">{p.chain.length}</span><span className="cc-stat-lbl">Nodes</span></div>
                <div className="cc-stat"><span className="num cc-stat-num" style={{ color: st.color, fontSize: 13, fontWeight: 600 }}>{st.label}</span><span className="cc-stat-lbl">Status</span></div>
              </div>
              <div className="cc-foot">
                <span style={{ fontSize: 11.5, color: "var(--text-3)" }}>Updated {p.updated}</span>
                <span className="cc-open">Open <Icon name="chevronRight" size={13} /></span>
              </div>
            </button>
          );
        })}
      </div>
      )}
    </div>
  );
}

function Scouting() {
  const K = React.useMemo(srcKinds, []);
  const [openId, setOpenId] = React.useState(null);
  const [view, setView] = React.useState("Flow");
  const [creating, setCreating] = React.useState(false);
  const [, bump] = React.useReducer((x) => x + 1, 0);
  const pipe = SRC_PIPELINES.find((p) => p.id === openId);
  const seed = React.useMemo(() => pipe ? chainSeed(K, pipe) : null, [openId]);
  const createPipeline = ({ name, out, chain }) => {
    const id = "sp_" + Date.now();
    const srcData = chain[0] === "src_event" ? { event: (window.WTC.events.find((e) => e.type === "event") || {}).name }
      : chain[0] === "src_travel" ? { region: (window.WTC.events.find((e) => e.type === "travel") || {}).name }
      : chain[0] === "src_lookalike" ? { basis: "Poppi" }
      : chain[0] === "src_agency" ? { agency: "Jack Morton" } : {};
    SRC_PIPELINES.push({ id, name, out, status: "draft", found: 0, updated: "just now", chain, srcData });
    setCreating(false); bump(); setView("Flow"); setOpenId(id);
  };
  if (!pipe) return (
    <React.Fragment>
      <SourcingList onOpen={(id) => { setOpenId(id); setView("Flow"); }} onNew={() => setCreating(true)} />
      {creating && <NewPipelineModal onClose={() => setCreating(false)} onCreate={createPipeline} />}
    </React.Fragment>
  );
  const st = SP_STATUS[pipe.status];
  return (
    <div className="builder-surface">
      <div className="builder-bar">
        <button className="back-link" onClick={() => setOpenId(null)}><Icon name="chevronLeft" size={15} /> Scouting</button>
        <div className="builder-head">
          <div style={{ display: "flex", alignItems: "center", gap: 12, minWidth: 0 }}>
            <LaneChip lane={pipe.out} />
            <span className="builder-title" style={{ padding: "4px 0" }}>{pipe.name}</span>
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <Segmented value={view} onChange={setView} options={["Flow", "Results"]} />
            <span className="cc-status" style={{ color: st.color }}><span className="stagedot" style={{ background: st.color }}></span>{st.label}</span>
            <span className="num" style={{ fontSize: 12.5, color: "var(--text-3)" }}>{pipe.found} sourced</span>
          </div>
        </div>
      </div>
      {view === "Flow" ? <SourcingCanvas key={pipe.id} seed={seed} /> : <ScoutResults lane={pipe.out} />}
    </div>
  );
}

window.SRC_KINDS_REF = srcKinds();
window.SRC_CAT_REF = SRC_CAT;
Object.assign(window, { Scouting });
