      const ex = el("button", { class: "btn sm secondary", onclick: () => { window.location = `/campaigns/${MON.id}/export`; } }, "Export CSV");
      const pv = el("button", { class: "btn sm secondary", onclick: () => campaignPreview(MON.id) }, "Preview");
      ctl.append(ex, pv);
    }).catch(() => {});
  }
  async function drawRecipients() {
    let box = $("#recBox");
    if (!box) { box = el("div", { id: "recBox" }); $("#monBody").append(box); }
    await loadRecipients(box);
  }
  async function loadRecipients(box) {
    const p = new URLSearchParams({ status: MON.statusF, search: MON.q,
                                    page: MON.page, page_size: 50 });
    let d;
    try { d = await GET(`/campaigns/${MON.id}/recipients?` + p); }
    catch (e) { box.innerHTML = `<div class="notice err"><span class="n-ico">✕</span><div>${esc(e.message)}</div></div>`; return; }
    const rows = d.recipients || [];
    const RS_BADGE = { pending: "b-gray", queued: "b-blue", processing: "b-amber", sent: "b-green",
                       temp_failed: "b-amber", perm_failed: "b-red", suppressed: "b-violet",
                       skipped: "b-gray", bounced: "b-red" };
    box.innerHTML = `
      <div class="card"><div class="card-hd"><h2>Per-recipient monitor</h2></div><div class="card-bd">
      <div class="filters flex mb-1">
        <select id="recF"><option value="">All statuses</option>
          ${["pending", "queued", "processing", "sent", "temp_failed", "perm_failed", "suppressed", "skipped", "bounced"].map(s => `<option ${MON.statusF === s ? "selected" : ""}>${s}</option>`).join("")}
        </select>
        <input id="recQ" placeholder="Search email…" value="${esc(MON.q || "")}">
        <button class="btn sm" id="recGo">Filter</button>
        <span class="small muted" style="margin-left:auto">${d.total} recipients</span>
      </div>
      ${rows.length ? `<div class="tbl-wrap"><table class="tbl">
        <thead><tr><th>Seq</th><th>Email</th><th>Status</th><th>Account</th><th>Subject</th><th>Invoice</th><th>Amount</th><th>Attempts</th><th>Sent at</th><th>Message ID</th><th>Last error</th></tr></thead>
        <tbody>${rows.map(r => `<tr>
          <td class="center mono-s">${r.seq}</td>
          <td>${esc(r.email)}</td>
          <td><span class="badge ${RS_BADGE[r.status] || "b-gray"}">${esc(r.status)}</span></td>
    <td class="small">${esc(r.account || "—")}</td>
          <td class="err-cell" title="${esc(r.subject || "")}">${esc((r.subject || "—").slice(0, 60))}</td>
          <td class="mono-s">${esc(r.invoice_number || "—")}</td>
          <td class="nowrap">${r.amount != null ? fmtMoney(r.amount) : "—"}</td>
          <td class="center">${r.attempts ?? 0}</td>
          <td class="nowrap small">${fmtDT(r.sent_at)}</td>
          <td class="mono-s" title="${esc(r.message_id || "")}">${r.message_id ? esc(r.message_id.slice(0, 14)) + "…" : "—"}</td>
          <td class="err-cell" title="${esc(r.last_error || "")}">${esc((r.last_error || "—").slice(0, 80))}</td>
        </tr>`).join("")}</tbody></table></div>` : `<div class="empty-state"><div class="e-ico">◷</div><p>No recipients materialized yet — they are created when the campaign starts.</p></div>`}
      <div class="pager" id="recPager"></div>
      </div></div>`;
    $("#recF").onchange = () => { MON.statusF = $("#recF").value; MON.page = 1; loadRecipients(box); };
    $("#recQ").onkeydown = e => { if (e.key === "Enter") { MON.q = $("#recQ").value.trim(); MON.page = 1; loadRecipients(box); } };
    $("#recGo").onclick = () => { MON.statusF = $("#recF").value; MON.q = $("#recQ").value.trim(); MON.page = 1; loadRecipients(box); };
    const pages = Math.max(1, Math.ceil(d.total / 50));
    $("#recPager").innerHTML = pages > 1 ?
      `<button class="btn sm secondary" ${MON.page <= 1 ? "disabled" : ""} data-pg="${MON.page - 1}">‹ Prev</button>
       <span class="small muted">Page ${MON.page} of ${pages}</span>
       <button class="btn sm secondary" ${MON.page >= pages ? "disabled" : ""} data-pg="${MON.page + 1}">Next ›</button>` : "";
    $$("#recPager [data-pg]").forEach(b => b.onclick = () => { MON.page = +b.dataset.pg; loadRecipients(box); });
  }

  /* ==================== CAMPAIGN PREVIEW MODAL ==================== */
  async function campaignPreview(cid, size) {
    let d;
