(() => {
  const escapeHtml = (value) => String(value ?? "")
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;");

  const questions = {
    "stage-flow": "业务如何按阶段推进",
    comparison: "两种业务方式有什么本质差异",
    "input-output": "输入经过什么处理形成什么输出",
    "layered-architecture": "能力由哪些层级共同构成",
    "deliverable-stack": "哪些要素共同组成可交付成果",
    "governance-gate": "什么条件通过后才允许继续",
    lifecycle: "如何通过反馈持续改进",
    scorecard: "是否具备进入下一阶段的条件"
  };

  const box = (x, y, w, h, label, fill = "#fff", stroke = "#15366d") => `
    <rect x="${x}" y="${y}" width="${w}" height="${h}" rx="8" fill="${fill}" stroke="${stroke}" stroke-width="3"/>
    <text x="${x + w / 2}" y="${y + h / 2 + 6}" text-anchor="middle" class="cv-label">${escapeHtml(label)}</text>`;

  const wrap = (type, inner, caption, question = questions[type], viewBox = "0 0 900 190") => `
    <div class="semantic-visual business-visual courseware-visual courseware-${type}"
         data-visual-type="${type}"
         data-business-question="${escapeHtml(question)}">
      <svg viewBox="${viewBox}" role="img" aria-label="${escapeHtml(caption)}">${inner}</svg>
      <div class="semantic-caption">${escapeHtml(caption)}</div>
    </div>`;

  const stageFlow = (labels, caption, question) => {
    const gap = 28;
    const width = Math.min(160, (820 - gap * (labels.length - 1)) / labels.length);
    const total = labels.length * width + (labels.length - 1) * gap;
    const start = (900 - total) / 2;
    const inner = labels.map((label, index) => {
      const x = start + index * (width + gap);
      const arrow = index < labels.length - 1
        ? `<path d="M${x + width} 91 H${x + width + gap - 9}" stroke="#1f5be0" stroke-width="6"/>
           <path d="M${x + width + gap - 16} 81 l11 10 -11 10" fill="none" stroke="#1f5be0" stroke-width="4"/>`
        : "";
      return `${box(x, 48, width, 86, label, index === labels.length - 1 ? "#eaf1ff" : "#fff")}
        <rect x="${x + 9}" y="29" width="40" height="34" rx="5" fill="#15366d"/>
        <text x="${x + 29}" y="53" text-anchor="middle" class="cv-step">${index + 1}</text>${arrow}`;
    }).join("");
    return wrap("stage-flow", inner, caption, question, "0 20 900 130");
  };

  const comparison = (payload, caption, question) => {
    const [leftTitle, leftItems, rightTitle, rightItems] = payload;
    const column = (x, title, items, highlight) => `
      <rect x="${x}" y="24" width="370" height="142" rx="10" fill="${highlight ? "#eef4ff" : "#f7f8fa"}" stroke="#15366d" stroke-width="3"/>
      <rect x="${x}" y="24" width="370" height="40" rx="10" fill="${highlight ? "#15366d" : "#40516b"}"/>
      <text x="${x + 185}" y="51" text-anchor="middle" class="cv-head-light">${escapeHtml(title)}</text>
      ${items.slice(0, 3).map((item, index) => `<circle cx="${x + 34}" cy="${86 + index * 31}" r="6" fill="${highlight ? "#1f5be0" : "#98a2b3"}"/>
        <text x="${x + 52}" y="${92 + index * 31}" class="cv-copy">${escapeHtml(item)}</text>`).join("")}`;
    return wrap("comparison", column(55, leftTitle, leftItems, false) + column(475, rightTitle, rightItems, true), caption, question);
  };

  const inputOutput = (payload, caption, question) => {
    const [inputs, core, outputs] = payload;
    const pills = (items, x) => items.slice(0, 3).map((item, index) => box(x, 12 + index * 52, 190, 40, item)).join("");
    return wrap("input-output", `
      ${pills(inputs, 42)}${pills(outputs, 668)}
      <path d="M232 96 H342 M558 96 H668" stroke="#1f5be0" stroke-width="6"/>
      <path d="M330 86 l12 10 -12 10 M656 86 l12 10 -12 10" fill="none" stroke="#1f5be0" stroke-width="4"/>
      ${box(342, 55, 216, 82, core, "#eaf1ff")}
      <text x="137" y="181" text-anchor="middle" class="cv-note">输入与约束</text>
      <text x="450" y="181" text-anchor="middle" class="cv-note">核心处理</text>
      <text x="763" y="181" text-anchor="middle" class="cv-note">交付与回写</text>`, caption, question);
  };

  const layeredArchitecture = (layers, caption, question) => wrap(
    "layered-architecture",
    layers.slice(0, 4).map((layer, index) => {
      const [name, detail] = Array.isArray(layer) ? layer : [`第 ${index + 1} 层`, layer];
      const y = 8 + index * 44;
      return `<rect x="92" y="${y}" width="716" height="40" rx="6" fill="${index === 0 ? "#eaf1ff" : index % 2 ? "#fff" : "#f4f6f8"}" stroke="#15366d" stroke-width="2.5"/>
        <rect x="92" y="${y}" width="164" height="40" rx="6" fill="#15366d"/>
        <text x="174" y="${y + 30}" text-anchor="middle" class="cv-head-light">${escapeHtml(name)}</text>
        <text x="276" y="${y + 30}" class="cv-copy">${escapeHtml(detail)}</text>`;
    }).join(""),
    caption,
    question
  );

  const deliverableStack = (payload, caption, question) => {
    const [items, output] = payload;
    const columns = items.length > 4 ? 3 : 2;
    return wrap("deliverable-stack", `
      <rect x="28" y="8" width="574" height="168" rx="10" fill="#f8fafc" stroke="#98a2b3" stroke-width="2" stroke-dasharray="7 7"/>
      ${items.slice(0, 6).map((item, index) => box(46 + (index % columns) * 184, 24 + Math.floor(index / columns) * 70, 160, 50, item)).join("")}
      ${box(650, 38, 200, 116, output, "#eaf1ff")}
      <path d="M602 96 H650" stroke="#1f5be0" stroke-width="6"/>
      <path d="M630 83 l20 13 -20 13" fill="#1f5be0"/>`, caption, question);
  };

  const governanceGate = (payload, caption, question) => {
    const [checks, gate, output] = payload;
    return wrap("governance-gate", `
      ${checks.slice(0, 4).map((item, index) => box(40, 8 + index * 44, 235, 40, item)).join("")}
      <path d="M275 95 H382 M570 95 H674" stroke="#1f5be0" stroke-width="6"/>
      <path d="M370 85 l12 10 -12 10 M662 85 l12 10 -12 10" fill="none" stroke="#1f5be0" stroke-width="4"/>
      <rect x="382" y="30" width="188" height="130" rx="10" fill="#eef4ff" stroke="#15366d" stroke-width="4"/>
      <path d="M430 70 h92 v44 h-92z M445 70 V52 a31 31 0 0 1 62 0 v18" fill="#fff" stroke="#15366d" stroke-width="4"/>
      <text x="476" y="146" text-anchor="middle" class="cv-label">${escapeHtml(gate)}</text>
      ${box(674, 62, 184, 66, output, "#eaf1ff")}`, caption, question);
  };

  const lifecycle = (labels, caption, question) => {
    const width = 145;
    const inner = labels.slice(0, 5).map((label, index) => {
      const x = 56 + index * (width + 28);
      const arrow = index < labels.length - 1
        ? `<path d="M${x + width} 83 H${x + width + 20}" stroke="#1f5be0" stroke-width="5"/>
           <path d="M${x + width + 13} 75 l9 8 -9 8" fill="none" stroke="#1f5be0" stroke-width="3"/>`
        : "";
      return box(x, 54, width, 58, label, index === labels.length - 1 ? "#eaf1ff" : "#fff") + arrow;
    }).join("");
    const endX = 56 + (Math.min(labels.length, 5) - 1) * (width + 28) + width / 2;
    return wrap("lifecycle", `${inner}
      <path d="M${endX} 120 V154 H${56 + width / 2} V120" fill="none" stroke="#98a2b3" stroke-width="3"/>
      <text x="450" y="174" text-anchor="middle" class="cv-note">复测结果重新进入下一轮设计</text>`, caption, question);
  };

  const scorecard = (labels, caption, question) => {
    const items = labels.slice(0, 7);
    const rows = Math.ceil(items.length / 3);
    return wrap("scorecard", items.map((label, index) => {
      const x = 70 + (index % 3) * 265;
      const y = rows === 2 ? 26 + Math.floor(index / 3) * 82 : 18 + Math.floor(index / 3) * 54;
      const height = rows === 2 ? 58 : 40;
      return `<rect x="${x}" y="${y}" width="236" height="${height}" rx="7" fill="${index === items.length - 1 ? "#eaf1ff" : "#fff"}" stroke="#15366d" stroke-width="2.5"/>
        <circle cx="${x + 24}" cy="${y + height / 2}" r="10" fill="#fff" stroke="#1f5be0" stroke-width="3"/>
        <path d="M${x + 18} ${y + height / 2} l5 5 9 -11" fill="none" stroke="#15366d" stroke-width="3"/>
        <text x="${x + 45}" y="${y + height / 2 + 6}" class="cv-copy">${escapeHtml(label)}</text>`;
    }).join(""), caption, question);
  };

  const renderers = {
    "stage-flow": stageFlow,
    comparison,
    "input-output": inputOutput,
    "layered-architecture": layeredArchitecture,
    "deliverable-stack": deliverableStack,
    "governance-gate": governanceGate,
    lifecycle,
    scorecard
  };

  const css = `
    .courseware-visual{width:min(100%,900px);align-self:center;background:#fff;border:1.5px solid #cfd8e6;border-radius:9px;padding:9px 14px 7px;display:flex;flex-direction:column;overflow:hidden}
    .courseware-visual svg{width:100%;height:auto;display:block;background:#fff}
    .courseware-visual .semantic-caption{text-align:center;font-size:14px;line-height:1.25;color:#15366d;font-weight:800;margin-top:3px}
    .courseware-visual text{font-family:"PingFang SC","Microsoft YaHei",Arial,sans-serif}
    .courseware-visual .cv-label{font-size:18px;font-weight:800;fill:#101828}
    .courseware-visual .cv-copy{font-size:16px;font-weight:700;fill:#253858}
    .courseware-visual .cv-note{font-size:14px;font-weight:700;fill:#40516b}
    .courseware-visual .cv-step{font-size:17px;font-weight:900;fill:#fff}
    .courseware-visual .cv-head-light{font-size:17px;font-weight:800;fill:#fff}
    @media(min-width:1700px){.courseware-visual{width:min(100%,1200px)}.courseware-visual .semantic-caption{font-size:18px}}
    @media(max-width:700px){.courseware-visual{padding:8px}.courseware-visual .semantic-caption{font-size:13px}}
  `;

  const installStyles = () => {
    if (document.getElementById("courseware-visual-starter-styles")) return;
    const style = document.createElement("style");
    style.id = "courseware-visual-starter-styles";
    style.textContent = css;
    document.head.appendChild(style);
  };

  const render = (type, payload, caption, question) => {
    if (!renderers[type]) throw new Error(`Unknown courseware visual type: ${type}`);
    return renderers[type](payload, caption, question);
  };

  const mount = (target, type, payload, caption, question) => {
    installStyles();
    const element = typeof target === "string" ? document.querySelector(target) : target;
    if (!element) throw new Error("Courseware visual mount target not found");
    element.insertAdjacentHTML("beforeend", render(type, payload, caption, question));
    return element.lastElementChild;
  };

  window.CoursewareVisuals = { css, installStyles, mount, questions, render, types: Object.keys(renderers) };
})();

