      $("#gal").innerHTML = `<div class="empty-state" style="grid-column:1/-1"><div class="e-ico">❑</div>
        <p>${all.length ? "No templates match the filters." : "No saved templates yet. Create or upload templates on the HTML Templates page."}</p></div>`;
      return;
    }
    $("#gal").innerHTML = rows.map(t => `
      <div class="tcard" data-id="${t.id}">
        <div class="tc-hd">
          <div class="flex-between">
            <h3>${esc(t.name)}</h3>
            <span class="src-badge">${esc(t.source || "—")}</span>
          </div>
          ${t.subject_hint ? `<div class="small muted mt-1" style="white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${esc(t.subject_hint)}</div>` : ""}
        </div>
        <div class="tc-bd">
          ${thumb(t)}
          <div class="var-chips">
            ${(t.variables || []).slice(0, 10).map(v => `<span class="chip">${esc(v)}</span>`).join("")}
            ${(t.variables || []).length > 10 ? `<span class="chip">+${t.variables.length - 10} more</span>` : ""}
            ${(t.unknown_variables || []).length ? `<span class="chip bad" title="${esc((t.unknown_variables || []).join(", "))}">${t.unknown_variables.length} unknown</span>` : ""}
          </div>
        </div>
        <div class="tc-ft">
          <button class="btn xs" data-prev="${t.id}">Preview</button>
          <button class="btn xs secondary" data-pdf="${t.id}">Test PDF</button>
          <button class="btn xs secondary" data-dup="${t.id}">Duplicate</button>
          <button class="btn xs danger-o" data-del="${t.id}">Delete</button>
        </div>
      </div>`).join("");

    $$("#gal [data-prev]").forEach(b => b.onclick = () => previewTpl(+b.dataset.prev));
    $$("#gal [data-pdf]").forEach(b => b.onclick = () => testPdf(+b.dataset.pdf));
    $$("#gal [data-dup]").forEach(b => b.onclick = async () => {
      try { const d = await POST(`/templates/${b.dataset.dup}/duplicate`); toast(`Duplicated — new template #${d.id}`, "ok"); load(); }
      catch (e) { toast(e.message, "err", 6000); }
    });
    $$("#gal [data-del]").forEach(b => b.onclick = () =>
      confirmedAction(`Delete this template? Campaigns that reference it will show a warning at start.`,
        async () => { await DEL(`/templates/${b.dataset.del}`); load(); return "Template deleted"; },
        { title: "Delete template", confirmLabel: "Delete" }));
  }

  async function previewTpl(id) {
    let d;
    try { d = await POST(`/templates/${id}/preview`, {}); }
    catch (e) { toast(e.message, "err", 6000); return; }
    const node = el("div", {},
      (d.unknown_variables || []).length || (d.missing_variables || []).length || (d.warnings || []).length ?
        el("div", { class: "notice warn mb-1", html: `<span class="n-ico">⚠</span><div>` +
          [].concat(d.unknown_variables || []).map(v => `Unknown variable: <b>${esc(v)}</b>`).join("<br>") +
          [].concat(d.missing_variables || []).map(v => `Missing data: <b>${esc(v)}</b>`).join("<br>") +
          [].concat(d.warnings || []).map(w => `Validation: ${esc(String(w))}`).join("<br>") + `</div>` }) : null,
      el("div", { class: "kv-inline" },
        d.generated && d.generated.invoice_number ? el("span", { class: "badge" }, esc(d.generated.invoice_number)) : null,
        d.generated && d.generated.amount != null ? el("span", { class: "badge" }, fmtMoney(d.generated.amount)) : null,
        d.generated && d.generated.shipping != null ? el("span", { class: "badge" }, "ship " + fmtMoney(d.generated.shipping)) : null),
      el("iframe", { class: "preview-frame", srcdoc: d.rendered_html }));
    openModal(`Preview — ${((all.find(t => t.id === id) || {}).name) || "#" + id}`, node, { xw: true });
  }

  async function testPdf(id) {
    toast("Rendering PDF via WeasyPrint…", "info");
    try {
      const d = await POST(`/templates/${id}/test-pdf`, {});
      const blob = b64Blob(d.pdf_base64, "application/pdf");
      dlBlob(blob, `template_${id}_test.pdf`);
      toast(`PDF generated — ${fmtBytes(d.size_bytes)} (${d.engine})`, "ok");
    } catch (e) { toast(e.message, "err", 6000); }
  }

  $("#fSearch").oninput = debounce(() => { q = $("#fSearch").value.trim(); render(); }, 250);
  $("#fSrc").onchange = () => { src = $("#fSrc").value; render(); };
  $("#btnClear").onclick = () =>
    confirmedAction("Clear ALL saved templates? Every template will be permanently deleted. Campaigns referencing them will fail validation.",
      async () => { const r = await POST("/templates/clear-all"); load(); return `Cleared ${r.deleted ?? 0} template(s)`; },
      { title: "Clear all templates", confirmLabel: "Delete everything" });

  load();
};
