  function setMode(m) {
    mode = m;
    $$("#modeTabs .mode-tab").forEach(t => t.classList.toggle("active", t.dataset.mode === m));
    $("#listPane").classList.toggle("hidden", m !== "list");
    $("#monPane").classList.toggle("hidden", m !== "monitor");
    if (m === "list") { stopStream(); }
    if (m === "monitor" && MON.id) { refreshMonSel(); startMonitor(); }
  }
  async function refreshMonSel() {
    try {
      const d = await GET("/campaigns/list?page_size=100");
      $("#monSel").innerHTML = (d.campaigns || []).map(c =>
        `<option value="${c.id}" ${c.id === MON.id ? "selected" : ""}>#${c.id} — ${esc(c.name)} (${c.status})</option>`).join("");
    } catch {}
    $("#monSel").onchange = () => { MON.id = +$("#monSel").value; startMonitor(); };
  }
  function stopStream() {
    if (MON.es) { MON.es.close(); MON.es = null; }
    if (MON.timer) { clearInterval(MON.timer); MON.timer = null; }
    $("#liveInd").classList.add("hidden");
  }
  async function startMonitor() {
    if (!MON.id) return;
    stopStream();
    const body = $("#monBody");
    body.innerHTML = `<div class="skeleton"></div><div class="skeleton" style="width:60%"></div>`;
    await drawProgress();     // initial paint via REST
    await drawRecipients();
    drawControls();
    // SSE
    MON.es = new EventSource(`/campaigns/${MON.id}/stream`);
    MON.es.addEventListener("connected", () => $("#liveInd").classList.remove("hidden"));
    MON.es.addEventListener("progress", ev => { try { paintProgress(JSON.parse(ev.data)); } catch {} });
    MON.es.addEventListener("done", ev => {
      try { paintProgress(JSON.parse(ev.data)); } catch {}
      stopStream();
      toast(`Campaign finished — status: ${(JSON.parse(ev.data) || {}).status || "?"}`, "info", 6000);
      drawControls();
    });
    MON.es.onerror = () => { $("#liveInd").classList.add("hidden"); };
    // Live poll: keep per-recipient table + stats fresh while running/paused.
