window.pageBoot = async (me) => {
  const fmtDT = window.fmtDT || (s => s || "—");
  let templates = [];

  async function load(){
    const d = await GET("/templates/list");
    templates = d.templates || [];
    $("#count").textContent = String(templates.length);
    $("#empty").style.display = templates.length ? "none" : "";
    $("#body").innerHTML = templates.map(t => `
      <tr>
        <td><b>${esc(t.name)}</b></td>
        <td class="mut" style="max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(t.subject_hint || "—")}</td>
        <td><span class="badge b-gray">${esc(t.source)}</span></td>
        <td>${(t.size_html/1024).toFixed(1)} KB</td>
        <td class="mut" style="font-size:.78rem;max-width:240px">${esc((t.variables||[]).join(", ") || "—")}</td>
        <td>${(t.unknown_variables||[]).length ? `<span class="badge b-amber" title="${esc((t.unknown_variables||[]).join(", "))}">${(t.unknown_variables||[]).length} unknown</span>` : "—"}</td>
        <td class="mut" style="font-size:.8rem">${esc(fmtDT(t.created_at))}</td>
        <td>
          <div class="btns" style="gap:6px">
            <button class="btn xs" data-preview="${t.id}">Preview</button>
            <button class="btn xs secondary" data-edit="${t.id}">Edit</button>
            <button class="btn xs secondary" data-dup="${t.id}">Duplicate</button>
            <button class="btn xs warn" data-pdf="${t.id}">Test PDF</button>
            <button class="btn xs danger-o" data-del="${t.id}">Del</button>
          </div>
        </td>
      </tr>`).join("");
  }

  function editorModal(existing){
    const isEdit = !!existing;
    const nm = el("input", {class:"inp", value: existing ? existing.name : "", placeholder:"Invoice template — blue"});
    const sh = el("input", {class:"inp", value: existing ? existing.subject_hint : "", placeholder:"Optional subject hint for this template"});
    const html = el("textarea", {class:"inp code", placeholder:
`<html><body style="font-family:Arial,sans-serif">
  <p>Dear %FirstName%,</p>
  <p>Your invoice <b>%InvoiceNumber%</b> for <b>%TotalAmount%</b> is due on %DueDate%.</p>
  <p><a href="%UnsubscribeLink%">Unsubscribe</a></p>
</body></html>`});
    const txt = el("textarea", {class:"inp", rows:4, placeholder:"Optional plain-text alternative body"});
    const pv = el("div", {class:"pv-frame"}, el("div",{class:"mut",style:"padding:14px"},"Click “Render Preview” to see the sanitized template with sample variables substituted."));
    const frame = el("iframe");

    const buildPreview = () => {
      const sample = html.value
        .replace(/%FirstName%/g,"Alex").replace(/%LastName%/g,"Rivera")
        .replace(/%CompanyName%/g,"Acme Corp").replace(/%CompanyPhone%/g,"+1 555 0100")
        .replace(/%InvoiceNumber%/g,"INV-8492").replace(/%TotalAmount%/g,"$1,240.00")
        .replace(/%RandomTotal%/g,"$1,240.00").replace(/%RandomShippingCharge%/g,"$18.50")
        .replace(/%RandomInvoiceNumber%/g,"INV-8492")
        .replace(/%InvoiceDate%/g,"2025-01-15").replace(/%DueDate%/g,"2025-01-30")
        .replace(/%RandomName%/g,"Jordan Lee")
        .replace(/%UnsubscribeLink%/g,"#").replace(/%CustomerEmail%/g,"alex@example.com");
      pv.innerHTML = "";
      frame.srcdoc = sample;
      pv.appendChild(frame);
    };

    const form = el("div", {class:"form"},
      el("label",{}, "Template name"), nm,
      el("label",{}, "Subject hint"), sh,
      el("div", {class:"tpl-ed"},
        el("div", {}, el("label",{}, "HTML body"), html, el("label",{style:"margin-top:8px"}, "Plain text alternative"), txt),
        el("div", {}, el("label",{}, "Live preview"), pv)),
      el("div",{class:"btns"},
        el("button",{class:"btn secondary",type:"button",onclick:()=>buildPreview()},"Render Preview")));

    openModal(isEdit ? "Edit Template" : "New Template", form, {
      xw: true,
      buttons: [{ label: isEdit ? "Save Changes" : "Create Template", kind:"btn", onClick: async (close) => {
        if (!html.value.trim()) { toast("HTML body is required","err"); return; }
        if (/<script/i.test(html.value)) { toast("Script tags are not allowed","err"); return; }
        if (isEdit) await api(`/templates/${existing.id}`, { method:"PUT", body:{
          name:nm.value, body_html:html.value, body_text:txt.value, subject_hint:sh.value }});
        else await POST("/templates", { name:nm.value, body_html:html.value,
          body_text:txt.value, subject_hint:sh.value });
        toast(isEdit ? "Template saved" : "Template created", "ok");
        close(); await load();
      }}]
    });
  }

  $("#btnNew").addEventListener("click", () => editorModal(null));

  $("#btnUpload").addEventListener("click", () => {
    const f = el("input", {type:"file", accept:".html,.htm,.txt", required:"required"});
    const nm = el("input", {class:"inp", placeholder:"Name (defaults to filename)"});
    openModal("Upload Template", el("div",{class:"form"},
      el("label",{},"HTML file"), f, el("label",{},"Template name (optional)"), nm), {
      buttons: [{ label:"Upload", kind:"btn", onClick: async (close) => {
        if (!f.files[0]) return;
        const fd = new FormData(); fd.append("file", f.files[0]);
        if (nm.value.trim()) fd.append("name", nm.value.trim());
        try {
          const d = await api("/templates/upload", {method:"POST", body:fd});
          toast(`Uploaded as “${d.name}”`, "ok"); close(); await load();
        } catch (err) { toast("Upload failed: " + err.message, "err"); }
      }}]
    });
  });

  $("#btnUploadMulti").addEventListener("click", () => {
    const f = el("input", {type:"file", accept:".html,.htm,.txt", multiple:"multiple", required:"required"});
    openModal("Upload Multiple Templates", el("div",{class:"form"},
      el("p",{class:"mut",style:"margin-top:0;font-size:.85rem"},"Select multiple .html files. Each becomes a separate template; they will be sequenced by order of upload."),
      el("label",{},"Files"), f), {
      buttons: [{ label:"Upload All", kind:"btn", onClick: async (close) => {
        if (!f.files.length) return;
        const fd = new FormData();
        [...f.files].forEach(file => fd.append("files", file));
        try {
          const d = await api("/templates/upload-multiple", {method:"POST", body:fd});
          toast(`Uploaded ${d.added} templates`, "ok"); close(); await load();
        } catch (err) { toast("Upload failed: " + err.message, "err"); }
      }}]
    });
  });

  $("#btnClear").addEventListener("click", () => confirmedAction(
    "Delete ALL templates? This cannot be undone.", async () => {
      const d = await POST("/templates/clear-all");
      toast(`Deleted ${d.deleted} templates`, "ok"); await load();
    }));

  $("#body").addEventListener("click", async (e) => {
    const b = e.target.closest("button"); if (!b) return;
    const id = b.dataset.preview || b.dataset.edit || b.dataset.dup || b.dataset.pdf || b.dataset.del;
    if (b.dataset.preview) {
      try {
        const d = await POST(`/templates/${id}/preview`);
        const tname = (templates.find(t=>t.id==id)||{}).name || ("#" + id);
        const warns = (d.warnings && (d.warnings.errors || d.warnings.warnings)) || [];
        const unknown = d.unknown_variables || [];
        const node = el("div", {},
          (unknown.length ? el("div", {class:"notice warn", style:"font-size:.82rem;margin-bottom:10px"},
              "Unknown variables: " + esc(unknown.join(", "))) : null),
          (warns.length ? el("div", {class:"notice err", style:"font-size:.82rem;margin-bottom:10px"},
              "Validation: " + esc(warns.join("; "))) : null),
          el("div", {class:"pv-frame"}, el("iframe", {srcdoc: d.rendered_html})));
        openModal("Preview — " + tname, node, { xw:true, buttons:[{label:"Close",kind:"btn secondary",onClick:(c)=>c()}]});
      } catch (err) { toast("Preview failed: " + err.message, "err"); }
    }
    if (b.dataset.edit) {
      const d = await GET(`/templates/${id}`);
      editorModal(d);
    }
    if (b.dataset.dup) {
      const d = await POST(`/templates/${id}/duplicate`);
      toast(`Duplicated (new id ${d.id})`, "ok"); await load();
    }
    if (b.dataset.pdf) {
      b.disabled = true; b.textContent = "Rendering…";
      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 with ${d.engine} (${fmtBytes(d.size_bytes)}) — downloading`, "ok");
      } catch (err) { toast("PDF failed: " + err.message, "err"); }
      finally { b.disabled = false; b.textContent = "Test PDF"; }
    }
    if (b.dataset.del) confirmedAction("Delete this template?", async () => {
      await api(`/templates/${id}`, {method:"DELETE"}); toast("Deleted","ok"); await load();
    });
  });

  await load();
};
===== templates router =====
27:@router.get("/list")
42:@router.post("")
62:@router.post("/upload")
81:@router.post("/upload-multiple")
101:@router.get("/{template_id}")
113:@router.put("/{template_id}")
129:@router.post("/{template_id}/duplicate")
142:@router.delete("/{template_id}")
153:@router.post("/clear-all")
160:@router.post("/{template_id}/preview")
191:@router.post("/{template_id}/test-pdf")
