// dashboard-renderplaybookshub.js — extracted verbatim from dashboard.js (pure move).

// ─────────────────────────────────────────────────────────────────
//
// A Playbook is an owner-authored, persisted bundle (e.g. "Promote Market
// Makers", "Promote Event X") with a brief (target profile, brand voice,
// compliance posture). Slice 1 = create + edit the brief. Attachments (the
// automations belonging to the mission), templates (install coin_mm etc.), and
// run-all land in later slices. Playbooks scope to the active workspace via the
// X-Workspace-Id header attached by the background DASHBOARD_ROUTES dispatcher.

// The brief fields surfaced in the editor. Free-form text — no automation
// logic depends on them in Slice 1; they feed AI drafters (brand voice) and
// give the operator one place to write down what this mission is.
const PLAYBOOK_BRIEF_FIELDS = [
  { key: "goal", label: "Goal", placeholder: "What is this mission trying to achieve?" },
  {
    key: "target_profile",
    label: "Target profile",
    placeholder: "Who are we reaching? (e.g. low-liquidity coins needing market making)",
  },
  { key: "pitch", label: "Pitch / offer", placeholder: "Your offer in one line" },
  {
    key: "brand_voice",
    label: "Brand voice",
    placeholder: "Tone for AI-drafted messages (overrides the workspace voice)",
  },
  {
    key: "compliance_posture",
    label: "Compliance posture",
    placeholder: "Approval-gated? Disclosure? Volume caps? Anything off-limits?",
  },
];

