            m.raw_variables && m.raw_variables.invoice_number ? el("span", { class: "badge" }, esc(m.raw_variables.invoice_number)) : null,
            m.raw_variables && m.raw_variables.amount != null ? el("span", { class: "badge" }, fmtMoney(m.raw_variables.amount)) : null,
            m.raw_variables && m.raw_variables.shipping != null ? el("span", { class: "badge" }, "ship " + fmtMoney(m.raw_variables.shipping)) : null),
          m.warnings && m.warnings.length ? el("div", { class: "notice warn", html:
            `<span class="n-ico">⚠</span><div>${m.warnings.map(w => "• " + esc(w)).join("<br>")}</div>` }) : null,
          el("iframe", { class: "preview-frame", srcdoc: m.body_html })))) : el("p", { class: "muted" }, "No sample messages."),
      d.plan_summary ? el("p", { class: "muted small mt-2" }, "Plan summary: " + esc(JSON.stringify(d.plan_summary))) : null);
    openModal(`Preview campaign #${cid}`, node, { xw: true, buttons: [
      { label: "Close", kind: "secondary", onClick: (ev, close) => close() },
      { label: "Refresh sample", kind: "primary", onClick: (ev, close) => { close(); campaignPreview(cid, (size || 5)); } },
    ]});
  }

  /* ==================== 12-STEP WIZARD ==================== */
  async function openWizard(editId) {
    try { if (!OPT) await loadOptions(); } catch (e) { toast(e.message, "err", 6000); return; }
    editingId = editId || null;
    Object.assign(C, DEF());
    step = 1;
    if (editingId) {
      try {
        const c = await GET(`/campaigns/${editingId}`);
        Object.assign(C, DEF(), {
          name: c.name, recipient_list_id: c.recipient_list_id,
          subject_set_ids: c.subject_set_ids || [], template_ids: c.template_ids || [],
          message_type: c.message_type, attach_mode: c.attach_mode,
          attach_pdf: c.attach_pdf, attach_pdf_template_id: c.attach_pdf_template_id,
          attachment_id: c.attachment_id, variable_config: c.variable_config || {},
          account_ids: c.account_ids || [], rotation_strategy: c.rotation_strategy,
          rotation_weights: c.rotation_weights || {}, subject_rotation: c.subject_rotation,
          template_sequence_mode: c.template_sequence_mode, phone_selection: c.phone_selection,
          max_per_account: c.max_per_account, send_interval_seconds: c.send_interval_seconds,
          rate_per_minute: c.rate_per_minute, max_retries: c.max_retries,
          retry_delay_seconds: c.retry_delay_seconds, unsubscribe_enabled: c.unsubscribe_enabled,
          preview_sample_size: c.preview_sample_size,
        });
      } catch (e) { toast(e.message, "err"); return; }
    }
    const wrap = el("div", {},
      el("div", { class: "wiz-steps", id: "wSteps" }),
      el("div", { class: "step-body", id: "wBody" }),
      el("div", { class: "wiz-foot" },
        el("button", { class: "btn secondary", id: "wCancel", onclick: () => closeModal() }, "Cancel"),
        el("div", { class: "flex", style: "gap:8px" },
          el("button", { class: "btn secondary", id: "wBack" }, "‹ Back"),
          el("button", { class: "btn", id: "wNext" }, "Next ›"))));
    openModal(editingId ? `Edit campaign #${editingId}` : "New campaign — 12-step wizard", wrap, { xw: true, onClose: () => stopWizardWatch() });
    $("#wBack").onclick = () => { if (step > 1) { step--; renderStep(); } };
    $("#wNext").onclick = next;
    renderStep();
  }
  function closeModal() { const x = $(".modal-back.open .x-btn"); if (x) x.click(); }
  function stopWizardWatch() {}

  function renderStepsBar() {
    $("#wSteps").innerHTML = STEPS.map((s, i) => {
      const n = i + 1;
      const cls = n === step ? "active" : (n < step ? "done" : "");
      return `<div class="wstep ${cls}" data-step="${n}" title="${esc(s)}"><span class="n">${n < step ? "✓" : n}</span>${esc(s)}</div>`;
    }).join("");
    $$("#wSteps .wstep.done").forEach(w => w.onclick = () => { step = +w.dataset.step; renderStep(); });
  }

  function numRow(label, key, attrs = "") {
    return `<div class="num-row"><label>${label}</label><input id="w_${key}" ${attrs} value="${esc(C[key] ?? "")}"></div>`;
  }

  function renderStep() {
    renderStepsBar();
    const b = $("#wBody");
    $("#wBack").style.visibility = step > 1 ? "visible" : "hidden";
    $("#wNext").textContent = step === 12 ? (editingId ? "Save changes" : "Create & start later") : "Next ›";
    const H = t => `<h3 style="margin:2px 0 4px">${t}</h3>`;
    const hint = t => `<p class="step-hint">${t}</p>`;

    if (step === 1) {
      b.innerHTML = H("Step 1 — Campaign name") + hint("A clear, unique name for this campaign. It appears in logs and exports.") +
        `<label class="lab">Campaign name</label>
         <input id="w_name" maxlength="250" placeholder="e.g. November invoice run — batch A" value="${esc(C.name)}">`;
    } else if (step === 2) {
      b.innerHTML = H("Step 2 — Recipient list") + hint(`Pick the list to send to. Max ${OPT.limits.max_recipients} recipients per campaign; only valid, non-suppressed rows are used.`) +
        pickList("lists", C.recipient_list_id, l => ({ v: l.id, main: l.name, sub: `${l.row_count} rows · ${l.valid_count} valid` }), "recipient_list_id", true);
    } else if (step === 3) {
      b.innerHTML = H("Step 3 — Subject lines") + hint(`Select subject line variations (up to ${OPT.limits.max_subjects}) and how they rotate across recipients.`) +
        pickList("subjects", C.subject_set_ids, s => ({ v: s.id, main: s.subject, sub: "" }), "subject_set_ids") +
        `<label class="lab mt-2">Subject rotation</label>
         <div class="radio-row">
           ${["round_robin", "random", "fixed_first"].map(m => `
             <label class="chk"><input type="radio" name="subjRot" value="${m}" ${C.subject_rotation === m ? "checked" : ""}>
             ${m === "round_robin" ? "Round robin" : m === "random" ? "Random shuffle" : "Fixed first subject"}</label>`).join("")}
         </div>`;
    } else if (step === 4) {
      b.innerHTML = H("Step 4 — HTML templates") + hint("Select one or more templates and how they sequence across recipients.") +
        pickList("templates", C.template_ids, t => ({ v: t.id, main: t.name, sub: "" }), "template_ids") +
        `<label class="lab mt-2">Template sequence mode</label>
         <div class="radio-row">
           ${["repeat", "stop", "random"].map(m => `
             <label class="chk"><input type="radio" name="tplMode" value="${m}" ${C.template_sequence_mode === m ? "checked" : ""}>
             ${m === "repeat" ? "Repeat from start" : m === "stop" ? "Stop after last" : "Random"}</label>`).join("")}
         </div>`;
    } else if (step === 5) {
      const am = C.attach_mode;
      b.innerHTML = H("Step 5 — Attachments") + hint("Choose what to attach. PDF mode renders a template to a unique Invoice PDF per recipient.") +
        `<div class="radio-row">
           ${[["none", "No attachment"], ["pdf", "Generated PDF invoice"], ["file", "Stored file attachment"], ["image", "Image attachment"]].map(([m, t]) => `
             <label class="chk"><input type="radio" name="attMode" value="${m}" ${am === m ? "checked" : ""}> ${t}</label>`).join("")}
         </div>
         <div id="attExtra"></div>`;
      bindAttExtra();
    } else if (step === 6) {
      const vc = C.variable_config;
      b.innerHTML = H("Step 6 — Variables & random amounts") + hint("Configure the invoice number format and random amount ranges used by %RandomInvoiceNumber%, %RandomTotal% and %RandomShippingCharge%.") +
        `<div class="grid cols-2">
           <div>
             <label class="lab">Invoice prefix</label>
             <input id="w_invoice_prefix" value="${esc(vc.invoice_prefix ?? "INV-")}">
             <label class="lab mt-1">Invoice format tokens</label>
             <input id="w_invoice_format" value="${esc(vc.invoice_format ?? "{prefix}{R4}-{R5}-{R6}-{seq4}")}">
             <p class="wcount">Tokens: {prefix} {R#} upper {r#} lower {d#} digits {seq4} {date} {hash}. Crypto-strong, collision-checked per campaign.</p>
           </div>
           <div>