async function renderPlaybooksHub() {
  const wrapper = el("div", { class: "playbooks-hub", style: "padding:16px;" });
  setContentNode(wrapper);

  const newName = el("input", {
    type: "text",
    placeholder: "New playbook name (e.g. Promote Market Makers)",
    style:
      "flex:1;min-width:200px;padding:7px 10px;background:#0d0d12;border:1px solid #2a2a38;border-radius:6px;color:#eee;font-size:13px;",
    onkeydown: (e) => {
      if (e.key === "Enter") createBtn.click();
    },
  });
  const createBtn = el(
    "button",
    {
      class: "btn btn-primary btn-tiny",
      onclick: async (e) => {
        const name = newName.value.trim();
        if (!name) {
          toast("Give the playbook a name first", "err");
          return;
        }
        await withProgress(e.target, async () => {
          const r = await api("playbook-create", { body: { name } });
          if (r?.error) {
            toast(r.error, "err");
            return;
          }
          toast("Playbook created", "ok");
          navigate("playbooks/" + r.playbook.id);
        });
      },
    },
    "+ Create",
  );

  const installBtn = el("button", { class: "btn btn-secondary btn-tiny" }, "📥 Install template");
  wrapper.appendChild(
    el("div", { class: "page-header", style: "margin-bottom:10px;" }, [
      el("h1", { style: "margin:0;font-size:18px;" }, "📦 Playbooks"),
      el(
        "p",
        { class: "muted", style: "margin:4px 0 0;font-size:12px;" },
        'A playbook is a mission section — e.g. "Promote Market Makers" — that bundles the customized automations for that goal in one place.',
      ),
      el(
        "div",
        { style: "display:flex;gap:8px;align-items:center;margin-top:12px;flex-wrap:wrap;" },
        [newName, createBtn, installBtn],
      ),
    ]),
  );

  // Template picker — install a ready-made playbook (the 8 Goals, vertical
  // bundles like Market Makers, and your saved templates) in one click.
  const tplPanel = el("div", {
    style:
      "display:none;margin-top:10px;padding:12px;border:1px solid #2a2a38;border-radius:8px;background:#13131a;",
  });
  wrapper.appendChild(tplPanel);
  installBtn.onclick = async () => {
    if (tplPanel.style.display === "block") {
      tplPanel.style.display = "none";
      return;
    }
    tplPanel.style.display = "block";
    tplPanel.innerHTML = "";
    tplPanel.appendChild(
      el("div", { class: "muted", style: "font-size:12px;" }, "Loading templates…"),
    );
    const tr = await api("playbook-templates");
    tplPanel.innerHTML = "";
    if (!tr || tr.error) {
      tplPanel.appendChild(
        el("div", { class: "muted", style: "font-size:12px;" }, tr?.error || "Couldn't load"),
      );
      return;
    }
    const SRC_LABEL = { builtin: "Goal", file: "Bundle", user: "Saved" };
    // Group templates by category for a scannable picker.
    const byCat = new Map();
    for (const t of tr.templates || []) {
      const cat = t.category || "Other";
      if (!byCat.has(cat)) byCat.set(cat, []);
      byCat.get(cat).push(t);
    }
    const renderTplRow = (t) =>
      el("div", { style: "display:flex;align-items:center;gap:8px;padding:6px 0;" }, [
        el("div", { style: "font-size:16px;" }, t.icon || "📦"),
        el("div", { style: "flex:1;min-width:0;" }, [
          el("div", { style: "font-size:13px;font-weight:500;" }, [
            t.name,
            el(
              "span",
              {
                style:
                  "font-size:10px;color:#8a8a9a;margin-left:6px;padding:1px 6px;border:1px solid #2a2a38;border-radius:8px;",
              },
              SRC_LABEL[t.source] || t.source,
            ),
          ]),
          el(
            "div",
            { class: "muted", style: "font-size:11px;line-height:1.3;" },
            `${t.goal || ""} · ${t.step_count} automation(s)`,
          ),
        ]),
        el(
          "button",
          {
            class: "btn btn-primary btn-tiny",
            onclick: async (e) => {
              await withProgress(e.target, async () => {
                const r = await api("playbook-install", { body: { template_id: t.id } });
                if (r?.error) {
                  toast(r.error, "err");
                  return;
                }
                const made = (r.created || []).length;
                toast(`Installed${made ? ` — ${made} automation(s) set up as drafts` : ""}`, "ok");
                navigate("playbooks/" + r.playbook.id);
              });
            },
          },
          "Install",
        ),
      ]);
    for (const [cat, items] of byCat) {
      tplPanel.appendChild(
        el(
          "div",
          {
            style:
              "font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.04em;color:#7a7a8a;margin:12px 0 2px;",
          },
          cat,
        ),
      );
      for (const t of items) tplPanel.appendChild(renderTplRow(t));
    }
    if (!(tr.templates || []).length)
      tplPanel.appendChild(
        el("div", { class: "muted", style: "font-size:12px;" }, "No templates available."),
      );
  };

  _accountRiskBanner()
    .then((b) => {
      if (b) wrapper.insertBefore(b, wrapper.children[1] || null);
    })
    .catch(() => {});

  const grid = el("div", {
    style:
      "display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px;margin-top:14px;",
  });
  wrapper.appendChild(grid);
  grid.appendChild(el("div", { class: "muted", style: "font-size:12px;" }, "Loading playbooks…"));

  let res = null;
  try {
    res = await api("playbooks-list");
  } catch (err) {
    grid.innerHTML = "";
    grid.appendChild(
      el("div", { class: "empty-state", style: "padding:24px;" }, [
        el("div", { class: "empty-state-icon" }, "⚠️"),
        el("div", { class: "empty-state-title" }, "Couldn't reach the bot"),
        el(
          "div",
          { class: "empty-state-hint" },
          `${err.message}. If you just updated the extension, reload it at chrome://extensions (the background service worker may be stale), then make sure the Nexus bot is running.`,
        ),
      ]),
    );
    return;
  }
  grid.innerHTML = "";
  if (!res || res.error) {
    grid.appendChild(
      el("div", { class: "empty-state", style: "padding:24px;" }, [
        el("div", { class: "empty-state-icon" }, "⚠️"),
        el("div", { class: "empty-state-title" }, "Couldn't load playbooks"),
        el("div", { class: "empty-state-hint" }, res?.error || "unknown error"),
      ]),
    );
    return;
  }
  const playbooks = res.playbooks || [];
  if (!playbooks.length) {
    grid.appendChild(
      el("div", { class: "empty-state", style: "padding:24px;" }, [
        el("div", { class: "empty-state-icon" }, "📦"),
        el("div", { class: "empty-state-title" }, "No playbooks yet"),
        el(
          "div",
          { class: "empty-state-hint" },
          "Name one above and hit Create. Then add the automations for that mission to it.",
        ),
      ]),
    );
    return;
  }
  for (const p of playbooks) {
    grid.appendChild(
      el(
        "div",
        {
          class: "card",
          style:
            "border:1px solid #2a2a38;border-radius:10px;padding:14px;cursor:pointer;background:#13131a;",
          onclick: () => navigate("playbooks/" + p.id),
        },
        [
          el("div", { style: "font-size:22px;" }, p.icon || "📦"),
          el("div", { style: "font-weight:600;margin-top:6px;font-size:14px;" }, p.name),
          el(
            "div",
            { class: "muted", style: "font-size:12px;margin-top:4px;line-height:1.4;" },
            (p.brief && p.brief.goal) || "No brief yet — click to set it up.",
          ),
          _playbookRollupLine(p),
        ],
      ),
    );
  }
}

// Small "🟢 2 running · ⏸ 1 draft" summary line for a hub card.
function _playbookRollupLine(p) {
  const rs = p.run_state || {};
  const n = p.attachment_count || 0;
  if (!n)
    return el(
      "div",
      { style: "font-size:11px;color:#6a6a78;margin-top:8px;" },
      "No automations yet",
    );
  const parts = [];
  if (rs.running) parts.push(`🟢 ${rs.running} running`);
  if (rs.draft) parts.push(`⏸ ${rs.draft} draft`);
  if (rs.missing) parts.push(`⚠️ ${rs.missing} missing`);
  if (!parts.length) parts.push(`${n} automation${n > 1 ? "s" : ""}`);
  return el("div", { style: "font-size:11px;color:#8a8a9a;margin-top:8px;" }, parts.join(" · "));
}

// Display metadata for each attachment kind.
const PLAYBOOK_KIND_META = {
  custom_workflow: { icon: "🧩", label: "Scheduled automations" },
  composition: { icon: "🛠", label: "Workflows" },
  boost: { icon: "🚀", label: "Boosts" },
  campaign: { icon: "📣", label: "Campaigns" },
  note: { icon: "📌", label: "Notes" },
};

// Status pill — green for running/active, amber for draft/paused, grey else.
function _playbookStatusPill(status) {
  if (!status) return null;
  const s = String(status);
  const running = s === "active";
  const idle = s === "draft" || s === "paused";
  const style = running
    ? "background:#143d2a;color:#4ade80;"
    : idle
      ? "background:#3a3320;color:#c9a96a;"
      : "background:#22222c;color:#9a9aa8;";
  const label = running ? "running" : idle ? s : s;
  return el("span", { style: `font-size:10px;padding:1px 7px;border-radius:8px;${style}` }, label);
}

async function renderPlaybookDetail(params = {}) {
  const id = params.id;
  const root = el("div", { class: "playbook-detail", style: "padding:16px;max-width:840px;" });
  setContentNode(root);
  root.appendChild(
    el(
      "button",
      { class: "btn btn-secondary btn-tiny", onclick: () => navigate("playbooks") },
      "« All playbooks",
    ),
  );
  const mount = el("div", { style: "margin-top:12px;" }, [
    el("div", { class: "muted" }, "Loading…"),
  ]);
  root.appendChild(mount);

  async function load() {
    const res = await api("playbook-get", { id });
    mount.innerHTML = "";
    if (!res || res.error) {
      mount.appendChild(el("div", { class: "muted" }, `Couldn't load: ${res?.error || "error"}`));
      return;
    }
    const p = res.playbook;
    const briefState = { ...(p.brief || {}) };

    // Header: editable name + icon.
    const nameInput = el("input", {
      type: "text",
      value: p.name,
      style:
        "font-size:20px;font-weight:600;background:transparent;border:none;border-bottom:1px solid transparent;color:#eee;flex:1;min-width:160px;padding:2px 0;",
      onfocus: (e) => {
        e.target.style.borderBottomColor = "#2a2a38";
      },
      onblur: async (e) => {
        e.target.style.borderBottomColor = "transparent";
        const v = e.target.value.trim();
        if (v && v !== p.name) {
          await api("playbook-patch", { id, body: { name: v } });
          p.name = v;
          toast("Renamed", "ok");
        }
      },
    });
    const iconInput = el("input", {
      type: "text",
      value: p.icon || "📦",
      maxlength: "4",
      style:
        "width:44px;text-align:center;font-size:20px;background:#0d0d12;border:1px solid #2a2a38;border-radius:6px;color:#eee;padding:4px;",
      onblur: async (e) => {
        const v = e.target.value.trim() || "📦";
        if (v !== p.icon) {
          await api("playbook-patch", { id, body: { icon: v } });
          p.icon = v;
        }
      },
    });
    mount.appendChild(
      el("div", { style: "display:flex;align-items:center;gap:10px;" }, [iconInput, nameInput]),
    );

    // Brief editor — one place to write down what this mission is.
    const form = el("div", {
      style:
        "margin-top:14px;padding:12px;border:1px solid #2a2a38;border-radius:8px;background:#13131a;",
    });
    form.appendChild(
      el("div", { style: "font-weight:600;font-size:13px;margin-bottom:10px;" }, "Brief"),
    );
    for (const f of PLAYBOOK_BRIEF_FIELDS) {
      form.appendChild(
        el(
          "label",
          { style: "font-size:11px;color:#9a9aa8;display:block;margin-bottom:2px;" },
          f.label,
        ),
      );
      form.appendChild(
        el("textarea", {
          rows: f.key === "goal" || f.key === "target_profile" ? "2" : "1",
          placeholder: f.placeholder,
          style:
            "width:100%;margin-bottom:10px;padding:6px 8px;background:#0d0d12;border:1px solid #2a2a38;border-radius:6px;color:#eee;font-size:12px;box-sizing:border-box;resize:vertical;font-family:inherit;",
          value: briefState[f.key] || "",
          oninput: (e) => {
            briefState[f.key] = e.target.value;
          },
        }),
      );
    }
    form.appendChild(
      el(
        "button",
        {
          class: "btn btn-primary btn-tiny",
          onclick: async (e) => {
            await withProgress(e.target, async () => {
              const r = await api("playbook-patch", { id, body: { brief: briefState } });
              if (r?.error) toast(r.error, "err");
              else toast("Brief saved", "ok");
            });
          },
        },
        "Save brief",
      ),
    );
    mount.appendChild(form);

    // ── Template settings strip + setup steps + live status (template-backed) ──
    const tpl = res.template;
    if (tpl) {
      const settingsState = { ...(p.settings || {}) };
      // Setup steps (e.g. coin_mm CSV import) — surfaced so the previously
      // hidden prerequisite is visible.
      if ((tpl.setup_steps || []).length) {
        const su = el("div", {
          style:
            "margin-top:14px;padding:12px;border:1px solid #3a3320;border-radius:8px;background:#1c1812;",
        });
        su.appendChild(
          el("div", { style: "font-weight:600;font-size:13px;margin-bottom:6px;" }, "⚙️ Setup"),
        );
        for (const s of tpl.setup_steps) {
          su.appendChild(
            el("div", { style: "margin-bottom:6px;" }, [
              el("div", { style: "font-size:12px;font-weight:500;" }, s.label),
              s.detail
                ? el(
                    "div",
                    { class: "muted", style: "font-size:11px;line-height:1.4;margin-top:1px;" },
                    s.detail,
                  )
                : null,
            ]),
          );
        }
        mount.appendChild(su);
      }
      // Settings toggles (mirror to the engine's KV keys server-side).
      if ((tpl.settings_schema || []).length) {
        const sb = el("div", {
          style:
            "margin-top:14px;padding:12px;border:1px solid #2a2a38;border-radius:8px;background:#13131a;",
        });
        sb.appendChild(
          el("div", { style: "font-weight:600;font-size:13px;margin-bottom:8px;" }, "Settings"),
        );
        for (const f of tpl.settings_schema) {
          const cur = settingsState[f.key] !== undefined ? settingsState[f.key] : f.default;
          const rowEl = el("div", {
            style: "display:flex;align-items:flex-start;gap:10px;padding:6px 0;",
          });
          if (f.type === "bool") {
            const cb = el("input", { type: "checkbox", style: "margin-top:2px;" });
            cb.checked = !!cur;
            cb.onchange = async () => {
              settingsState[f.key] = cb.checked;
              const r = await api("playbook-patch", {
                id,
                body: { settings: { [f.key]: cb.checked } },
              });
              if (r?.error) {
                toast(r.error, "err");
                cb.checked = !cb.checked;
              } else toast("Saved", "ok");
            };
            rowEl.appendChild(cb);
          } else {
            const inp = el("input", {
              type: "number",
              value: cur != null ? String(cur) : "",
              style:
                "width:90px;padding:4px 6px;background:#0d0d12;border:1px solid #2a2a38;border-radius:6px;color:#eee;font-size:12px;",
              onchange: async (e) => {
                const val = Number(e.target.value);
                settingsState[f.key] = val;
                const r = await api("playbook-patch", { id, body: { settings: { [f.key]: val } } });
                if (r?.error) toast(r.error, "err");
                else toast("Saved", "ok");
              },
            });
            rowEl.appendChild(inp);
          }
          rowEl.appendChild(
            el("div", { style: "flex:1;" }, [
              el("div", { style: "font-size:12px;font-weight:500;" }, f.label),
              f.hint
                ? el("div", { class: "muted", style: "font-size:11px;line-height:1.4;" }, f.hint)
                : null,
            ]),
          );
          sb.appendChild(rowEl);
        }
        if (tpl.live_status_route) {
          sb.appendChild(
            el(
              "a",
              {
                href: "#" + tpl.live_status_route,
                style: "display:inline-block;margin-top:6px;font-size:12px;color:#6aa9ff;",
              },
              "View live status →",
            ),
          );
        }
        mount.appendChild(sb);
      } else if (tpl.live_status_route) {
        mount.appendChild(
          el("div", { style: "margin-top:12px;" }, [
            el(
              "a",
              { href: "#" + tpl.live_status_route, style: "font-size:12px;color:#6aa9ff;" },
              "View live status →",
            ),
          ]),
        );
      }
    }

    // ── Configure: structured inputs that fill the params of automations you
    // add (rival handle, hashtag, event slug, your pitch, …). Saved to the
    // brief; applied at add-time so installed automations are run-ready.
    if (res.template && (res.template.inputs || []).length) {
      const inp = { ...((p.brief && p.brief.inputs) || {}) };
      const f = el("div", {
        style:
          "margin-top:14px;padding:12px;border:1px solid #2a3550;border-radius:8px;background:#11131c;",
      });
      f.appendChild(el("div", { style: "font-weight:600;font-size:13px;" }, "⚙ Configure"));
      f.appendChild(
        el(
          "div",
          { class: "muted", style: "font-size:11px;margin:2px 0 8px;" },
          "Fills the automations you add below. Set these first, then add from the menu.",
        ),
      );
      for (const q of res.template.inputs) {
        f.appendChild(
          el(
            "label",
            { style: "font-size:11px;color:#9a9aa8;display:block;margin-bottom:2px;" },
            q.label || q.key,
          ),
        );
        f.appendChild(
          el("input", {
            type: "text",
            value: inp[q.key] || "",
            placeholder: q.placeholder || "",
            style:
              "width:100%;margin-bottom:8px;padding:6px 8px;background:#0d0d12;border:1px solid #2a2a38;border-radius:6px;color:#eee;font-size:12px;box-sizing:border-box;",
            oninput: (e) => {
              inp[q.key] = e.target.value;
            },
          }),
        );
      }
      f.appendChild(
        el(
          "button",
          {
            class: "btn btn-secondary btn-tiny",
            onclick: async (e) => {
              await withProgress(e.target, async () => {
                await api("playbook-patch", { id, body: { brief: { inputs: inp } } });
              });
              toast("Saved — applies to automations you add", "ok");
            },
          },
          "Save config",
        ),
      );
      mount.appendChild(f);
    }

    // ── Goal menu: EVERY automation relevant to this goal, grouped by funnel
    // stage, each with its live state + add/enable. This is the "pick a goal →
    // see all the automations" surface. Shown when the playbook has a goal.
    if (res.catalog) {
      const cat = res.catalog;
      const RISK = { green: "🟢", yellow: "🟡", orange: "🟠", red: "🔴" };

      const previewBtn = el(
        "button",
        {
          class: "btn btn-secondary btn-tiny",
          onclick: async () => {
            const pv = await api("playbook-preview", { id });
            if (pv?.error) {
              toast(pv.error, "err");
              return;
            }
            const lines = (pv.plan || [])
              .map((p) => `${p.will_run ? "▶" : "—"} ${p.name} — ${p.note}`)
              .join("\n");
            alert(
              `Run all will execute ${pv.runnable}/${pv.total} now:\n\n${lines || "(nothing runnable yet)"}`,
            );
          },
        },
        "🔎 Preview",
      );
      const runBtn = el(
        "button",
        {
          class: "btn btn-primary btn-tiny",
          onclick: async (e) => {
            if (!confirm("Run all activated automations in this playbook now?")) return;
            await withProgress(e.target, async () => {
              const r = await api("playbook-run", { id });
              if (r?.error) toast(r.error, "err");
              else
                toast(
                  `Ran ${r.ran}${r.failed ? `, ${r.failed} failed` : ""}`,
                  r.failed ? "err" : "ok",
                  5000,
                );
            });
            load();
          },
        },
        "▶ Run all",
      );

      const recPending = [];
      for (const s of cat.stages)
        for (const it of s.items)
          if (it.recommended && it.addable && it.state === "not_added") recPending.push(it.id);
      const recBtn = recPending.length
        ? el(
            "button",
            {
              class: "btn btn-primary btn-tiny",
              onclick: async (e) => {
                await withProgress(e.target, async () => {
                  let ok = 0;
                  for (const itemId of recPending) {
                    const r = await api("playbook-catalog-add", { id, body: { item_id: itemId } });
                    if (!r?.error) ok++;
                  }
                  toast(`Added ${ok} recommended automation(s) as drafts`, ok ? "ok" : "err");
                });
                load();
              },
            },
            `⭐ Install recommended (${recPending.length})`,
          )
        : null;

      const csec = el("div", { style: "margin-top:18px;" });
      csec.appendChild(
        el(
          "div",
          { style: "display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:4px;" },
          [
            el(
              "div",
              { style: "font-weight:600;font-size:14px;flex:1;" },
              "Automations for this goal",
            ),
            recBtn,
            previewBtn,
            runBtn,
          ],
        ),
      );
      csec.appendChild(
        el(
          "div",
          { class: "muted", style: "font-size:11px;margin-bottom:8px;" },
          `${cat.total} automations · ${cat.recommended_count} recommended. ⭐ = starter · added items install as drafts (off until you Enable).`,
        ),
      );

      const renderCatalogItem = (it) => {
        const row = el("div", {
          style:
            "display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid #22222c;border-radius:8px;margin-bottom:6px;background:#101015;",
        });
        row.appendChild(
          el("div", { style: "font-size:14px;", title: it.risk }, RISK[it.risk] || "•"),
        );
        const main = el("div", { style: "flex:1;min-width:0;" }, [
          el("div", { style: "font-size:13px;font-weight:500;" }, [
            it.recommended ? el("span", { title: "Recommended starter" }, "⭐ ") : "",
            it.name,
            el("span", { style: "font-size:10px;color:#7a7a8a;margin-left:6px;" }, it.platform),
          ]),
          it.note
            ? el("div", { class: "muted", style: "font-size:11px;line-height:1.3;" }, it.note)
            : null,
        ]);
        row.appendChild(main);

        // State pill
        const pill = (txt, bg) =>
          el("span", { style: `font-size:10px;padding:1px 7px;border-radius:8px;${bg}` }, txt);
        if (it.state === "running")
          row.appendChild(pill("running", "background:#143d2a;color:#4ade80;"));
        else if (it.state === "draft")
          row.appendChild(pill("draft", "background:#3a3320;color:#c9a96a;"));
        if (it.needs_config)
          row.appendChild(pill("⚙ needs config", "background:#3a2030;color:#e0a0b0;"));

        const actions = el("div", { style: "display:flex;gap:4px;align-items:center;" });
        if (it.state === "not_added" && it.addable) {
          actions.appendChild(
            el(
              "button",
              {
                class: "btn btn-primary btn-tiny",
                onclick: async (e) => {
                  await withProgress(e.target, async () => {
                    const r = await api("playbook-catalog-add", { id, body: { item_id: it.id } });
                    if (r?.error) toast(r.error, "err");
                    else toast("Added as a draft — Enable it to start", "ok");
                  });
                  load();
                },
              },
              "+ Add",
            ),
          );
        } else if (it.state === "draft") {
          if (it.install?.type === "preset" && it.ref_id) {
            actions.appendChild(
              el(
                "button",
                {
                  class: "btn btn-primary btn-tiny",
                  onclick: async (e) => {
                    await withProgress(e.target, async () => {
                      const r = await api("automation-update-custom", {
                        customId: it.ref_id,
                        body: { status: "active" },
                      });
                      if (r?.error) toast(r.error, "err");
                      else toast("Enabled — runs on schedule", "ok");
                    });
                    load();
                  },
                },
                "▶ Enable",
              ),
            );
          } else if (it.deep_link || it.ref_id) {
            actions.appendChild(
              el(
                "button",
                {
                  class: "btn btn-secondary btn-tiny",
                  onclick: () => navigate(`workflows/canvas/${it.ref_id}`),
                },
                "Open ▸",
              ),
            );
          }
        } else if (it.state === "control") {
          actions.appendChild(
            el(
              "button",
              { class: "btn btn-secondary btn-tiny", onclick: () => navigate(it.install.deepLink) },
              "Control ▸",
            ),
          );
        } else if (it.state === "external") {
          actions.appendChild(
            el(
              "button",
              { class: "btn btn-secondary btn-tiny", onclick: () => navigate(it.install.deepLink) },
              "Open ▸",
            ),
          );
        }
        // Remove for anything added to this playbook.
        if (it.attach_id) {
          actions.appendChild(
            el(
              "button",
              {
                class: "btn btn-secondary btn-tiny",
                onclick: async (e) => {
                  await withProgress(e.target, async () => {
                    const r = await api("playbook-detach", { id, attachId: it.attach_id });
                    if (r?.error) toast(r.error, "err");
                    else toast("Removed", "ok");
                  });
                  load();
                },
              },
              "✕",
            ),
          );
        }
        row.appendChild(actions);
        return row;
      };

      for (const stage of cat.stages) {
        csec.appendChild(
          el(
            "div",
            {
              style:
                "font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.04em;color:#7a7a8a;margin:14px 0 4px;",
            },
            `${stage.icon} ${stage.label}`,
          ),
        );
        for (const it of stage.items) csec.appendChild(renderCatalogItem(it));
      }
      mount.appendChild(csec);
    }

    // ── Attached automations (fallback bundle view for playbooks with no goal) ──
    if (!res.catalog) {
      const groups = res.attachment_groups || {};
      const sec = el("div", { style: "margin-top:18px;" });
      const head = el(
        "div",
        { style: "display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap;" },
        [el("div", { style: "font-weight:600;font-size:14px;flex:1;" }, "Attached automations")],
      );
      const hasRunnable = (res.attachments || []).some(
        (a) => a.kind === "custom_workflow" && !a.missing,
      );
      if (hasRunnable) {
        head.appendChild(
          el(
            "button",
            {
              class: "btn btn-secondary btn-tiny",
              onclick: async (e) => {
                const pv = await api("playbook-preview", { id });
                if (pv?.error) {
                  toast(pv.error, "err");
                  return;
                }
                const lines = (pv.plan || [])
                  .map((p) => `${p.will_run ? "▶" : "—"} ${p.name} — ${p.note}`)
                  .join("\n");
                alert(`Run all will execute ${pv.runnable}/${pv.total} now:\n\n${lines}`);
              },
            },
            "🔎 Preview",
          ),
        );
        head.appendChild(
          el(
            "button",
            {
              class: "btn btn-primary btn-tiny",
              onclick: async (e) => {
                if (!confirm("Run all Scheduled automations in this playbook now?")) return;
                await withProgress(e.target, async () => {
                  const r = await api("playbook-run", { id });
                  if (r?.error) toast(r.error, "err");
                  else
                    toast(
                      `Ran ${r.ran}${r.failed ? `, ${r.failed} failed` : ""}`,
                      r.failed ? "err" : "ok",
                      5000,
                    );
                });
                load();
              },
            },
            "▶ Run all",
          ),
        );
      }
      const addBtn = el("button", { class: "btn btn-primary btn-tiny" }, "+ Add automation");
      head.appendChild(addBtn);
      sec.appendChild(head);

      // Inline picker panel (hidden until "+ Add automation" is clicked).
      const picker = el("div", {
        style:
          "display:none;margin-bottom:10px;padding:10px;border:1px solid #2a2a38;border-radius:8px;background:#13131a;",
      });
      sec.appendChild(picker);
      addBtn.onclick = async () => {
        if (picker.style.display === "block") {
          picker.style.display = "none";
          return;
        }
        picker.style.display = "block";
        picker.innerHTML = "";
        picker.appendChild(
          el("div", { class: "muted", style: "font-size:12px;" }, "Loading your automations…"),
        );
        const ar = await api("playbook-attachable", { id });
        picker.innerHTML = "";
        if (!ar || ar.error) {
          picker.appendChild(
            el("div", { class: "muted", style: "font-size:12px;" }, ar?.error || "Couldn't load"),
          );
          return;
        }
        const att = ar.attachable || {};
        let any = false;
        for (const kind of ["custom_workflow", "composition", "boost"]) {
          const rows = att[kind] || [];
          if (!rows.length) continue;
          any = true;
          const meta = PLAYBOOK_KIND_META[kind] || { icon: "•", label: kind };
          picker.appendChild(
            el(
              "div",
              { style: "font-size:11px;color:#9a9aa8;margin:6px 0 3px;" },
              `${meta.icon} ${meta.label}`,
            ),
          );
          for (const r of rows) {
            picker.appendChild(
              el("div", { style: "display:flex;align-items:center;gap:8px;padding:5px 0;" }, [
                el("div", { style: "flex:1;font-size:12px;" }, r.name || `#${r.ref_id}`),
                _playbookStatusPill(r.status),
                el(
                  "button",
                  {
                    class: "btn btn-secondary btn-tiny",
                    onclick: async (e) => {
                      await withProgress(e.target, async () => {
                        const rr = await api("playbook-attach", {
                          id,
                          body: { kind, ref_id: r.ref_id },
                        });
                        if (rr?.error) toast(rr.error, "err");
                        else toast("Added", "ok");
                      });
                      load();
                    },
                  },
                  "Add",
                ),
              ]),
            );
          }
        }
        if (!any) {
          picker.appendChild(
            el("div", { class: "muted", style: "font-size:12px;" }, [
              "Nothing left to add. Create automations first — ",
              el("a", { href: "#automations", style: "color:#6aa9ff;" }, "Run now"),
              " or ",
              el("a", { href: "#workflows", style: "color:#6aa9ff;" }, "Scheduled"),
              ".",
            ]),
          );
        }
      };

      // Grouped list of what's attached.
      const items = res.attachments || [];
      if (!items.length) {
        sec.appendChild(
          el(
            "div",
            {
              style: "padding:14px;border:1px dashed #2a2a38;border-radius:8px;background:#0f0f15;",
            },
            [
              el(
                "div",
                { class: "muted", style: "font-size:12px;" },
                "No automations attached yet. Click “+ Add automation” to bundle the pieces of this mission together.",
              ),
            ],
          ),
        );
      } else {
        for (const kind of Object.keys(groups)) {
          const rows = groups[kind] || [];
          if (!rows.length) continue;
          const meta = PLAYBOOK_KIND_META[kind] || { icon: "•", label: kind };
          sec.appendChild(
            el(
              "div",
              { style: "font-size:11px;color:#9a9aa8;margin:10px 0 4px;" },
              `${meta.icon} ${meta.label}`,
            ),
          );
          for (const a of rows) {
            const row = el("div", {
              style:
                "display:flex;align-items:center;gap:8px;padding:9px 10px;border:1px solid #22222c;border-radius:8px;margin-bottom:6px;background:#101015;",
            });
            row.appendChild(
              el("div", { style: "flex:1;font-size:13px;" }, [
                a.missing
                  ? el("span", { style: "color:#e06a6a;" }, `${a.name || a.ref_id} (deleted)`)
                  : a.name || `#${a.ref_id}`,
              ]),
            );
            if (!a.missing) {
              const pill = _playbookStatusPill(a.status);
              if (pill) row.appendChild(pill);
            }
            if (!a.missing && a.deep_link) {
              row.appendChild(
                el(
                  "button",
                  { class: "btn btn-secondary btn-tiny", onclick: () => navigate(a.deep_link) },
                  "Open",
                ),
              );
            }
            row.appendChild(
              el(
                "button",
                {
                  class: "btn btn-secondary btn-tiny",
                  onclick: async (e) => {
                    await withProgress(e.target, async () => {
                      const rr = await api("playbook-detach", { id, attachId: a.id });
                      if (rr?.error) toast(rr.error, "err");
                      else toast("Removed", "ok");
                    });
                    load();
                  },
                },
                "Remove",
              ),
            );
            sec.appendChild(row);
          }
        }
      }
      mount.appendChild(sec);
    }

    // Save as template + Archive.
    mount.appendChild(
      el("div", { style: "margin-top:18px;display:flex;gap:8px;flex-wrap:wrap;" }, [
        el(
          "button",
          {
            class: "btn btn-secondary btn-tiny",
            onclick: async (e) => {
              await withProgress(e.target, async () => {
                const r = await api("playbook-save-template", { id });
                if (r?.error) toast(r.error, "err");
                else
                  toast(
                    `Saved as a reusable template${r.step_count ? ` (${r.step_count} automation(s))` : ""}`,
                    "ok",
                  );
              });
            },
          },
          "💾 Save as template",
        ),
        el(
          "button",
          {
            class: "btn btn-secondary btn-tiny",
            onclick: async (e) => {
              if (!confirm(`Archive "${p.name}"? It will be hidden from the list.`)) return;
              await withProgress(e.target, async () => {
                const r = await api("playbook-delete", { id });
                if (r?.error) toast(r.error, "err");
                else {
                  toast("Archived", "ok");
                  navigate("playbooks");
                }
              });
            },
          },
          "Archive playbook",
        ),
      ]),
    );
  }

  await load();
}
