/*!
 * BlockWriteAI
 * A dependency-free, embeddable block rich text editor for script-tag usage.
 */
(function (global, factory) {
  if (typeof module === "object" && module.exports) {
    module.exports = factory();
  } else {
    var Editor = factory();
    global.BlockWriteAI = Editor;
    global.RichBlockEditor = Editor;
  }
})(typeof window !== "undefined" ? window : this, function () {
  "use strict";

var VERSION = "2.0.1";
  var GLOBAL_SCOPE = typeof window !== "undefined" ? window : (typeof globalThis !== "undefined" ? globalThis : {});
  var PLATFORM_ENDPOINTS = {
    cloudProxy: "https://blockwriteai.in/api/premium_proxy.php",
    premiumPlugins: "https://blockwriteai.in/api/premium_plugins.php",
    licenseVerify: "https://blockwriteai.in/api/license_verify.php",
    aiUsage: "https://blockwriteai.in/api/ai_usage.php",
    aiUsageDetail: "https://blockwriteai.in/api/ai_usage_detail.php",
    aiGenerate: "https://blockwriteai.in/api/ai_generate.php",
    aiGenerateStream: "https://blockwriteai.in/api/ai_generate_stream.php",
    aiHistory: "https://blockwriteai.in/api/ai_history.php",
    premiumUsage: "https://blockwriteai.in/api/premium_usage.php",
    premiumEvent: "https://blockwriteai.in/api/premium_event.php",
    signatureEvent: "https://blockwriteai.in/api/signature_event.php",
    letterheadSettings: "https://blockwriteai.in/api/letterhead_settings.php"
  };
  var BLOCK_TYPES = [
    "paragraph",
    "heading",
    "quote",
    "list",
    "checklist",
    "code",
    "image",
    "table",
    "embed",
    "callout",
    "delimiter",
    "button",
    "toggle",
    "signatureRow",
    "linkPreview",
    "raw",
    "attaches",
    "drawing",
    "mermaid",
    "signature",
    "signatureFlow",
    "letterHead",
    "sheetAnalyzer"
  ];

  var TOOL_LABELS = {
    paragraph: { label: "Text", hint: "Plain text block", icon: "font" },
    heading: { label: "Heading", hint: "Section heading", icon: "heading" },
    quote: { label: "Quote", hint: "Quote with caption", icon: "quote-left" },
    list: { label: "List", hint: "Ordered or unordered list", icon: "list-ul" },
    checklist: { label: "Checklist", hint: "Tasks with checkboxes", icon: "square-check" },
    code: { label: "Code", hint: "Code block", icon: "code" },
    image: { label: "Image", hint: "Upload or paste an image", icon: "image" },
    table: { label: "Table", hint: "Editable table", icon: "table" },
    embed: { label: "Embed", hint: "YouTube, Vimeo, X, maps, or any iframe URL", icon: "play" },
    callout: { label: "Callout", hint: "Highlighted note", icon: "circle-info" },
    delimiter: { label: "Divider", hint: "Horizontal divider", icon: "minus" },
    button: { label: "Button", hint: "Linked CTA button", icon: "rectangle-ad" },
    toggle: { label: "Toggle", hint: "Collapsible content", icon: "chevron-down" },
    signatureRow: { label: "Multiple blocks in a row", hint: "Place up to 3 blocks in one row", icon: "table-columns" },
    linkPreview: { label: "Link Preview", hint: "Rich link card", icon: "link" },
    raw: { label: "Raw HTML", hint: "Trusted HTML block", icon: "file-code" },
    attaches: { label: "Attachment", hint: "Upload or link a file", icon: "paperclip" },
    drawing: { label: "Drawing", hint: "Premium drawing canvas", icon: "pen-ruler" },
    mermaid: { label: "Mermaid", hint: "Premium drawing and diagram block", icon: "diagram-project" },
    signature: { label: "Signature", hint: "Premium signature field", icon: "signature" },
    signatureFlow: { label: "Signature Flow", hint: "Premium multi-signer workflow", icon: "file-signature" },
    letterHead: { label: "AI Letter head", hint: "Premium branded document sheet", icon: "file-lines" },
    sheetAnalyzer: { label: "AI Sheet Analyzer", hint: "Premium spreadsheet analysis", icon: "table" }
  };
  var PLUGIN_TOOLS = {};
  var GLOBAL_OPTIONS = {};
  var PREMIUM_FEATURE_LABELS = {
    drawing: "Drawing",
    mermaid: "Mermaid",
    signature: "Signature",
    signature_flow: "Signature Flow",
    letterhead: "AI Letter head",
    sheet_analyzer: "AI Sheet Analyzer",
    ai: "AI"
  };
  var PREMIUM_TOOL_FEATURES = {
    drawing: "drawing",
    mermaid: "mermaid",
    signature: "signature",
    signatureFlow: "signature_flow",
    letterHead: "letterhead",
    sheetAnalyzer: "sheet_analyzer"
  };
  var BUILTIN_PLUGIN_SCRIPTS = {
    drawing: "plugins/blockwriteai-drawing.min.js",
    mermaid: "plugins/blockwriteai-mermaid.min.js",
    signature: "plugins/blockwriteai-signature.min.js",
    signatureFlow: "plugins/blockwriteai-signature-flow.min.js",
    letterHead: "plugins/blockwriteai-letterhead.min.js",
    sheetAnalyzer: "plugins/blockwriteai-sheet-analyzer.min.js"
  };
  var LOADED_PREMIUM_PLUGIN_BUNDLES = {};

  var INLINE_BUTTONS = [
    { command: "bold", label: "B", title: "Bold" },
    { command: "italic", label: "I", title: "Italic" },
    { command: "underline", label: "U", title: "Underline" },
    { command: "strikeThrough", label: "S", title: "Strike" },
    { command: "formatBlock", value: "blockquote", icon: "quote-left", label: "Q", title: "Quote selection" },
    { command: "createLink", icon: "link", label: "Link", title: "Add link" },
    { command: "justifyLeft", icon: "align-left", label: "L", title: "Align left" },
    { command: "justifyCenter", icon: "align-center", label: "C", title: "Align center" },
    { command: "justifyRight", icon: "align-right", label: "R", title: "Align right" },
    { command: "insertComment", icon: "comment-dots", label: "Comment", title: "Insert comment" },
    { command: "removeComment", icon: "comment-slash", label: "Remove comment", title: "Remove comment" },
    { command: "removeFormat", icon: "eraser", label: "Clear", title: "Clear formatting" }
  ];

  var CODE_LANGUAGES = [
    ["plaintext", "Plain text"],
    ["javascript", "JavaScript"],
    ["typescript", "TypeScript"],
    ["html", "HTML"],
    ["css", "CSS"],
    ["json", "JSON"],
    ["php", "PHP"],
    ["python", "Python"],
    ["java", "Java"],
    ["csharp", "C#"],
    ["cpp", "C++"],
    ["sql", "SQL"],
    ["bash", "Bash"],
    ["markdown", "Markdown"]
  ];

  function localVendorAsset(path) {
    var scriptSrc = "";
    if (typeof document !== "undefined") {
      scriptSrc = document.currentScript && document.currentScript.src || "";
      if (!scriptSrc) {
        var scripts = document.getElementsByTagName ? document.getElementsByTagName("script") : [];
        for (var i = scripts.length - 1; i >= 0; i -= 1) {
          var src = scripts[i].src || "";
          if (/blockwriteai(?:\.protected)?(?:\.min)?\.js(?:\?|#|$)/.test(src)) {
            scriptSrc = src;
            break;
          }
        }
      }
    }
    var prefix = scriptSrc.indexOf("/blockwriteai_editor/static/blockwriteai/") !== -1 ? "../../../../" : "../";
    try {
      return scriptSrc ? new URL(prefix + path, scriptSrc).href : path;
    } catch (error) {
      return prefix + path;
    }
  }

  function localPluginAsset(path) {
    var scriptSrc = "";
    if (typeof document !== "undefined") {
      scriptSrc = document.currentScript && document.currentScript.src || "";
      if (!scriptSrc) {
        var scripts = document.getElementsByTagName ? document.getElementsByTagName("script") : [];
        for (var i = scripts.length - 1; i >= 0; i -= 1) {
          var src = scripts[i].src || "";
          if (/blockwriteai(?:\.bundle)?(?:\.protected)?(?:\.min)?\.js(?:\?|#|$)/.test(src)) {
            scriptSrc = src;
            break;
          }
        }
      }
    }
    try {
      return scriptSrc ? new URL(path, scriptSrc).href : path;
    } catch (error) {
      return path;
    }
  }

  var JQUERY_JS = localVendorAsset("vendor/jquery/jquery.min.js");
  var SELECT2_CSS = localVendorAsset("vendor/select2/css/select2.min.css");
  var SELECT2_JS = localVendorAsset("vendor/select2/js/select2.min.js");
  var ATTACHMENT_ACCEPT = [
    "audio/*",
    "video/*",
    ".pdf",
    ".doc",
    ".docx",
    ".xls",
    ".xlsx",
    ".ppt",
    ".pptx",
    ".csv",
    ".mp3",
    ".wav",
    ".m4a",
    ".aac",
    ".ogg",
    ".flac",
    ".txt",
    ".rtf",
    ".odt",
    ".ods",
    ".odp"
  ].join(",");
  var ATTACHMENT_EXTENSIONS = new Set([
    "pdf",
    "doc",
    "docx",
    "xls",
    "xlsx",
    "ppt",
    "pptx",
    "csv",
    "mp3",
    "wav",
    "m4a",
    "aac",
    "ogg",
    "flac",
    "txt",
    "rtf",
    "odt",
    "ods",
    "odp"
  ]);

  var ALLOWED_TAGS = new Set([
    "a",
    "abbr",
    "b",
    "blockquote",
    "br",
    "code",
    "del",
    "em",
    "figcaption",
    "h1",
    "h2",
    "h3",
    "h4",
    "i",
    "img",
    "li",
    "mark",
    "ol",
    "p",
    "pre",
    "s",
    "span",
    "strike",
    "strong",
    "sub",
    "sup",
    "u",
    "ul"
  ]);

  var URL_ATTRS = new Set(["href", "src"]);
  var GLOBAL_ATTRS = new Set(["title", "alt", "target", "rel", "style"]);
  var TAG_ATTRS = {
    a: new Set(["href", "target", "rel", "title"]),
    img: new Set(["src", "alt", "title", "width", "height"]),
    code: new Set(["data-language"]),
    span: new Set(["style", "class", "data-rbe-comment", "data-rbe-comment-id", "data-rbe-comment-status", "data-rbe-comment-reason", "data-rbe-comment-date", "data-rbe-comment-updated", "data-rbe-comment-reply", "data-rbe-comment-reply-date", "data-rbe-comment-thread", "data-rbe-comment-member-name", "data-rbe-comment-member-role", "data-rbe-comment-updated-member-name", "data-rbe-comment-updated-member-role"]),
    mark: new Set(["style"])
  };
  var SAFE_STYLE_PROPS = new Set([
    "color",
    "background-color",
    "font-family",
    "font-size",
    "font-weight",
    "font-style",
    "text-decoration",
    "text-decoration-line",
    "text-align"
  ]);

  var UID_COUNTER = 0;

  function uid(prefix) {
    UID_COUNTER += 1;
    return (prefix || "block") + "-" + Date.now().toString(36) + "-" + UID_COUNTER.toString(36) + "-" + Math.random().toString(36).slice(2, 8);
  }

  function el(tag, className, attrs) {
    var node = document.createElement(tag);
    if (className) node.className = className;
    if (attrs) {
      Object.keys(attrs).forEach(function (key) {
        if (attrs[key] === false || attrs[key] === null || attrs[key] === undefined) return;
        if (key === "text") node.textContent = attrs[key];
        else if (key === "html") node.innerHTML = attrs[key];
        else if (key === "dataset") {
          Object.keys(attrs[key]).forEach(function (dataKey) {
            node.dataset[dataKey] = attrs[key][dataKey];
          });
        } else if (key in node) {
          node[key] = attrs[key];
        } else {
          node.setAttribute(key, attrs[key]);
        }
      });
    }
    return node;
  }

  function escapeHTML(value) {
    return String(value == null ? "" : value)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;");
  }

  function renderableRawHTML(value) {
    var html = String(value || "");
    if (!html.trim()) return "";
    if (/(<!doctype|<html[\s>]|<body[\s>])/i.test(html)) return html;
    return [
      "<!doctype html>",
      '<html><head><meta charset="utf-8"><base target="_blank">',
      "<style>",
      "body{margin:0;font:16px/1.5 system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:#172033;background:#fff;}",
      "img,video,iframe{max-width:100%;height:auto;}table{border-collapse:collapse;max-width:100%;}td,th{border:1px solid #d7dee8;padding:8px;}",
      "</style></head><body>",
      html,
      "</body></html>"
    ].join("");
  }

  function updateRawPreview(preview, html) {
    if (!preview) return;
    var frame = preview.querySelector(".rbe-raw-frame");
    if (!frame) {
      preview.innerHTML = "";
      frame = el("iframe", "rbe-raw-frame", {
        title: "Raw HTML preview",
        loading: "lazy",
        sandbox: "allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin"
      });
      frame.onload = function () {
        try {
          var doc = frame.contentDocument || frame.contentWindow.document;
          var page = doc.documentElement;
          var body = doc.body;
          var height = Math.max(
            page ? page.scrollHeight : 0,
            body ? body.scrollHeight : 0,
            220
          );
          frame.style.height = Math.min(height + 4, 900) + "px";
        } catch (error) {
          frame.style.height = "360px";
        }
      };
      preview.appendChild(frame);
    }
    frame.srcdoc = renderableRawHTML(html);
  }

  function rawOutputFrame(html) {
    html = String(html || "");
    if (!html.trim()) return "";
    var wrapRawOutput = function (content) {
      return '<div class="rbe-output-raw-container"><div class="rbe-output-raw-html">' + content + "</div></div>";
    };
    if (/(<!doctype|<html[\s>]|<body[\s>])/i.test(html) && typeof DOMParser !== "undefined") {
      try {
        var parsed = new DOMParser().parseFromString(html, "text/html");
        var headNodes = parsed.head ? Array.prototype.slice.call(parsed.head.querySelectorAll("style,link[rel='stylesheet']")) : [];
        var headAssets = headNodes.map(function (node) {
          return node.outerHTML || "";
        }).join("");
        var bodyHTML = parsed.body ? parsed.body.innerHTML : "";
        return wrapRawOutput(headAssets + bodyHTML);
      } catch (error) {
        return wrapRawOutput(html);
      }
    }
    return wrapRawOutput(html);
  }

  function svgIcon(name, label) {
    name = String(name || "circle").trim();
    var paths = {
      "align-center": '<path d="M5 6h14M8 10h8M5 14h14M8 18h8"/>',
      "align-left": '<path d="M4 6h16M4 10h10M4 14h16M4 18h10"/>',
      "align-right": '<path d="M4 6h16M10 10h10M4 14h16M10 18h10"/>',
      "arrow-down": '<path d="M12 5v14M6 13l6 6 6-6"/>',
      "arrow-down-long": '<path d="M12 4v16"/><path d="m6 14 6 6 6-6"/>',
      "arrow-left-long": '<path d="M20 12H4"/><path d="m10 6-6 6 6 6"/>',
      "arrow-pointer": '<path d="m4 3 8 18 2-7 7-2z"/>',
      "arrow-right": '<path d="M5 12h14"/><path d="m13 6 6 6-6 6"/>',
      "arrow-right-long": '<path d="M4 12h16"/><path d="m14 6 6 6-6 6"/>',
      "arrow-up": '<path d="M12 19V5M6 11l6-6 6 6"/>',
      "arrow-up-long": '<path d="M12 20V4"/><path d="m6 10 6-6 6 6"/>',
      "arrow-up-right-from-square": '<path d="M14 5h5v5"/><path d="M10 14 19 5"/><path d="M19 14v5H5V5h5"/>',
      "arrows-left-right": '<path d="M4 12h16"/><path d="m8 8-4 4 4 4"/><path d="m16 8 4 4-4 4"/>',
      "arrows-up-down": '<path d="M12 4v16"/><path d="m8 8 4-4 4 4"/><path d="m8 16 4 4 4-4"/>',
      "bezier-curve": '<circle cx="6" cy="18" r="2"/><circle cx="18" cy="6" r="2"/><path d="M8 18c5 0 3-12 8-12"/><path d="M6 16V8h6"/><path d="M18 8v8h-6"/>',
      "bold": '<path d="M8 5h5a4 4 0 0 1 0 8H8z"/><path d="M8 13h6a3 3 0 0 1 0 6H8z"/>',
      "briefcase": '<path d="M9 7V5h6v2"/><path d="M4 7h16v12H4z"/><path d="M4 12h16"/>',
      "caret-down": '<path d="m7 10 5 5 5-5"/>',
      "check": '<path d="m5 12 4 4L19 6"/>',
      "chevron-down": '<path d="m6 9 6 6 6-6"/>',
      "circle-exclamation": '<circle cx="12" cy="12" r="9"/><path d="M12 7v6"/><path d="M12 17h.01"/>',
      "circle-info": '<circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 8h.01"/>',
      "circle": '<circle cx="12" cy="12" r="8"/>',
      "circle-check": '<circle cx="12" cy="12" r="9"/><path d="m8 12 3 3 5-6"/>',
      "circle-plus": '<circle cx="12" cy="12" r="9"/><path d="M12 8v8M8 12h8"/>',
      "chart-line": '<path d="M4 19h16"/><path d="M5 15l4-4 3 3 6-7"/><path d="M17 7h2v2"/>',
      "chart-column": '<path d="M4 19h16"/><path d="M7 16V9"/><path d="M12 16V5"/><path d="M17 16v-4"/>',
      "chart-pie": '<path d="M12 3v9h9"/><path d="M20.5 14.5A9 9 0 1 1 9.5 3.5"/><path d="M14 3.3A9 9 0 0 1 20.7 10"/>',
      "chart-doughnut": '<path d="M12 3v4"/><path d="M12 17v4"/><path d="M3 12h4"/><path d="M17 12h4"/><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="4"/>',
      "chart-area": '<path d="M4 19h16"/><path d="M5 16l4-5 4 3 6-8v10H5z"/><path d="M5 16l4-5 4 3 6-8"/>',
      "chart-scatter": '<path d="M4 19h16"/><path d="M5 5v14"/><circle cx="9" cy="14" r="1.5"/><circle cx="13" cy="10" r="1.5"/><circle cx="17" cy="7" r="1.5"/><circle cx="18" cy="15" r="1.5"/>',
      "chart-stacked": '<path d="M4 19h16"/><path d="M7 16v-3"/><path d="M7 13v-4"/><path d="M12 16v-5"/><path d="M12 11V6"/><path d="M17 16v-2"/><path d="M17 14v-5"/>',
      "chart-comparison": '<path d="M4 19h16"/><path d="M7 16V9"/><path d="M10 16v-5"/><path d="M14 16V6"/><path d="M17 16v-8"/>',
      "trending-up": '<path d="M4 17l6-6 4 4 6-8"/><path d="M14 7h6v6"/>',
      "gauge-high": '<path d="M4 14a8 8 0 1 1 16 0"/><path d="M12 14l4-5"/><path d="M8 18h8"/><path d="M7 14h.01M17 14h.01"/>',
      "clock-rotate-left": '<path d="M4 7v5h5"/><path d="M5.6 15A7 7 0 1 0 6 8"/><path d="M12 8v5l3 2"/>',
      "cloud-arrow-up": '<path d="M16 16l-4-4-4 4"/><path d="M12 12v8"/><path d="M20 16.5A4.5 4.5 0 0 0 16 10a6 6 0 0 0-11 3 3.5 3.5 0 0 0 1 6h2"/>',
      "cloud": '<path d="M17.5 19H8a5 5 0 0 1-.8-9.9A6.5 6.5 0 0 1 19.5 12 3.5 3.5 0 0 1 17.5 19Z"/>',
      "code": '<path d="m8 8-4 4 4 4"/><path d="m16 8 4 4-4 4"/><path d="m14 5-4 14"/>',
      "code-branch": '<path d="M6 4v7a3 3 0 0 0 3 3h6"/><path d="M18 11l3 3-3 3"/><circle cx="6" cy="4" r="2"/><circle cx="6" cy="20" r="2"/><path d="M6 13v5"/>',
      "comment": '<path d="M21 12a8 8 0 0 1-8 8H7l-4 3v-6a8 8 0 1 1 18-5Z"/>',
      "comment-dots": '<path d="M21 12a8 8 0 0 1-8 8H7l-4 3v-6a8 8 0 1 1 18-5Z"/><path d="M8 12h.01M12 12h.01M16 12h.01"/>',
      "comment-slash": '<path d="M21 12a8 8 0 0 1-8 8H7l-4 3v-6a8 8 0 0 1 2-11"/><path d="m3 3 18 18"/>',
      "comments": '<path d="M17 10a6 6 0 0 1-6 6H7l-4 3v-5a6 6 0 1 1 14-4Z"/><path d="M12 18h5l4 3v-5a6 6 0 0 0-3-5"/>',
      "compress": '<path d="M8 4v4H4"/><path d="m4 8 5-5"/><path d="M16 4v4h4"/><path d="m20 8-5-5"/><path d="M8 20v-4H4"/><path d="m4 16 5 5"/><path d="M16 20v-4h4"/><path d="m20 16-5 5"/>',
      "copy": '<path d="M8 8h11v11H8z"/><path d="M5 16H4a1 1 0 0 1-1-1V4h11v1"/>',
      "crop-simple": '<path d="M6 2v14h16"/><path d="M18 22V8H2"/>',
      "cube": '<path d="m12 3 8 4.5v9L12 21l-8-4.5v-9z"/><path d="M12 12 4 7.5"/><path d="M12 12v9"/><path d="m12 12 8-4.5"/>',
      "database": '<ellipse cx="12" cy="5" rx="7" ry="3"/><path d="M5 5v14c0 1.7 3.1 3 7 3s7-1.3 7-3V5"/><path d="M5 12c0 1.7 3.1 3 7 3s7-1.3 7-3"/>',
      "diamond": '<path d="m12 3 9 9-9 9-9-9z"/>',
      "diagram-project": '<path d="M4 5h6v6H4zM14 13h6v6h-6zM14 5h6v4h-6z"/><path d="M10 8h4M7 11v5h7"/>',
      "divide": '<path d="M5 12h14"/><path d="M12 7h.01"/><path d="M12 17h.01"/>',
      "download": '<path d="M12 4v10"/><path d="m7 10 5 5 5-5"/><path d="M5 20h14"/>',
      "down-left-and-up-right-to-center": '<path d="M8 8H4V4"/><path d="M4 8l6-6"/><path d="M16 16h4v4"/><path d="m14 22 6-6"/>',
      "draw-polygon": '<path d="M5 7 12 3l7 4v10l-7 4-7-4z"/><circle cx="5" cy="7" r="1"/><circle cx="19" cy="7" r="1"/><circle cx="12" cy="21" r="1"/>',
      "eraser": '<path d="m4 15 8-8a3 3 0 0 1 4 0l4 4-9 9H7z"/><path d="M14 20h7"/>',
      "equals": '<path d="M5 9h14"/><path d="M5 15h14"/>',
      "file-code": '<path d="M14 3H6v18h12V7z"/><path d="M14 3v4h4"/><path d="m10 13-2 2 2 2M14 13l2 2-2 2"/>',
      "file-export": '<path d="M14 3H6v18h12v-6"/><path d="M14 3v4h4"/><path d="M14 13h8M18 9l4 4-4 4"/>',
      "file-lines": '<path d="M14 3H6v18h12V7z"/><path d="M14 3v4h4"/><path d="M9 12h6M9 16h6"/>',
      "file-signature": '<path d="M14 3H6v18h12V7z"/><path d="M14 3v4h4"/><path d="M8 17c2-3 3-3 4 0 1-2 2-2 4 0"/>',
      "fill-drip": '<path d="m14 4 6 6-8 8-6-6z"/><path d="M4 20h16"/><path d="M14 12H6"/><path d="M19 16c0 1-.8 2-2 2s-2-1-2-2c0-1.5 2-4 2-4s2 2.5 2 4Z"/>',
      "floppy-disk": '<path d="M5 3h12l2 2v16H5z"/><path d="M8 3v6h8V3"/><path d="M8 21v-7h8v7"/>',
      "font": '<path d="M5 20 12 4l7 16"/><path d="M8 14h8"/>',
      "gear": '<circle cx="12" cy="12" r="3"/><path d="M19 12a7 7 0 0 0-.1-1l2-1.5-2-3.4-2.4 1a7 7 0 0 0-1.7-1L14.5 3h-5l-.4 3.1a7 7 0 0 0-1.7 1l-2.4-1-2 3.4L5 11a7 7 0 0 0 0 2l-2 1.5 2 3.4 2.4-1a7 7 0 0 0 1.7 1l.4 3.1h5l.4-3.1a7 7 0 0 0 1.7-1l2.4 1 2-3.4-2-1.5a7 7 0 0 0 .1-1Z"/>',
      "grip-lines": '<path d="M7 8h10"/><path d="M7 12h10"/><path d="M7 16h10"/>',
      "grip-lines-vertical": '<path d="M8 7v10"/><path d="M12 7v10"/><path d="M16 7v10"/>',
      "grip-vertical": '<path d="M9 5h.01M15 5h.01M9 12h.01M15 12h.01M9 19h.01M15 19h.01"/>',
      "heading": '<path d="M6 5v14M18 5v14M6 12h12"/>',
      "heart": '<path d="M20.8 8.6c0 5.1-8.8 10.4-8.8 10.4S3.2 13.7 3.2 8.6A4.6 4.6 0 0 1 12 6.7a4.6 4.6 0 0 1 8.8 1.9Z"/>',
      "highlighter": '<path d="m4 20 5-1 9-9-4-4-9 9z"/><path d="m14 6 4-4 4 4-4 4"/>',
      "image": '<path d="M4 5h16v14H4z"/><path d="m4 16 5-5 4 4 2-2 5 5"/><circle cx="15" cy="9" r="1"/>',
      "indent": '<path d="M4 6h16M12 10h8M12 14h8M4 18h16"/><path d="m4 10 4 2-4 2z"/>',
      "italic": '<path d="M10 5h8M6 19h8M14 5l-4 14"/>',
      "link": '<path d="M10 13a5 5 0 0 0 7 0l2-2a5 5 0 0 0-7-7l-1 1"/><path d="M14 11a5 5 0 0 0-7 0l-2 2a5 5 0 0 0 7 7l1-1"/>',
      "list-check": '<path d="m4 7 2 2 4-4"/><path d="M12 7h8"/><path d="m4 15 2 2 4-4"/><path d="M12 15h8"/>',
      "list-ol": '<path d="M10 6h10M10 12h10M10 18h10"/><path d="M4 5h1v4M4 9h2M4 12h2l-2 3h2M4 18h2M6 18v3"/>',
      "list-ul": '<path d="M8 6h12M8 12h12M8 18h12"/><path d="M4 6h.01M4 12h.01M4 18h.01"/>',
      "lock": '<rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/>',
      "minus": '<path d="M5 12h14"/>',
      "outdent": '<path d="M4 6h16M12 10h8M12 14h8M4 18h16"/><path d="m8 10-4 2 4 2z"/>',
      "paperclip": '<path d="m21 11-8 8a5 5 0 0 1-7-7l9-9a3 3 0 0 1 4 4l-9 9a1 1 0 1 1-1-1l8-8"/>',
      "paragraph": '<path d="M18 5H9a4 4 0 0 0 0 8h3"/><path d="M14 5v14M18 5v14"/>',
      "pen-nib": '<path d="m12 3 7 7-4 9-3-3-3 3-4-4 3-3-3-3z"/><path d="m12 3-3 9"/><circle cx="12" cy="12" r="1"/>',
      "pen-ruler": '<path d="m14 6 4 4-9 9H5v-4z"/><path d="m16 4 4 4"/><path d="M4 4h6M4 8h3M4 12h2"/>',
      "pencil": '<path d="M4 20h4l11-11-4-4L4 16z"/><path d="m13 7 4 4"/><path d="M14 5l1-1a2 2 0 0 1 3 0l2 2a2 2 0 0 1 0 3l-1 1"/>',
      "pen-to-square": '<path d="M11 5H5v14h14v-6"/><path d="M13 15l7-7-4-4-7 7v4z"/><path d="m15 6 3 3"/>',
      "people-arrows": '<path d="M8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM16 19a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z"/><path d="M3 19a5 5 0 0 1 8-4M13 9a5 5 0 0 1 8 4M13 5h7l-2-2M11 19H4l2 2"/>',
      "play": '<path d="M8 5v14l11-7z"/>',
      "plus": '<path d="M12 5v14"/><path d="M5 12h14"/>',
      "puzzle-piece": '<path d="M8 3h5v4a2 2 0 1 0 4 0V3h4v6h-3a2 2 0 1 0 0 4h3v8h-6v-3a2 2 0 1 0-4 0v3H3v-6h4a2 2 0 1 0 0-4H3V3h5z"/>',
      "quote-left": '<path d="M8 11h4v8H5v-6a8 8 0 0 1 4-7"/><path d="M18 11h4v8h-7v-6a8 8 0 0 1 4-7"/>',
      "rectangle-ad": '<path d="M3 6h18v12H3z"/><path d="M7 15V9l4 6 4-6v6"/>',
      "reply": '<path d="M9 7 4 12l5 5"/><path d="M4 12h9a7 7 0 0 1 7 7v1"/>',
      "rotate-left": '<path d="M4 7v5h5"/><path d="M5.6 15A7 7 0 1 0 6 8"/>',
      "rotate-right": '<path d="M20 7v5h-5"/><path d="M18.4 15A7 7 0 1 1 18 8"/>',
      "shapes": '<path d="M5 5h6v6H5z"/><circle cx="17" cy="8" r="3"/><path d="m8 15 4 6H4z"/><path d="M15 15h5v5h-5z"/>',
      "share-nodes": '<circle cx="6" cy="12" r="2"/><circle cx="18" cy="6" r="2"/><circle cx="18" cy="18" r="2"/><path d="m8 11 8-4"/><path d="m8 13 8 4"/>',
      "signature": '<path d="M4 17c3-4 5-4 6 0 1-3 3-3 4 0 1-2 3-2 6 0"/><path d="m15 7 3-3 2 2-3 3-3 1z"/>',
      "sparkles": '<path d="M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5z"/><path d="M19 14l.8 2.2L22 17l-2.2.8L19 20l-.8-2.2L16 17l2.2-.8z"/><path d="M5 14l.7 1.8L7.5 16l-1.8.7L5 18.5l-.7-1.8L2.5 16l1.8-.7z"/>',
      "square": '<path d="M5 5h14v14H5z"/>',
      "square-check": '<path d="M4 5h16v14H4z"/><path d="m8 12 3 3 5-6"/>',
      "square-root-variable": '<path d="M4 13h3l3 7 4-16h6"/><path d="M16 13l4 4"/><path d="M20 13l-4 4"/>',
      "strikethrough": '<path d="M6 12h12"/><path d="M16 6H9a3 3 0 0 0-1 6l6 2a3 3 0 0 1-1 6H7"/>',
      "subscript": '<path d="m5 7 8 10M13 7 5 17"/><path d="M17 19h4l-4 3h4"/>',
      "superscript": '<path d="m5 17 8-10M13 17 5 7"/><path d="M17 5h4l-4 3h4"/>',
      "table": '<path d="M4 5h16v14H4z"/><path d="M4 11h16M10 5v14"/>',
      "table-columns": '<path d="M4 5h16v14H4z"/><path d="M10 5v14M15 5v14"/>',
      "text-height": '<path d="M6 5h12"/><path d="M12 5v14"/><path d="M8 19h8"/><path d="M4 8v8"/><path d="m2 10 2-2 2 2"/><path d="m2 14 2 2 2-2"/>',
      "trash-can": '<path d="M4 7h16"/><path d="M9 7V4h6v3"/><path d="M6 7l1 14h10l1-14"/><path d="M10 11v6M14 11v6"/>',
      "triangle-exclamation": '<path d="M12 3 22 20H2z"/><path d="M12 9v5"/><path d="M12 17h.01"/>',
      "underline": '<path d="M7 5v6a5 5 0 0 0 10 0V5"/><path d="M5 21h14"/>',
      "up-right-and-down-left-from-center": '<path d="M15 3h6v6"/><path d="m14 10 7-7"/><path d="M9 21H3v-6"/><path d="m10 14-7 7"/>',
      "volume-high": '<path d="M4 9v6h4l5 4V5L8 9z"/><path d="M16 8.5a5 5 0 0 1 0 7"/><path d="M19 6a9 9 0 0 1 0 12"/>',
      "wand-magic-sparkles": '<path d="m4 20 12-12"/><path d="m14 6 4 4"/><path d="M5 4v3M3.5 5.5h3M19 15v3M17.5 16.5h3"/>',
      "window-minimize": '<path d="M5 19h14"/>',
      "xmark": '<path d="M6 6l12 12M18 6 6 18"/>'
    };
    var html = paths[name] || '<circle cx="12" cy="12" r="8"/>';
    return (
      '<svg class="rbe-svg-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" data-rbe-icon="' +
      escapeHTML(name) +
      '">' +
      html +
      '</svg><span class="rbe-sr-only">' +
      escapeHTML(label || name) +
      "</span>"
    );
  }

  function loadStyleOnce(href, key) {
    if (typeof document === "undefined" || !document.head) return;
    if (document.querySelector("link[data-rbe-lib='" + key + "'], link[href='" + href + "']")) return;
    document.head.appendChild(el("link", "", { rel: "stylesheet", href: href, dataset: { rbeLib: key } }));
  }

  function loadScriptOnce(src, key, callback) {
    if (typeof document === "undefined" || !document.head) return;
    var existing = document.querySelector("script[data-rbe-lib='" + key + "'], script[src='" + src + "']");
    if (existing) {
      if (existing.dataset.loaded === "true") callback();
      else existing.addEventListener("load", callback, { once: true });
      return;
    }
    var script = el("script", "", { src: src, dataset: { rbeLib: key } });
    script.addEventListener("load", function () {
      script.dataset.loaded = "true";
      callback();
    });
    document.head.appendChild(script);
  }

  function pluginToolReady(type) {
    return !!(PLUGIN_TOOLS[type] && typeof PLUGIN_TOOLS[type].render === "function");
  }

  function ensureBuiltinPluginTool(type, callback) {
    if (pluginToolReady(type)) {
      callback(true);
      return;
    }
    var scriptPath = BUILTIN_PLUGIN_SCRIPTS[type];
    if (!scriptPath) {
      callback(false);
      return;
    }
    if (typeof document === "undefined" || !document.head) {
      callback(false);
      return;
    }
    loadScriptOnce(localPluginAsset(scriptPath), "builtin-plugin-" + type, function () {
      callback(pluginToolReady(type));
    });
  }

  function ensureSelect2(callback) {
    if (typeof window === "undefined" || typeof document === "undefined") return;
    loadStyleOnce(SELECT2_CSS, "select2-css");
    var ready = function () {
      if (window.jQuery && window.jQuery.fn && window.jQuery.fn.select2) callback();
    };
    if (window.jQuery && window.jQuery.fn && window.jQuery.fn.select2) {
      ready();
      return;
    }
    var loadPlugin = function () {
      loadScriptOnce(SELECT2_JS, "select2-js", ready);
    };
    if (window.jQuery) loadPlugin();
    else loadScriptOnce(JQUERY_JS, "jquery-js", loadPlugin);
  }

  function isSafeUrl(value) {
    if (!value) return true;
    var text = String(value).trim().toLowerCase();
    return (
      text.indexOf("http://") === 0 ||
      text.indexOf("https://") === 0 ||
      text.indexOf("mailto:") === 0 ||
      text.indexOf("tel:") === 0 ||
      text.indexOf("data:image/") === 0 ||
      text.indexOf("/") === 0 ||
      text.indexOf("#") === 0 ||
      text.indexOf("./") === 0 ||
      text.indexOf("../") === 0
    );
  }

  function cleanStyle(value) {
    return String(value || "")
      .split(";")
      .map(function (part) {
        var pair = part.split(":");
        if (pair.length < 2) return "";
        var prop = pair.shift().trim().toLowerCase();
        var val = pair.join(":").trim();
        if (!SAFE_STYLE_PROPS.has(prop)) return "";
        if (/url\s*\(|expression\s*\(/i.test(val)) return "";
        return prop + ": " + val;
      })
      .filter(Boolean)
      .join("; ");
  }

  function cleanColorValue(value) {
    var text = String(value || "").trim();
    if (!text) return "";
    if (/^#[0-9a-f]{3,8}$/i.test(text)) return text;
    if (/^rgba?\(\s*(\d{1,3}\s*,\s*){2}\d{1,3}(\s*,\s*(0|1|0?\.\d+))?\s*\)$/i.test(text)) return text;
    if (/^[a-z]+$/i.test(text)) return text;
    return "";
  }

  function sanitizeHTML(input) {
    var template = document.createElement("template");
    template.innerHTML = String(input || "");

    function walk(parent) {
      Array.prototype.slice.call(parent.childNodes).forEach(function (child) {
        if (child.nodeType === Node.TEXT_NODE) return;
        if (child.nodeType !== Node.ELEMENT_NODE) {
          child.remove();
          return;
        }

        var tag = child.tagName.toLowerCase();
        if (tag === "font") {
          var color = cleanColorValue(child.getAttribute("color"));
          if (color) {
            var span = document.createElement("span");
            span.style.color = color;
            while (child.firstChild) span.appendChild(child.firstChild);
            child.replaceWith(span);
            walk(span);
          } else {
            var fontFragment = document.createDocumentFragment();
            while (child.firstChild) fontFragment.appendChild(child.firstChild);
            child.replaceWith(fontFragment);
            walk(parent);
          }
          return;
        }
        if (!ALLOWED_TAGS.has(tag)) {
          var fragment = document.createDocumentFragment();
          while (child.firstChild) fragment.appendChild(child.firstChild);
          child.replaceWith(fragment);
          walk(parent);
          return;
        }

        Array.prototype.slice.call(child.attributes).forEach(function (attr) {
          var name = attr.name.toLowerCase();
          var tagAllowed = TAG_ATTRS[tag] && TAG_ATTRS[tag].has(name);
          if (!GLOBAL_ATTRS.has(name) && !tagAllowed) {
            child.removeAttribute(attr.name);
            return;
          }
          if (URL_ATTRS.has(name) && !isSafeUrl(attr.value)) {
            child.removeAttribute(attr.name);
            return;
          }
          if (name === "style") {
            var style = cleanStyle(attr.value);
            if (style) child.setAttribute("style", style);
            else child.removeAttribute("style");
          }
        });

        if (tag === "a") {
          child.setAttribute("rel", "noopener noreferrer");
          if (!child.getAttribute("target")) child.setAttribute("target", "_blank");
        }

        walk(child);
      });
    }

    walk(template.content);
    return template.innerHTML;
  }

  function textToParagraphs(text) {
    return String(text || "")
      .split(/\n{2,}/)
      .map(function (part) {
        return part.trim();
      })
      .filter(Boolean)
      .map(function (part) {
        return {
          id: uid("block"),
          type: "paragraph",
          data: { text: escapeHTML(part).replace(/\n/g, "<br>") }
        };
      });
  }

  function debounce(fn, wait) {
    var timer = null;
    return function () {
      var args = arguments;
      var ctx = this;
      clearTimeout(timer);
      timer = setTimeout(function () {
        fn.apply(ctx, args);
      }, wait);
    };
  }

  function closestBlock(node) {
    if (!node) return null;
    if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
    return node && node.closest ? node.closest(".rbe-block") : null;
  }

  var MAX_BLOCKS_PER_ROW = 3;

  function isRowCanvas(node) {
    return !!(node && node.classList && node.classList.contains("rbe-signature-row-canvas"));
  }

  function isEmptyRowPlaceholderBlock(block) {
    if (!block || block.dataset.type !== "paragraph") return false;
    var paragraph = block.querySelector(".rbe-paragraph");
    return !!paragraph && (paragraph.textContent || "").trim() === "" && !paragraph.querySelector("img,video,iframe,table");
  }

  function isOnlyRootPlaceholderBlock(editor, block) {
    if (!editor || !editor.canvas || !block || block.parentElement !== editor.canvas) return false;
    return editor.canvas.children.length === 1 && isEmptyRowPlaceholderBlock(block);
  }

  function rowContentBlocks(canvas) {
    return canvas ? Array.prototype.slice.call(canvas.children).filter(function (child) {
      return !isEmptyRowPlaceholderBlock(child);
    }) : [];
  }

  function ensureRowPlaceholder(editor, canvas) {
    if (!canvas || rowContentBlocks(canvas).length || canvas.children.length) return;
    var empty = editor.createBlock("paragraph", { text: "" }, uid("block"));
    empty.classList.add("rbe-nested-block");
    canvas.appendChild(empty);
  }

  function normalizeBlocks(data) {
    if (!data) return [];
    if (Array.isArray(data)) return data;
    if (typeof data === "string") return textToParagraphs(data);
    if (Array.isArray(data.blocks)) return data.blocks;
    return [];
  }

  function getYouTubeId(url) {
    var source = String(url || "").trim();
    var iframeSrc = source.match(/<iframe[^>]+src=["']([^"']+)["']/i);
    if (iframeSrc) source = iframeSrc[1];

    try {
      var parsed = new URL(source, currentOrigin() || undefined);
      var host = parsed.hostname.replace(/^www\./, "").toLowerCase();
      var parts = parsed.pathname.split("/").filter(Boolean);

      if (host === "youtu.be") return parts[0] || "";
      if (host === "youtube.com" || host.endsWith(".youtube.com")) {
        if (parsed.pathname === "/watch") return parsed.searchParams.get("v") || "";
        if ((parts[0] === "embed" || parts[0] === "shorts") && parts[1]) return parts[1];
      }
      if ((host === "youtube-nocookie.com" || host.endsWith(".youtube-nocookie.com")) && parts[0] === "embed") {
        return parts[1] || "";
      }
    } catch (error) {
      // Fall back to pattern matching for pasted fragments instead of full URLs.
    }

    var match = source.match(/(?:youtube(?:-nocookie)?\.com\/(?:watch\?[^#\s]*v=|embed\/|shorts\/)|youtu\.be\/)([A-Za-z0-9_-]{6,})/);
    return match ? match[1] : "";
  }

  function currentOrigin() {
    if (typeof window === "undefined" || !window.location || !window.location.origin) return "";
    return /^https?:\/\//i.test(window.location.origin) ? window.location.origin : "";
  }

  function youtubeEmbedUrl(id) {
    var params = "rel=0&modestbranding=1&playsinline=1";
    var origin = currentOrigin();
    if (origin) params += "&origin=" + encodeURIComponent(origin);
    return "https://www.youtube.com/embed/" + id + "?" + params;
  }

  function getVimeoId(url) {
    var match = String(url).match(/vimeo\.com\/(?:video\/)?([0-9]+)/);
    return match ? match[1] : "";
  }

  function toEmbed(url) {
    var source = String(url || "").trim();
    var youtube = getYouTubeId(source);
    if (youtube) {
      return {
        service: "youtube",
        source: source,
        embed: youtubeEmbedUrl(youtube)
      };
    }
    var vimeo = getVimeoId(source);
    if (vimeo) {
      return {
        service: "vimeo",
        source: source,
        embed: "https://player.vimeo.com/video/" + vimeo
      };
    }
    if (/google\.[^/]+\/maps|goo\.gl\/maps|maps\.app\.goo\.gl/.test(source)) {
      return {
        service: "map",
        source: source,
        embed: source
      };
    }
    return {
      service: "link",
      source: source,
      embed: source
    };
  }

  function readFileAsDataURL(file) {
    return new Promise(function (resolve, reject) {
      var reader = new FileReader();
      reader.onload = function () {
        resolve(reader.result);
      };
      reader.onerror = reject;
      reader.readAsDataURL(file);
    });
  }

  function normalizeUploadOutput(value) {
    var output = Array.isArray(value) ? value : value ? [value] : ["base64"];
    output = output.map(function (item) {
      return String(item || "").toLowerCase().trim();
    }).filter(function (item) {
      return item === "base64" || item === "upload";
    });
    return output.length ? Array.from(new Set(output)) : ["base64"];
  }

  function cloneJSON(value) {
    if (!value || typeof value !== "object") return value || null;
    try {
      return JSON.parse(JSON.stringify(value));
    } catch (error) {
      return value;
    }
  }

  function wait(ms) {
    return new Promise(function (resolve) {
      setTimeout(resolve, Math.max(0, Number(ms) || 0));
    });
  }

  function premiumFeatures(enabled) {
    var features = {};
    Object.keys(PREMIUM_FEATURE_LABELS).forEach(function (feature) {
      features[feature] = !!enabled;
    });
    return features;
  }

  function cleanPremiumFeature(feature) {
    feature = String(feature || "").trim();
    if (feature === "signatureFlow") return "signature_flow";
    return feature;
  }

  function premiumFeatureLabel(feature) {
    feature = cleanPremiumFeature(feature);
    return PREMIUM_FEATURE_LABELS[feature] || feature || "Premium feature";
  }

  function normalizeLicenseMember(member) {
    member = member && typeof member === "object" ? member : {};
    var role = member.role || member.member_role || member.plan_role || member.type || "";
    return {
      name: String(member.name || member.full_name || member.member_name || member.email || "").trim(),
      email: String(member.email || member.member_email || "").trim(),
      role: String(role || "").trim(),
      feature_access: member.feature_access && typeof member.feature_access === "object" ? member.feature_access : {}
    };
  }

  function memberHasFeature(member, feature) {
    member = normalizeLicenseMember(member);
    feature = cleanPremiumFeature(feature);
    var access = member.feature_access || {};
    return access[feature] === true || access[feature] === "true" || access[feature] === 1 || access[feature] === "1";
  }

  function premiumFeatureForTool(type) {
    var definition = PLUGIN_TOOLS[type] || {};
    return cleanPremiumFeature(definition.premiumFeature || definition.premium || PREMIUM_TOOL_FEATURES[type] || "");
  }

  function normalizePremiumFeatureList(value) {
    if (typeof value === "string") value = value.split(",");
    if (!Array.isArray(value)) value = Object.keys(PREMIUM_FEATURE_LABELS);
    var seen = {};
    return value
      .map(cleanPremiumFeature)
      .filter(function (feature) {
        if (!feature || seen[feature]) return false;
        seen[feature] = true;
        return true;
      });
  }

  function readPremiumBundleError(text, fallback) {
    try {
      var payload = JSON.parse(text || "{}");
      return payload.error || payload.message || fallback;
    } catch (error) {
      return text || fallback;
    }
  }

  function inferUpgradeUrl(options) {
    options = options || {};
    var explicit = options.upgradeUrl || options.upgrade_url || options.billingUrl || options.billing_url || "";
    if (explicit) return String(explicit);
    var endpoint = String(options.verifyEndpoint || options.verify || options.endpoint || options.usageEndpoint || options.pluginEndpoint || "").trim();
    if (!endpoint) return "";
    return endpoint.replace(/\/api\/[^\/?#]+(?:[?#].*)?$/i, "/dashboard.php#billing");
  }

  function todayKey() {
    return new Date().toISOString().slice(0, 10);
  }

  function localStorageGet(key) {
    try {
      return typeof localStorage !== "undefined" ? localStorage.getItem(key) : null;
    } catch (error) {
      return null;
    }
  }

  function localStorageSet(key, value) {
    try {
      if (typeof localStorage !== "undefined") localStorage.setItem(key, value);
    } catch (error) {}
  }

  function isPremiumServerMode(options) {
    options = options || {};
    var mode = String(options.mode || options.premiumMode || "").toLowerCase();
    return mode === "server" || mode === "proxy" || options.serverMode === true || options.proxy === true || options.useServerProxy === true;
  }

  function premiumEndpointAction(endpoint, action) {
    endpoint = String(endpoint || "").trim();
    if (!endpoint) return "";
    if (/\/api\/[^\/?#]+\.php(?:[?#].*)?$/i.test(endpoint) && !/blockwriteai-premium\.php(?:[?#].*)?$/i.test(endpoint)) return endpoint;
    var separator = endpoint.indexOf("?") >= 0 ? "&" : "?";
    return endpoint + separator + "action=" + encodeURIComponent(action);
  }

  function premiumEndpointReplaceAction(endpoint, action) {
    endpoint = String(endpoint || "").trim();
    if (!endpoint) return "";
    if (/[?&](?:action|route)=/i.test(endpoint)) {
      return endpoint.replace(/([?&](?:action|route)=)[^&#]*/i, "$1" + encodeURIComponent(action));
    }
    return premiumEndpointAction(endpoint, action);
  }

  function resolveCloudEndpoints(options) {
    options = options || {};
    var endpoints = Object.assign({}, PLATFORM_ENDPOINTS, options.endpoints || {});
    var proxyEndpoint = String(options.endpoint || options.cloudProxy || options.proxyEndpoint || endpoints.cloudProxy || "").trim();
    if (proxyEndpoint) {
      endpoints.cloudProxy = proxyEndpoint;
      endpoints.premiumPlugins = premiumEndpointAction(proxyEndpoint, "premium_plugins");
      endpoints.licenseVerify = premiumEndpointAction(proxyEndpoint, "license_verify");
      endpoints.aiUsage = premiumEndpointAction(proxyEndpoint, "ai_usage");
      endpoints.aiUsageDetail = premiumEndpointAction(proxyEndpoint, "ai_usage_detail");
      endpoints.aiGenerate = premiumEndpointAction(proxyEndpoint, "ai_generate");
      endpoints.aiGenerateStream = premiumEndpointAction(proxyEndpoint, "ai_generate_stream");
      endpoints.aiHistory = premiumEndpointAction(proxyEndpoint, "ai_history");
      endpoints.premiumUsage = premiumEndpointAction(proxyEndpoint, "premium_usage");
      endpoints.premiumEvent = premiumEndpointAction(proxyEndpoint, "premium_event");
      endpoints.signatureEvent = premiumEndpointAction(proxyEndpoint, "signature_event");
      endpoints.letterheadSettings = premiumEndpointAction(proxyEndpoint, "letterhead_settings");
    }
    return endpoints;
  }

  function cloudPremiumConfig(options) {
    options = options || {};
    var endpoints = resolveCloudEndpoints(options);
    var licenseKey = String(options.licenseKey || options.license_key || options.license || "").trim();
    var sessionToken = String(options.sessionToken || options.session_token || "").trim();
    var features = normalizePremiumFeatureList(options.features || options.pluginFeatures || options.premiumPluginFeatures);
    return {
      endpoints: endpoints,
      premium: {
        licenseKey: licenseKey,
        sessionToken: sessionToken,
        verifyEndpoint: endpoints.licenseVerify,
        usageEndpoint: endpoints.aiUsage,
        usageDetailEndpoint: endpoints.aiUsageDetail,
        pluginEndpoint: endpoints.premiumPlugins,
        featureUsageEndpoint: endpoints.premiumUsage,
        eventEndpoint: endpoints.premiumEvent,
        signatureEventEndpoint: endpoints.signatureEvent,
        autoLoadPlugins: options.autoLoadPlugins !== false,
        features: features
      },
      ai: {
        endpoint: endpoints.aiGenerate,
        streamEndpoint: endpoints.aiGenerateStream,
        historyEndpoint: endpoints.aiHistory
      },
      letterheadSettingsEndpoint: endpoints.letterheadSettings
    };
  }

  function resolveDynamicValue(value, fallback) {
    if (typeof value === "function") return value();
    return value !== undefined ? value : fallback;
  }

  function customerStorageConfig(options) {
    options = options || {};
    var endpoint = String(options.endpoint || options.storageEndpoint || "api.php").trim();
    var headers = Object.assign({ "Content-Type": "application/json", Accept: "application/json" }, options.headers || {});
    function request(action, payload) {
      if (!endpoint) return Promise.reject(new Error("Missing BlockWriteAI storage endpoint."));
      if (typeof fetch !== "function") return Promise.reject(new Error("This browser does not support fetch()."));
      payload = Object.assign({}, payload || {}, { action: action });
      return fetch(endpoint, {
        method: "POST",
        headers: headers,
        body: JSON.stringify(payload)
      }).then(function (response) {
        return response.json().catch(function () {
          return {};
        }).then(function (json) {
          if (!response.ok || json.ok === false || json.success === false) {
            throw new Error(json.error || json.message || "BlockWriteAI storage request failed.");
          }
          return json;
        });
      });
    }
    function documentId() {
      return resolveDynamicValue(options.documentId || options.document_id || options.id, 0);
    }
    function documentTitle() {
      return String(resolveDynamicValue(options.title, "") || "").trim();
    }
    function saveDocument(data, extra) {
      extra = extra || {};
      return request(extra.action || "document.save", Object.assign({
        id: documentId(),
        document_id: documentId(),
        title: documentTitle(),
        data: data
      }, extra.payload || {}));
    }
    return {
      endpoint: endpoint,
      request: request,
      saveDocument: saveDocument,
      persistence: {
        save: saveDocument,
        comments: {
          save: function (context) {
            context = context || {};
            return saveDocument(context.data || context.document || {}, { action: "comments.save" });
          },
          hydrate: function (context) {
            context = context || {};
            return request("comments.hydrate", {
              id: context.documentId || documentId(),
              document_id: context.documentId || documentId(),
              data: context.data || context.document || {}
            });
          }
        },
        signatures: {
          save: function (context) {
            context = context || {};
            return request("signature.save", {
              document_id: context.documentId || documentId(),
              signature_type: context.signatureType || context.type || "signature",
              signature_key: context.signatureKey || context.key || "",
              payload: context.signature || context.payload || context
            });
          },
          list: function (context) {
            context = context || {};
            return request("signature.list", {
              document_id: context.documentId || documentId()
            });
          }
        }
      }
    };
  }

  function loadPremiumPluginBundle(options) {
    options = options || {};
    var serverMode = isPremiumServerMode(options);
    var endpoint = String(options.pluginEndpoint || options.pluginsEndpoint || options.bundleEndpoint || options.endpoint || PLATFORM_ENDPOINTS.premiumPlugins).trim();
    var licenseKey = String(options.licenseKey || options.license_key || options.license || "").trim();
    var explicitAuthorizationToken = String(options.authorizationToken || options.authorization_token || "").trim();
    var authorizationToken = String(explicitAuthorizationToken || (serverMode || licenseKey ? "" : localStorageGet("bwai_authorization_token")) || "").trim();
    var sessionToken = String(options.sessionToken || options.session_token || "").trim();
    var signatureEventEndpoint = String(options.signatureEventEndpoint || options.signatureEvent || PLATFORM_ENDPOINTS.signatureEvent).trim();
    var features = normalizePremiumFeatureList(options.features || options.pluginFeatures);
    if (!endpoint) return Promise.reject(new Error("Missing premium plugin endpoint."));
    if (!serverMode && !authorizationToken && !licenseKey) return Promise.reject(new Error("Missing premium authorization token."));
    if (typeof fetch !== "function") return Promise.reject(new Error("This browser does not support fetch()."));
    if (typeof document === "undefined") return Promise.reject(new Error("Premium plugins can only be loaded in a browser."));
    function applyRuntimeContext() {
      if (typeof window === "undefined") return;
      window.BlockWriteAIAuthorizationToken = authorizationToken;
      window.BlockWriteAILicenseKey = licenseKey;
      window.BlockWriteAISessionToken = sessionToken;
      window.BlockWriteAISignatureEventUrl = signatureEventEndpoint;
      BlockWriteAI.platformEndpoints = Object.assign({}, BlockWriteAI.platformEndpoints || {}, {
        cloudProxy: endpoint,
        premiumPlugins: endpoint,
        sheetAnalyzerEndpoint: function (name) {
          return premiumEndpointReplaceAction(endpoint, "sheet_analyzer_" + name);
        }
      });
    }
    var cacheKey = endpoint + "|" + (authorizationToken || licenseKey).slice(-24) + "|" + features.slice().sort().join(",");
    var allowBundleMemoryCache = !!options.cachePremiumBundle;
    if (allowBundleMemoryCache && LOADED_PREMIUM_PLUGIN_BUNDLES[cacheKey]) {
      applyRuntimeContext();
      return LOADED_PREMIUM_PLUGIN_BUNDLES[cacheKey];
    }
    var bundleRequest = fetch(endpoint, {
      method: "POST",
      cache: "no-store",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/javascript, application/json;q=0.9, */*;q=0.8",
        "Cache-Control": "no-cache"
      },
      body: JSON.stringify({
        authorization_token: serverMode ? undefined : authorizationToken || undefined,
        license_key: serverMode || authorizationToken ? undefined : licenseKey,
        session_token: sessionToken,
        features: features,
        domain: typeof window !== "undefined" && window.location ? window.location.origin : "",
        cache_bust: Date.now()
      })
    })
      .then(function (response) {
        return response.text().then(function (text) {
          if (!response.ok) {
            throw new Error(readPremiumBundleError(text, "Premium plugin license verification failed."));
          }
          return {
            text: text,
            response: response
          };
        });
      })
      .then(function (payload) {
        applyRuntimeContext();
        var script = document.createElement("script");
        script.type = "text/javascript";
        script.setAttribute("data-blockwriteai-premium-bundle", "true");
        script.text = payload.text + "\n//# sourceURL=blockwriteai-premium-plugins.js";
        document.head.appendChild(script);
        return {
          ok: true,
          features: features
        };
      })
      .catch(function (error) {
        delete LOADED_PREMIUM_PLUGIN_BUNDLES[cacheKey];
        throw error;
      });
    if (allowBundleMemoryCache) LOADED_PREMIUM_PLUGIN_BUNDLES[cacheKey] = bundleRequest;
    return bundleRequest;
  }

  function resolvePremiumOptions(options) {
    options = options || {};
    var premium = options.premium || options.Premium || {};
    if (premium === false) premium = { disabled: true };
    var serverMode = isPremiumServerMode(Object.assign({}, options, premium));
    var serverEndpoint = String(
      premium.serverEndpoint ||
        premium.proxyEndpoint ||
        (serverMode ? premium.endpoint : "") ||
        options.premiumServerEndpoint ||
        options.premiumProxyEndpoint ||
        options.serverEndpoint ||
        ""
    ).trim();
    var licenseKey =
      options.licenseKey ||
      options.license_key ||
      options.license ||
      premium.licenseKey ||
      premium.license_key ||
      premium.key ||
      "";
    var explicitAuthorizationToken = String(premium.authorizationToken || premium.authorization_token || options.authorizationToken || options.authorization_token || "").trim();
    var storedAuthorizationToken = serverMode || String(licenseKey || "").trim() || explicitAuthorizationToken ? "" : localStorageGet("bwai_authorization_token");
    return {
      disabled: !!premium.disabled,
      serverMode: serverMode,
      serverEndpoint: serverEndpoint,
      licenseKey: String(licenseKey || "").trim(),
      authorizationToken: String(explicitAuthorizationToken || storedAuthorizationToken || "").trim(),
      sessionToken: String(premium.sessionToken || premium.session_token || options.sessionToken || options.session_token || "").trim(),
      verifyEndpoint: String(serverMode && serverEndpoint ? premiumEndpointAction(serverEndpoint, "license_verify") : premium.verifyEndpoint || premium.verify || premium.endpoint || options.licenseVerifyEndpoint || PLATFORM_ENDPOINTS.licenseVerify).trim(),
      usageEndpoint: String(serverMode && serverEndpoint ? premiumEndpointAction(serverEndpoint, "ai_usage") : premium.usageEndpoint || premium.usage || options.aiUsageEndpoint || PLATFORM_ENDPOINTS.aiUsage).trim(),
      featureUsageEndpoint: String(serverMode && serverEndpoint ? premiumEndpointAction(serverEndpoint, "premium_usage") : premium.featureUsageEndpoint || premium.premiumUsageEndpoint || options.featureUsageEndpoint || options.premiumUsageEndpoint || PLATFORM_ENDPOINTS.premiumUsage).trim(),
      usageDetailEndpoint: String(serverMode && serverEndpoint ? premiumEndpointAction(serverEndpoint, "ai_usage_detail") : premium.usageDetailEndpoint || premium.usageDetail || options.aiUsageDetailEndpoint || "").trim(),
      pluginEndpoint: String(serverMode && serverEndpoint ? premiumEndpointAction(serverEndpoint, "premium_plugins") : premium.pluginEndpoint || premium.pluginsEndpoint || premium.bundleEndpoint || options.premiumPluginEndpoint || PLATFORM_ENDPOINTS.premiumPlugins).trim(),
      eventEndpoint: String(serverMode && serverEndpoint ? premiumEndpointAction(serverEndpoint, "premium_event") : premium.eventEndpoint || premium.premiumEventEndpoint || options.eventEndpoint || options.premiumEventEndpoint || PLATFORM_ENDPOINTS.premiumEvent).trim(),
      signatureEventEndpoint: String(serverMode && serverEndpoint ? premiumEndpointAction(serverEndpoint, "signature_event") : premium.signatureEventEndpoint || premium.signatureEvent || options.signatureEventEndpoint || PLATFORM_ENDPOINTS.signatureEvent).trim(),
      upgradeUrl: inferUpgradeUrl(Object.assign({}, options, premium)),
      pluginFeatures: normalizePremiumFeatureList(premium.features || premium.pluginFeatures || options.premiumPluginFeatures),
      autoLoadPlugins: !!(premium.autoLoadPlugins || premium.loadPlugins || options.autoLoadPremiumPlugins)
    };
  }

  function initialPremiumState(options) {
    if (options.disabled) {
      return {
        status: "disabled",
        valid: true,
        message: "Premium checks are disabled.",
        features: premiumFeatures(true),
        limits: {},
        usage: {},
        member: null
      };
    }
    return {
      status: (options.serverMode || options.authorizationToken || options.licenseKey) && options.verifyEndpoint ? "pending" : "missing",
      valid: false,
      message: (options.serverMode || options.authorizationToken || options.licenseKey) ? "Premium authorization is pending." : "License activation is required for premium features.",
      features: premiumFeatures(false),
      limits: {},
      usage: {},
      member: null
    };
  }

  function extractDocumentMeta(data) {
    var meta = {};
    if (!data || typeof data !== "object") return meta;
    Object.keys(data).forEach(function (key) {
      if (key === "time" || key === "version" || key === "blocks") return;
      meta[key] = cloneJSON(data[key]);
    });
    return meta;
  }

  function resolvePersistenceOptions(persistence, options) {
    options = options || {};
    if (!persistence && options.persistence !== undefined) persistence = options.persistence;
    if (!persistence && options.Persistence !== undefined) persistence = options.Persistence;
    if (!persistence) return null;
    if (typeof persistence === "function") persistence = { save: persistence };
    if (!persistence || typeof persistence !== "object") return null;
    return persistence;
  }

  function persistenceDocumentId(persistence, options, data) {
    persistence = persistence || {};
    options = options || {};
    data = data || {};
    return persistence.documentId || persistence.document_id || options.documentId || options.document_id || options.id || data.documentId || data.document_id || data.id || "";
  }

  function callPersistenceHook(hook, payload, editor) {
    if (typeof hook !== "function") return Promise.resolve(null);
    try {
      return Promise.resolve(hook(payload, editor));
    } catch (error) {
      return Promise.reject(error);
    }
  }

  function fileExtension(file) {
    var name = file && file.name ? file.name : "";
    var parts = name.split(".");
    return parts.length > 1 ? parts.pop().toLowerCase() : "";
  }

  function isImageFile(file) {
    return !!(file && file.type && file.type.indexOf("image/") === 0);
  }

  function isAllowedAttachmentFile(file) {
    if (!file || isImageFile(file)) return false;
    if (file.type && file.type.indexOf("audio/") === 0) return true;
    if (file.type && file.type.indexOf("video/") === 0) return true;
    if (ATTACHMENT_EXTENSIONS.has(fileExtension(file))) return true;
    return /^(audio\/|application\/pdf|application\/msword|application\/vnd\.ms-|application\/vnd\.openxmlformats|text\/plain|text\/csv|application\/rtf)/i.test(file.type || "");
  }

  function attachmentRestrictionMessage() {
    return "Attachments only allow audio, videos, and documents such as PDF, Word, Excel, PowerPoint, CSV, TXT, and RTF. Please use the Image block for images.";
  }

  function attachmentIconName(type, name) {
    var ext = fileExtension({ name: name || "" });
    if (type && type.indexOf("audio/") === 0) return "file-lines";
    if (type && type.indexOf("video/") === 0) return "file-video";
    if (ext === "pdf") return "file-pdf";
    if (ext === "xls" || ext === "xlsx" || ext === "csv" || ext === "ods") return "file-excel";
    if (ext === "ppt" || ext === "pptx" || ext === "odp") return "file-powerpoint";
    if (ext === "doc" || ext === "docx" || ext === "odt") return "file-word";
    return "file-lines";
  }

  function attachmentKindLabel(type, name) {
    var ext = fileExtension({ name: name || "" });
    if (type && type.indexOf("audio/") === 0) return "AUDIO";
    if (type && type.indexOf("video/") === 0) return "VIDEO";
    return ext ? ext.toUpperCase() : "FILE";
  }

  function attachmentKindKey(type, name) {
    var ext = fileExtension({ name: name || "" });
    if (type && type.indexOf("audio/") === 0) return "audio";
    if (type && type.indexOf("video/") === 0) return "video";
    if (ext === "pdf") return "pdf";
    if (ext === "xls" || ext === "xlsx" || ext === "csv" || ext === "ods") return "excel";
    if (ext === "ppt" || ext === "pptx" || ext === "odp") return "powerpoint";
    if (ext === "doc" || ext === "docx" || ext === "odt") return "word";
    if (ext === "txt" || ext === "rtf") return "text";
    return "file";
  }

  function normalizeImageItems(data) {
    data = data || {};
    var source = Array.isArray(data.items) ? data.items : Array.isArray(data.images) ? data.images : null;
    if (!source && (data.url || data.src)) source = [data];
    return (source || []).map(function (item) {
      item = item || {};
      return {
        url: item.url || item.src || item.base64 || "",
        base64: item.base64 || data.base64 || "",
        upload: cloneJSON(item.upload || data.upload || null),
        alt: item.alt || item.caption || "",
        caption: item.caption || "",
        alignment: cleanAlignment(item.alignment || data.alignment),
        width: Number(item.width || data.width || 100),
        displayWidth: Number(item.displayWidth || data.displayWidth || 0),
        minWidth: Number(item.minWidth || data.minWidth || 0),
        naturalWidth: Number(item.naturalWidth || data.naturalWidth || 0),
        naturalHeight: Number(item.naturalHeight || data.naturalHeight || 0),
        rotation: Number(item.rotation || data.rotation || 0),
        crop: item.crop || data.crop || "free",
        cropRatio: Number(item.cropRatio || data.cropRatio || 0),
        cropX: Number(item.cropX || data.cropX || 50),
        cropY: Number(item.cropY || data.cropY || 50),
        cropZoom: Number(item.cropZoom || data.cropZoom || 1)
      };
    }).filter(function (item) {
      return !!item.url;
    });
  }

  function clampNumber(value, min, max, fallback) {
    value = Number(value);
    if (!Number.isFinite(value)) value = fallback;
    return Math.min(max, Math.max(min, value));
  }

  function imageCropRatio(item) {
    return clampNumber(item && item.cropRatio, 0.35, 3.5, 0);
  }

  function imageCropPercent(value) {
    return clampNumber(value, 0, 100, 50);
  }

  function imageCropZoom(value) {
    return clampNumber(value, 1, 3, 1);
  }

  function imageOutputStageHTML(item) {
    var width = Math.min(100, Math.max(50, Number(item.width || 100)));
    var rotation = Number(item.rotation || 0);
    var crop = item.crop === "custom" ? "custom" : "free";
    var customRatio = imageCropRatio(item);
    var ratio = crop === "custom" && customRatio ? "aspect-ratio:" + customRatio.toFixed(3) + " / 1;" : "";
    var customWidth = Number(item.displayWidth || 0);
    var minWidth = Number(item.minWidth || 0);
    var naturalWidth = Number(item.naturalWidth || 0);
    if (customWidth > 0 && minWidth > 0) customWidth = Math.max(customWidth, minWidth);
    if (customWidth > 0 && naturalWidth > 0) customWidth = Math.min(customWidth, Math.max(minWidth || 0, naturalWidth));
    var stageWidth = customWidth > 0 ? Math.round(customWidth) : Math.round(180 * (width / 100));
    var stageStyle = "width:min(100%," + stageWidth + "px);" + ratio;
    var cropX = imageCropPercent(item.cropX);
    var cropY = imageCropPercent(item.cropY);
    var zoom = imageCropZoom(item.cropZoom);
    var imageStyle =
      "max-width:100%;" +
      (crop === "free" ? "max-height:360px;" : "max-height:none;") +
      "width:100%;" +
      (crop === "free" ? "height:auto;" : "height:100%;") +
      "object-fit:" +
      (crop === "free" ? "contain" : "cover") +
      ";object-position:" +
      cropX +
      "% " +
      cropY +
      "%;transform-origin:" +
      cropX +
      "% " +
      cropY +
      "%;--rbe-image-zoom:" +
      zoom +
      ";transform:rotate(" +
      rotation +
      "deg) scale(var(--rbe-image-zoom,1));";
    return '<div class="rbe-image-stage" style="' + escapeHTML(stageStyle) + '"><img class="rbe-output-image rbe-image-preview" src="' + escapeHTML(item.url) + '" alt="' + escapeHTML(item.alt || item.caption || "") + '" loading="lazy" style="' + escapeHTML(imageStyle) + '"></div>';
  }

  function normalizeAttachmentItems(data) {
    data = data || {};
    var source = Array.isArray(data.items) ? data.items : Array.isArray(data.attachments) ? data.attachments : null;
    if (!source && (data.url || data.name)) source = [data];
    return (source || []).map(function (item) {
      item = item || {};
      var upload = item.upload || data.upload || null;
      return {
        name: item.name || item.url || item.base64 || "Attachment",
        url: item.url || item.base64 || (upload && upload.url) || "",
        base64: item.base64 || data.base64 || "",
        upload: cloneJSON(upload),
        type: item.type || "",
        size: Number(item.size || 0),
        alignment: cleanAlignment(item.alignment || data.alignment)
      };
    }).filter(function (item) {
      return !!(item.url || item.base64 || item.upload);
    });
  }

  function placeCaretAtEnd(node) {
    if (!node || !window.getSelection) return;
    node.focus();
    if (!node.isContentEditable) return;
    var range = document.createRange();
    range.selectNodeContents(node);
    range.collapse(false);
    var selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(range);
  }

  function isEditableTarget(target) {
    if (!target) return false;
    return !!target.closest("[contenteditable='true'], textarea, input");
  }

  function defaultData() {
    return {
      time: Date.now(),
      version: VERSION,
      blocks: [
        {
          id: uid("block"),
          type: "paragraph",
          data: { text: "", alignment: "left" }
        }
      ]
    };
  }

  function cssSize(value) {
    if (value === null || value === undefined || value === false || value === "") return "";
    if (typeof value === "number" && Number.isFinite(value)) return Math.max(0, value) + "px";
    return String(value);
  }

  function blockwriteaiAssetUrl(filename) {
    var scripts = [];
    if (document.currentScript) scripts.push(document.currentScript);
    scripts = scripts.concat(Array.prototype.slice.call(document.scripts));
    var script = scripts.find(function (item) {
      return item.src && /\/blockwriteai(?:\.protected)?(?:\.min)?\.js(?:[?#].*)?$/i.test(item.src);
    });
    if (script && script.src) {
      try {
        return new URL(filename, script.src).href;
      } catch (error) {}
    }
    return "";
  }

  function inlineBlockWriteAILogoDataUrl() {
    var svg = [
      '<svg width="270" height="58" viewBox="0 0 270 58" fill="none" xmlns="http://www.w3.org/2000/svg">',
      '<defs><linearGradient id="bwai-icon" x1="8" y1="8" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#102033"/><stop offset="1" stop-color="#172A46"/></linearGradient><linearGradient id="bwai-accent" x1="16" y1="17" x2="43" y2="42" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2BEA8A"/><stop offset="1" stop-color="#10B981"/></linearGradient></defs>',
      '<rect x="2" y="7" width="44" height="44" rx="13" fill="url(#bwai-icon)"/>',
      '<rect x="13" y="17" width="9" height="9" rx="2.2" fill="#FFFFFF"/>',
      '<rect x="25" y="17" width="9" height="9" rx="2.2" fill="#FFFFFF" opacity="0.92"/>',
      '<rect x="13" y="29" width="9" height="9" rx="2.2" fill="url(#bwai-accent)"/>',
      '<path d="M27.5 38.3L37.6 28.2C38.2 27.6 39.1 27.6 39.7 28.2L43 31.5C43.6 32.1 43.6 33 43 33.6L32.9 43.7L26.1 45.1L27.5 38.3Z" fill="#FFFFFF"/>',
      '<path d="M36.1 29.7L41.5 35.1" stroke="#10B981" stroke-width="3.5" stroke-linecap="round"/>',
      '<circle cx="42" cy="45" r="5.5" fill="#19B86B" stroke="#FFFFFF" stroke-width="2.6"/>',
      '<path d="M49 18.5H54M51.5 16V21" stroke="#19B86B" stroke-width="2.4" stroke-linecap="round"/>',
      '<text x="60" y="37" font-family="Inter, Segoe UI, Arial, sans-serif" font-size="28" font-weight="850"><tspan fill="#102033">BlockWr&#305;te</tspan><tspan fill="#12A865">AI</tspan></text>',
      '<circle cx="182" cy="17.6" r="2.8" fill="#19B86B"/>',
      "</svg>"
    ].join("");
    return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg);
  }

  function resolveShellOptions(options) {
    if (!options || options.shell === false) return null;
    var raw = options.shell && typeof options.shell === "object" ? options.shell : {};
    var branding = raw.branding && typeof raw.branding === "object" ? raw.branding : options.branding && typeof options.branding === "object" ? options.branding : {};
    var maxWidth = raw.maxWidth !== undefined ? raw.maxWidth : options.maxWidth !== undefined ? options.maxWidth : options.editorMaxWidth;
    var logo =
      raw.logo !== undefined
        ? raw.logo
        : raw.logoUrl !== undefined
          ? raw.logoUrl
          : branding.logo !== undefined
            ? branding.logo
            : options.logoUrl !== undefined
              ? options.logoUrl
              : options.shellLogo !== undefined
                ? options.shellLogo
                : inlineBlockWriteAILogoDataUrl();
    var showHeader =
      raw.showBrandHeader !== undefined
        ? raw.showBrandHeader
        : options.showBrandHeader !== undefined
          ? options.showBrandHeader
          : raw.showHeader !== undefined
            ? raw.showHeader
            : options.showHeader;
    return {
      showHeader: showHeader !== false,
      title: raw.title || options.shellTitle || "BlockWriteAI",
      subtitle:
        raw.subtitle ||
        options.shellSubtitle ||
        "Block-first writing editor with AI, slash commands, inline toolbar, uploads, tables, embeds, autosave, and JSON responses.",
      heading: raw.heading || options.shellHeading || "Editor",
      status: raw.status || options.shellStatus || "Ready",
      logo: logo,
      maxWidth: cssSize(maxWidth),
      className: raw.className || options.shellClassName || ""
    };
  }

  function cleanAlignment(value) {
    return value === "center" || value === "right" ? value : "left";
  }

  function cleanTableBorderStyle(value) {
    return value === "dashed" || value === "dotted" || value === "double" ? value : "solid";
  }

  function cleanTableBorderWidth(value) {
    value = Number(value || 1);
    if (!Number.isFinite(value)) value = 1;
    return Math.min(6, Math.max(1, Math.round(value)));
  }

  function tableBorderValue(data) {
    data = data || {};
    return cleanTableBorderWidth(data.borderWidth) + "px " + cleanTableBorderStyle(data.borderStyle) + " #d7dee8";
  }

  function calloutTone(value) {
    return value === "success" || value === "warning" || value === "danger" ? value : "info";
  }

  function calloutExportStyle(tone, alignment) {
    tone = calloutTone(tone);
    var colors = {
      info: ["#2563eb", "#f6faff"],
      success: ["#07845f", "#f2fbf7"],
      warning: ["#b76b00", "#fff9ef"],
      danger: ["#cf2f2f", "#fff5f5"]
    };
    var pair = colors[tone];
    return (
      "border-left:5px solid " +
      pair[0] +
      ";background:" +
      pair[1] +
      ";padding:12px 14px;border-radius:8px;margin:0 0 16px;color:#172033;text-align:" +
      cleanAlignment(alignment) +
      ";"
    );
  }

  function codeLanguageLabel(value) {
    value = value || "plaintext";
    var match = CODE_LANGUAGES.find(function (item) {
      return item[0] === value;
    });
    return match ? match[1] : value;
  }

  function normalizeCodeLanguage(value) {
    value = String(value || "plaintext").toLowerCase();
    return CODE_LANGUAGES.some(function (item) { return item[0] === value; }) ? value : "plaintext";
  }

  function indentText(level) {
    return Array(Math.max(0, level) + 1).join("  ");
  }

  function normalizeFormattedLines(lines) {
    return lines
      .map(function (line) { return line.replace(/\s+$/g, ""); })
      .filter(function (line, index, list) {
        return line.trim() || (index > 0 && index < list.length - 1 && list[index - 1].trim() && list[index + 1].trim());
      })
      .join("\n")
      .trim();
  }

  function formatHTMLCode(code) {
    var voidTags = new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
    var tokens = String(code || "")
      .replace(/>\s+</g, "><")
      .match(/<!--[\s\S]*?-->|<!doctype[^>]*>|<\/?[\w:-]+(?:\s+[\w:-]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+))?)*\s*\/?>|[^<]+/gi);
    if (!tokens) return String(code || "").trim();
    var indent = 0;
    var lines = [];
    tokens.forEach(function (token) {
      var trimmed = token.trim();
      if (!trimmed) return;
      var close = /^<\//.test(trimmed);
      var tagMatch = trimmed.match(/^<\/?([\w:-]+)/);
      var tagName = tagMatch ? tagMatch[1].toLowerCase() : "";
      var selfClosing = /\/>$/.test(trimmed) || /^<!/i.test(trimmed) || voidTags.has(tagName);
      if (close) indent = Math.max(0, indent - 1);
      if (/^</.test(trimmed)) {
        lines.push(indentText(indent) + trimmed);
        if (!close && !selfClosing) indent += 1;
      } else {
        trimmed.split(/\s*\n+\s*/).forEach(function (part) {
          if (part.trim()) lines.push(indentText(indent) + part.trim());
        });
      }
    });
    return normalizeFormattedLines(lines);
  }

  function formatCSSCode(code) {
    var text = String(code || "").trim();
    var lines = [];
    var current = "";
    var indent = 0;
    var quote = "";
    var inComment = false;

    function pushCurrent(adjust) {
      var line = current.trim();
      if (line) lines.push(indentText(indent + (adjust || 0)) + line);
      current = "";
    }

    for (var i = 0; i < text.length; i += 1) {
      var char = text[i];
      var next = text[i + 1];
      if (inComment) {
        current += char;
        if (char === "*" && next === "/") {
          current += next;
          i += 1;
          inComment = false;
          pushCurrent();
        }
        continue;
      }
      if (quote) {
        current += char;
        if (char === "\\" && next) {
          current += next;
          i += 1;
        } else if (char === quote) {
          quote = "";
        }
        continue;
      }
      if (char === "/" && next === "*") {
        if (current.trim()) pushCurrent();
        current = "/*";
        i += 1;
        inComment = true;
        continue;
      }
      if (char === '"' || char === "'") {
        quote = char;
        current += char;
        continue;
      }
      if (char === "{") {
        current = current.trim() + " {";
        pushCurrent();
        indent += 1;
        continue;
      }
      if (char === "}") {
        pushCurrent();
        indent = Math.max(0, indent - 1);
        lines.push(indentText(indent) + "}");
        continue;
      }
      if (char === ";") {
        current += ";";
        pushCurrent();
        continue;
      }
      if (char === "\n" || char === "\r") {
        if (current.trim()) pushCurrent();
        continue;
      }
      current += char;
    }
    pushCurrent();
    return normalizeFormattedLines(lines);
  }

  function formatBraceCode(code) {
    var text = String(code || "").trim();
    var lines = [];
    var current = "";
    var indent = 0;
    var quote = "";
    var inLineComment = false;
    var inBlockComment = false;

    function pushCurrent(adjust) {
      var line = current.trim();
      if (line) lines.push(indentText(indent + (adjust || 0)) + line);
      current = "";
    }

    for (var i = 0; i < text.length; i += 1) {
      var char = text[i];
      var next = text[i + 1];
      if (inLineComment) {
        if (char === "\n" || char === "\r") {
          pushCurrent();
          inLineComment = false;
        } else {
          current += char;
        }
        continue;
      }
      if (inBlockComment) {
        current += char;
        if (char === "*" && next === "/") {
          current += next;
          i += 1;
          inBlockComment = false;
          pushCurrent();
        }
        continue;
      }
      if (quote) {
        current += char;
        if (char === "\\" && next) {
          current += next;
          i += 1;
        } else if (char === quote && quote !== "`") {
          quote = "";
        } else if (char === "`" && quote === "`") {
          quote = "";
        }
        continue;
      }
      if (char === "/" && next === "/") {
        current += "//";
        i += 1;
        inLineComment = true;
        continue;
      }
      if (char === "/" && next === "*") {
        if (current.trim()) pushCurrent();
        current = "/*";
        i += 1;
        inBlockComment = true;
        continue;
      }
      if (char === '"' || char === "'" || char === "`") {
        quote = char;
        current += char;
        continue;
      }
      if (char === "{") {
        current = current.trim() + " {";
        pushCurrent();
        indent += 1;
        continue;
      }
      if (char === "}") {
        pushCurrent();
        indent = Math.max(0, indent - 1);
        current = "}";
        if (next === ";" || next === ",") {
          current += next;
          i += 1;
        }
        pushCurrent();
        continue;
      }
      if (char === ";") {
        current += ";";
        pushCurrent();
        continue;
      }
      if (char === "\n" || char === "\r") {
        if (current.trim()) pushCurrent();
        continue;
      }
      current += char;
    }
    pushCurrent();
    return normalizeFormattedLines(lines);
  }

  function codeHasRegexLiteral(code) {
    return /(^|[=(:,\[\{!&|?;]\s*)\/(?![/*])(?:\\.|[^\/\n])+\/[dgimsuvy]*/.test(String(code || ""));
  }

  function formatCodeByLanguage(code, language) {
    code = String(code || "").trim();
    language = normalizeCodeLanguage(language);
    if (!code) return "";
    if (language === "json") {
      try {
        return JSON.stringify(JSON.parse(code), null, 2);
      } catch (error) {
        return code;
      }
    }
    if (language === "html") {
      return formatHTMLCode(code);
    }
    if (language === "css") {
      return formatCSSCode(code);
    }
    if ((language === "javascript" || language === "typescript") && codeHasRegexLiteral(code)) {
      return code;
    }
    if (language === "javascript" || language === "typescript" || language === "java" || language === "csharp" || language === "cpp" || language === "php") {
      return formatBraceCode(code);
    }
    return code;
  }

  function normalizeHexColor(value, fallback) {
    value = String(value || "").trim();
    if (/^#[0-9a-f]{3}$/i.test(value)) {
      return (
        "#" +
        value
          .slice(1)
          .split("")
          .map(function (char) { return char + char; })
          .join("")
      ).toLowerCase();
    }
    return /^#[0-9a-f]{6}$/i.test(value) ? value.toLowerCase() : fallback;
  }

  function readableTextColor(hex) {
    hex = normalizeHexColor(hex, "#d7dee8").slice(1);
    var r = parseInt(hex.slice(0, 2), 16);
    var g = parseInt(hex.slice(2, 4), 16);
    var b = parseInt(hex.slice(4, 6), 16);
    var luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
    return luminance > 0.56 ? "#172033" : "#f8fafc";
  }

  function codeHeaderColor(data) {
    return normalizeHexColor(data && data.headerColor, "#e5e7eb");
  }

  function codeBackgroundColor(data) {
    return normalizeHexColor(data && data.backgroundColor, "#d7dee8");
  }

  function codeColorStyle(data) {
    var header = codeHeaderColor(data);
    var background = codeBackgroundColor(data);
    return (
      "--rbe-code-header-bg:" +
      header +
      ";--rbe-code-header-text:" +
      readableTextColor(header) +
      ";--rbe-code-bg:" +
      background +
      ";--rbe-code-text:" +
      readableTextColor(background) +
      ";"
    );
  }

  function applyCodeColorVars(node, data) {
    if (!node) return;
    var header = codeHeaderColor(data);
    var background = codeBackgroundColor(data);
    node.style.setProperty("--rbe-code-header-bg", header);
    node.style.setProperty("--rbe-code-header-text", readableTextColor(header));
    node.style.setProperty("--rbe-code-bg", background);
    node.style.setProperty("--rbe-code-text", readableTextColor(background));
  }

  function renderCodeOutput(data) {
    data = data || {};
    var language = normalizeCodeLanguage(data.language);
    return '<div class="rbe-output-code" style="' + escapeHTML(codeColorStyle(data)) + '"><div class="rbe-output-code-title">' + escapeHTML(codeLanguageLabel(language)) + '</div><pre><code class="language-' + escapeHTML(language) + '">' + escapeHTML(data.code || "") + "</code></pre></div>";
  }

  function listItemText(item) {
    return typeof item === "object" && item ? item.text || "" : item || "";
  }

  function listItemLevel(item) {
    var level = typeof item === "object" && item ? Number(item.level || 0) : 0;
    if (!Number.isFinite(level)) level = 0;
    return Math.min(4, Math.max(0, Math.round(level)));
  }

  function normalizeListItem(item) {
    return {
      text: sanitizeHTML(listItemText(item)),
      level: listItemLevel(item),
      alignment: cleanAlignment(typeof item === "object" && item ? item.alignment : "left"),
      style: typeof item === "object" && item && item.style === "ordered" ? "ordered" : typeof item === "object" && item && item.style === "unordered" ? "unordered" : ""
    };
  }

  function normalizeChecklistItem(item) {
    item = typeof item === "object" && item ? item : { text: item || "" };
    return {
      text: sanitizeHTML(item.text || ""),
      checked: !!item.checked,
      level: listItemLevel(item)
    };
  }

  function isEmptyListItem(item) {
    return listItemText(item).replace(/<br\s*\/?>/gi, "").trim() === "";
  }

  function stripListMarkerText(text) {
    return String(text || "")
      .replace(/^\s*(?:&bull;|&#8226;|&#x2022;|\u2022|\u00b7|-|\*)\s*/i, "")
      .replace(/^\s*(?:\d+|[a-z]|[ivxlcdm]+)[.)]\s*/i, "");
  }

  function renderNestedList(items, style, alignment) {
    var tag = style === "ordered" ? "ol" : "ul";
    var roots = [];
    var stack = [roots];
    var previousLevel = 0;
    (items || []).map(normalizeListItem).filter(function (item) { return !isEmptyListItem(item); }).forEach(function (item) {
      if (item.level > previousLevel + 1) item.level = previousLevel + 1;
      while (stack.length > item.level + 1) stack.pop();
      while (item.level > 0 && !stack[item.level]) item.level -= 1;
      var node = { text: stripListMarkerText(item.text), alignment: cleanAlignment(item.alignment || alignment), style: item.style || style, children: [] };
      (stack[item.level] || roots).push(node);
      stack[item.level + 1] = node.children;
      previousLevel = item.level;
    });
    function renderNodes(nodes, counters) {
      var localCounters = counters.slice();
      return nodes.map(function (node) {
        var nodeStyle = node.style || style;
        if (nodeStyle === "ordered") {
          var nextCounters = localCounters.slice();
          nextCounters[nextCounters.length - 1] += 1;
          localCounters = nextCounters.slice();
          var marker = nextCounters.join(".") + ".";
          return '<li style="text-align:' + cleanAlignment(node.alignment || alignment) + '"><span class="rbe-output-list-marker">' + marker + '</span><span class="rbe-output-list-text">' + renderOutputInline(node.text) + "</span>" + (node.children.length ? '<ol class="rbe-output-outline-list">' + renderNodes(node.children, nextCounters.concat(0)) + "</ol>" : "") + "</li>";
        }
        return '<li style="text-align:' + cleanAlignment(node.alignment || alignment) + '"><span class="rbe-output-list-marker">&bull;</span><span class="rbe-output-list-text">' + renderOutputInline(node.text) + "</span>" + (node.children.length ? '<ul class="rbe-output-outline-list">' + renderNodes(node.children, counters.concat(0)) + "</ul>" : "") + "</li>";
      }).join("");
    }
    return '<' + tag + ' class="rbe-output-outline-list" style="text-align:' + cleanAlignment(alignment) + '">' + renderNodes(roots, [0]) + "</" + tag + ">";
  }

  function renderNestedChecklist(items) {
    var roots = [];
    var stack = [roots];
    var previousLevel = 0;
    (items || []).map(normalizeChecklistItem).filter(function (item) { return !isEmptyListItem(item); }).forEach(function (item) {
      if (item.level > previousLevel + 1) item.level = previousLevel + 1;
      while (stack.length > item.level + 1) stack.pop();
      while (item.level > 0 && !stack[item.level]) item.level -= 1;
      var node = { text: item.text, checked: item.checked, level: item.level, children: [] };
      (stack[item.level] || roots).push(node);
      stack[item.level + 1] = node.children;
      previousLevel = item.level;
    });
    function iconFor(node) {
      var nested = node.level > 0;
      var checkedClass = node.checked ? " is-checked" : "";
      var nestedClass = nested ? " is-nested" : "";
      var shape = nested
        ? '<circle cx="12" cy="12" r="8.5"></circle>'
        : '<rect x="4" y="4" width="16" height="16" rx="3.5"></rect>';
      var check = node.checked ? '<path d="m7.5 12.2 3 3.1 6-6.8"></path>' : "";
      return '<span class="rbe-output-check' + checkedClass + nestedClass + '" aria-hidden="true"><svg class="rbe-output-check-icon" viewBox="0 0 24 24" focusable="false">' + shape + check + "</svg></span>";
    }
    function renderNodes(nodes) {
      return nodes.map(function (node) {
        return '<li class="rbe-output-checklist-item" data-level="' + node.level + '">' + iconFor(node) + '<span class="rbe-output-checklist-text">' + renderOutputInline(node.text) + "</span>" + (node.children.length ? '<ul class="rbe-output-checklist rbe-output-checklist-nested">' + renderNodes(node.children) + "</ul>" : "") + "</li>";
      }).join("");
    }
    return '<ul class="rbe-output-checklist">' + renderNodes(roots) + "</ul>";
  }

  function normalizeCommentStatus(status) {
    status = String(status || "pending").trim().toLowerCase();
    return status === "resolved" || status === "declined" ? status : "pending";
  }

  function commentStatusLabel(status) {
    status = normalizeCommentStatus(status);
    return status === "resolved" ? "Accepted" : status === "declined" ? "Rejected" : "Pending";
  }

  function annotationEditIconHTML() {
    return '<span class="rbe-output-annotation-edit-glyph" aria-hidden="true">&#9998;</span><span class="rbe-sr-only">Edit comment</span>';
  }

  function annotationReplyIconHTML() {
    return svgIcon("reply", "Reply");
  }

  function formatCommentDate(value) {
    if (!value) return "";
    var date = value instanceof Date ? value : new Date(value);
    if (isNaN(date.getTime())) return "";
    return date.toLocaleString();
  }

  function parseCommentThread(value, legacyReplyText, legacyReplyDate) {
    try {
      var parsed = JSON.parse(value || "[]");
      if (Array.isArray(parsed)) {
        return parsed
          .map(function (item) {
            return item && item.text ? { text: String(item.text), date: String(item.date || ""), memberName: String(item.memberName || ""), memberRole: String(item.memberRole || "") } : null;
          })
          .filter(Boolean);
      }
    } catch (error) {
    }
    if (legacyReplyText) return [{ text: String(legacyReplyText), date: String(legacyReplyDate || "") }];
    return [];
  }

  function serializeCommentThread(thread) {
    return JSON.stringify(Array.isArray(thread) ? thread : []);
  }

  function buildAnnotationTooltipHTML(dateLabel, member) {
    member = normalizeLicenseMember(member || {});
    if (!dateLabel && !member.name && !member.role) return "";
    return (
      (member.name ? '<span><strong>Name :</strong> ' + escapeHTML(member.name) + "</span>" : "") +
      (member.role ? '<span><strong>Role :</strong> ' + escapeHTML(member.role) + "</span>" : "") +
      (dateLabel ? '<span><strong>Date &amp; Time :</strong> ' + escapeHTML(dateLabel) + "</span>" : "")
    );
  }

  function buildAnnotationReplyItemHTML(reply, canReply, fallbackMember) {
    reply = reply || {};
    var member = { name: reply.memberName || (fallbackMember && fallbackMember.name) || "", role: reply.memberRole || (fallbackMember && fallbackMember.role) || "" };
    return '<span class="rbe-output-annotation-thread-item"><span class="rbe-output-annotation-thread-head"><span class="rbe-output-annotation-thread-label">Reply</span><span class="rbe-output-annotation-thread-meta">' + (canReply ? '<button type="button" class="rbe-output-annotation-reply-toggle rbe-output-annotation-reply-toggle--icon" title="Reply">' + annotationReplyIconHTML() + "</button>" : "") + buildAnnotationDateChipHTML(formatCommentDate(reply.date || ""), member) + '</span></span><span class="rbe-output-annotation-thread-text">' + escapeHTML(reply.text || "") + "</span></span>";
  }

  function buildAnnotationDateChipHTML(dateLabel, member) {
    return '<span class="rbe-output-annotation-date-chip"><span class="rbe-output-annotation-date-icon" aria-hidden="true">&#128339;</span><span class="rbe-output-annotation-date-tooltip">' + buildAnnotationTooltipHTML(dateLabel, member) + "</span></span>";
  }

  function buildAnnotationCardHTML(comment, status, reason, options) {
    options = options || {};
    status = normalizeCommentStatus(status);
    reason = String(reason || "");
    var interactive = options.interactive !== false;
    var canReply = !!options.canReply;
    var createdAt = options.createdAt || "";
    var updatedAt = options.updatedAt || "";
    var replyText = String(options.replyText || "");
    var replyDate = options.replyDate || "";
    var thread = Array.isArray(options.thread) ? options.thread : parseCommentThread(options.threadRaw || "", replyText, replyDate);
    var hasThread = thread.length > 0;
    var showInlineEdit = interactive && status !== "pending" && !!options.inlineEditInRejectMeta;
    var showRejectInlineEdit = showInlineEdit;
    var showEdit = interactive && status !== "pending" && !showInlineEdit;
    var showActions = interactive && status === "pending";
    var showRejected = status === "declined" && !!reason;
    var showReplyButton = !!options.canReply;
    var showRejectReplyButton = showReplyButton && !showInlineEdit && !hasThread;
    var statusDate = formatCommentDate(status === "pending" ? createdAt : updatedAt || createdAt);
    var reasonDate = formatCommentDate(updatedAt || createdAt);
    var createdMember = { name: options.memberName || "", role: options.memberRole || "" };
    var updatedMember = { name: options.updatedMemberName || options.memberName || "", role: options.updatedMemberRole || options.memberRole || "" };
    return (
      '<span class="rbe-output-annotation-card ' + (status === "resolved" ? "is-resolved" : status === "declined" ? "is-declined" : "is-pending") + '">' +
      '<span class="rbe-output-annotation-head">' +
      '<span class="rbe-output-annotation-icon" aria-hidden="true">i</span>' +
        '<span class="rbe-output-annotation-head-meta"><span class="rbe-output-annotation-heading"><strong>Comment</strong></span></span>' +
        '<span class="rbe-output-annotation-head-side"><span class="rbe-output-annotation-state-line"><span class="rbe-output-annotation-state-text">' + escapeHTML(commentStatusLabel(status)) + '</span></span><span class="rbe-output-annotation-tools"><button type="button" class="rbe-output-annotation-edit" title="Edit comment"' + (showEdit ? "" : " hidden") + ">" + annotationEditIconHTML() + "</button></span></span>" +
        "</span>" +
        '<span class="rbe-output-annotation-divider" aria-hidden="true"></span>' +
        '<span class="rbe-output-annotation-body"><span class="rbe-output-annotation-note">' + escapeHTML(comment) + '</span><span class="rbe-output-annotation-body-meta">' + buildAnnotationDateChipHTML(statusDate, status === "pending" ? createdMember : updatedMember) + "</span></span>" +
        '<span class="rbe-output-annotation-reject-block"' + (showRejected ? "" : " hidden") + '><span class="rbe-output-annotation-reject-head"><span class="rbe-output-annotation-reject-label">Reject Reason :</span></span><span class="rbe-output-annotation-reject-row"><span class="rbe-output-annotation-reject-text">' + escapeHTML(reason) + '</span><span class="rbe-output-annotation-reject-meta">' + (showRejectInlineEdit ? '<button type="button" class="rbe-output-annotation-edit rbe-output-annotation-reply-toggle--icon" title="Edit comment">' + annotationEditIconHTML() + "</button>" : showRejectReplyButton ? '<button type="button" class="rbe-output-annotation-reply-toggle rbe-output-annotation-reply-toggle--icon" title="Reply">' + annotationReplyIconHTML() + "</button>" : "") + buildAnnotationDateChipHTML(reasonDate, updatedMember) + '</span></span><span class="rbe-output-annotation-decline-form" hidden><textarea class="rbe-output-annotation-reply" placeholder="Write reject reason">' + escapeHTML(reason) + '</textarea><span class="rbe-output-annotation-form-actions"><button type="button" class="rbe-output-annotation-cancel-edit" title="Cancel edit">Cancel</button><button type="button" class="rbe-output-annotation-save-decline">Save</button></span></span></span>' +
        (showReplyButton ? '<span class="rbe-output-annotation-reply-form" hidden><textarea class="rbe-output-annotation-reply-input" placeholder="Write reply..."></textarea><span class="rbe-output-annotation-form-actions"><button type="button" class="rbe-output-annotation-reply-cancel">Cancel</button><button type="button" class="rbe-output-annotation-reply-save">Save reply</button></span></span>' : "") +
        '<span class="rbe-output-annotation-thread-block"' + (thread.length ? "" : " hidden") + '><span class="rbe-output-annotation-divider" aria-hidden="true"></span><span class="rbe-output-annotation-thread-list">' + thread.slice().reverse().map(function (item, index) { return buildAnnotationReplyItemHTML(item, showReplyButton && index === 0, updatedMember); }).join("") + "</span></span>" +
        '<span class="rbe-output-annotation-history" aria-live="polite"></span>' +
        (interactive
        ? '<span class="rbe-output-annotation-actions"' + (showActions ? "" : " hidden") + '><button type="button" class="rbe-output-annotation-resolve" title="Confirm comment">Confirm</button><button type="button" class="rbe-output-annotation-decline" title="Reject comment">Reject</button></span>'
        : "") +
      "</span>"
    );
  }

  function renderOutputInline(input) {
    var template = document.createElement("template");
    template.innerHTML = sanitizeHTML(input || "");
    Array.prototype.slice.call(template.content.querySelectorAll(".rbe-comment-mark")).forEach(function (mark) {
      var comment = mark.getAttribute("data-rbe-comment") || mark.getAttribute("title") || "";
      var id = mark.getAttribute("data-rbe-comment-id") || uid("comment");
      var status = normalizeCommentStatus(mark.getAttribute("data-rbe-comment-status"));
      var reason = mark.getAttribute("data-rbe-comment-reason") || "";
      var createdAt = mark.getAttribute("data-rbe-comment-date") || "";
      var updatedAt = mark.getAttribute("data-rbe-comment-updated") || "";
      var replyText = mark.getAttribute("data-rbe-comment-reply") || "";
      var replyDate = mark.getAttribute("data-rbe-comment-reply-date") || "";
      var threadRaw = mark.getAttribute("data-rbe-comment-thread") || "";
      var memberName = mark.getAttribute("data-rbe-comment-member-name") || "";
      var memberRole = mark.getAttribute("data-rbe-comment-member-role") || "";
      var updatedMemberName = mark.getAttribute("data-rbe-comment-updated-member-name") || memberName;
      var updatedMemberRole = mark.getAttribute("data-rbe-comment-updated-member-role") || memberRole;
      var wrapper = document.createElement("span");
      wrapper.className = "rbe-output-annotation" + (status === "resolved" ? " is-resolved" : status === "declined" ? " is-declined" : "");
      wrapper.setAttribute("data-comment-id", id);
      wrapper.setAttribute("data-comment", comment);
      wrapper.setAttribute("data-comment-status", status);
      wrapper.setAttribute("data-comment-reason", reason);
      wrapper.setAttribute("data-comment-date", createdAt);
      wrapper.setAttribute("data-comment-updated", updatedAt);
      wrapper.setAttribute("data-comment-reply", replyText);
      wrapper.setAttribute("data-comment-reply-date", replyDate);
      wrapper.setAttribute("data-comment-thread", threadRaw);
      wrapper.setAttribute("data-comment-member-name", memberName);
      wrapper.setAttribute("data-comment-member-role", memberRole);
      wrapper.setAttribute("data-comment-updated-member-name", updatedMemberName);
      wrapper.setAttribute("data-comment-updated-member-role", updatedMemberRole);
      wrapper.innerHTML = '<span class="rbe-output-annotation-text">' + mark.innerHTML + "</span>" + buildAnnotationCardHTML(comment, status, reason, { interactive: true, canReply: status === "declined", inlineEditInRejectMeta: true, createdAt: createdAt, updatedAt: updatedAt, replyText: replyText, replyDate: replyDate, threadRaw: threadRaw, memberName: memberName, memberRole: memberRole, updatedMemberName: updatedMemberName, updatedMemberRole: updatedMemberRole });
      mark.replaceWith(wrapper);
    });
    return template.innerHTML;
  }

  function pluginHelpers() {
    return {
      el: el,
      uid: uid,
      svgIcon: svgIcon,
      escapeHTML: escapeHTML,
      sanitizeHTML: sanitizeHTML,
      renderOutputInline: renderOutputInline,
      cleanAlignment: cleanAlignment,
      isSafeUrl: isSafeUrl
    };
  }

  function cellText(cell) {
    return typeof cell === "object" && cell ? cell.text || "" : cell || "";
  }

  function cellAlignment(cell) {
    return cleanAlignment(typeof cell === "object" && cell ? cell.alignment : "left");
  }

  function tableCellFromTarget(target) {
    return target && target.closest ? target.closest(".rbe-table td, .rbe-table th") : null;
  }

  function syncSelectValue(select, value) {
    if (!select) return;
    select.value = value;
    var option = Array.prototype.slice.call(select.options || []).find(function (item) {
      return item.value === value;
    });
    var container = select.nextElementSibling && select.nextElementSibling.classList && select.nextElementSibling.classList.contains("select2-container") ? select.nextElementSibling : null;
    if (container && option) {
      var rendered = container.querySelector(".select2-selection__rendered");
      if (rendered) {
        rendered.textContent = option.textContent || option.text || value;
        rendered.setAttribute("title", rendered.textContent);
      }
    }
    if (window.jQuery && window.jQuery(select).data("select2")) window.jQuery(select).val(value).trigger("change.select2");
  }

  function setSelectVisible(select, visible) {
    if (!select) return;
    select.hidden = !visible;
    select.style.display = visible ? "" : "none";
    var container = select.nextElementSibling && select.nextElementSibling.classList && select.nextElementSibling.classList.contains("select2-container") ? select.nextElementSibling : null;
    if (container) container.style.display = visible ? "" : "none";
  }

  function BlockWriteAI(options) {
    this.options = Object.assign(
      {
        holder: null,
        data: null,
        placeholder: "Type / for blocks",
        minHeight: "320px",
        autofocus: false,
        readOnly: false,
        tools: null,
        upload: null,
        uploadOutput: ["base64"],
        mentions: [],
        autosave: null,
        persistence: null,
        documentId: null,
        userName: "Current user",
        select2: false,
        shell: true,
        theme: "light",
        themeToggle: false,
        maxWidth: null,
        saveButton: false,
        exportButton: false,
        exportHtmlButton: false,
        bwaipdf: null,
        bwaidoc: null,
        exportPdfButton: null,
        exportWordButton: null,
        exportFilename: "blockwriteai-document",
        onReady: null,
        onChange: null,
        onSave: null
      },
      GLOBAL_OPTIONS,
      options || {}
    );
    var exportOptions = this.options.export || this.options.Export || null;
    if (exportOptions && typeof exportOptions === "object") {
      var exportButtons = "buttons" in exportOptions ? exportOptions.buttons : "Export Buttons" in exportOptions ? exportOptions["Export Buttons"] : exportOptions.exportButtons;
      var exportHtml = "html" in exportOptions ? exportOptions.html : "Export To HTML" in exportOptions ? exportOptions["Export To HTML"] : exportOptions.exportToHTML;
      if (exportButtons !== undefined) this.options.exportButton = !!exportButtons;
      if (exportHtml !== undefined) this.options.exportHtmlButton = !!exportHtml;
    }

    this.holder = typeof this.options.holder === "string" ? document.querySelector(this.options.holder) : this.options.holder;
    if (!this.holder) throw new Error("BlockWriteAI: holder was not found.");
    this.instanceId = uid("editor");
    this.claimedBlockIds = {};
    this.readOnly = !!this.options.readOnly;
    this.premiumOptions = resolvePremiumOptions(this.options);
    this.licenseState = initialPremiumState(this.premiumOptions);
    this.shellOptions = resolveShellOptions(this.options);
    if (typeof window !== "undefined") {
      window.BlockWriteAIAuthorizationToken = this.premiumOptions.authorizationToken || "";
      window.BlockWriteAILicenseKey = this.premiumOptions.serverMode ? "" : this.premiumOptions.licenseKey || localStorageGet("blockwriteai_license_key") || localStorageGet("bwai_license_key") || window.BlockWriteAILicenseKey || "";
      window.BlockWriteAISessionToken = this.premiumOptions.sessionToken || "";
      window.BlockWriteAISignatureEventUrl = this.premiumOptions.signatureEventEndpoint || PLATFORM_ENDPOINTS.signatureEvent;
      window.BlockWriteAIPremiumEventUrl = this.premiumOptions.eventEndpoint || PLATFORM_ENDPOINTS.premiumEvent;
      window.BlockWriteAILogoUrl = this.shellOptions && this.shellOptions.logo ? this.shellOptions.logo : inlineBlockWriteAILogoDataUrl();
    }
    this.shell = null;
    this.shellStatus = null;
    this.shellProgress = null;
    this.shellProgressText = null;
    this.editorMount = null;
    this.availableTools = this.resolveTools(this.options.tools);
    this.currentMenuBlock = null;
    this.slashBlock = null;
    this.draggingBlock = null;
    this.draggingContainer = null;
    this.pendingDragBlock = null;
    this.silent = false;
    this.savedRange = null;
    this.selectEnhanceTimer = null;
    this.inlineToolbarStickyUntil = 0;
    this.renderingData = false;
    this.documentMeta = extractDocumentMeta(this.options.data);
    this.persistenceOptions = resolvePersistenceOptions(this.options.persistence || this.options.Persistence, this.options);
    this.hasExplicitSave = false;
    this.savedExportData = null;
    this.externalExportButtons = [];
    this.boundDocumentMouseDown = this.handleDocumentMouseDown.bind(this);
    this.themeMode = this.resolveThemeMode(this.options.theme);

    this.root = el("div", "rbe", {
      dataset: { readonly: this.readOnly ? "true" : "false", premiumStatus: this.licenseState.status, theme: this.themeMode }
    });
    this.root.__blockwriteaiInstance = this;
    this.root.style.setProperty("--rbe-min-height", this.options.minHeight);
    this.topToolbar = this.createTopToolbar();
    this.canvas = el("div", "rbe-canvas", { role: "textbox", "aria-label": "Rich text editor" });
    this.inlineToolbar = this.createInlineToolbar();
    this.commentModal = this.createCommentModal();
    this.linkModal = this.createLinkModal();
    this.commentPopover = this.createCommentPopover();
    this.menu = el("div", "rbe-menu", { hidden: true });
    this.root.appendChild(this.topToolbar);
    this.root.appendChild(this.canvas);
    this.root.appendChild(this.inlineToolbar);
    this.root.appendChild(this.commentModal);
    this.root.appendChild(this.linkModal);
    this.root.appendChild(this.menu);
    this.holder.innerHTML = "";
    if (this.shellOptions) {
      this.shell = this.createShell();
      this.editorMount.appendChild(this.root);
      this.holder.appendChild(this.shell);
      this.bindShellEvents();
    } else {
      this.holder.appendChild(this.root);
    }
    this.applyTheme(this.themeMode);
    if (document.body && this.commentPopover) {
      document.body.appendChild(this.commentPopover);
      this.boundCommentPopoverClick = this.handleCommentPopoverClick.bind(this);
      this.commentPopover.addEventListener("click", this.boundCommentPopoverClick);
    }

    this.queueChange = debounce(this.emitChange.bind(this), 250);
    this.boundSelectionChange = this.handleSelectionChange.bind(this);
    this.bindEvents();
    this.setupExternalExportButtons();
    this.setupAutosave();
    if (!this.canvas.children.length) this.render(this.options.data || defaultData(), { silent: true });
    this.scheduleSelectEnhance();

    if (this.options.autofocus) {
      var first = this.canvas.querySelector("[contenteditable='true'], textarea, input");
      setTimeout(function () {
        if (first) placeCaretAtEnd(first);
      }, 0);
    }

    if (typeof this.options.onReady === "function") {
      this.options.onReady(this);
    }
    this.verifyPremiumLicense();
  }

  BlockWriteAI.version = VERSION;
  BlockWriteAI.platformEndpoints = Object.assign({}, PLATFORM_ENDPOINTS);
  BlockWriteAI.resolveCloudEndpoints = resolveCloudEndpoints;
  BlockWriteAI.cloudPremium = cloudPremiumConfig;
  BlockWriteAI.createCloudPremiumConfig = cloudPremiumConfig;
  BlockWriteAI.customerStorage = customerStorageConfig;
  BlockWriteAI.sanitizeHTML = sanitizeHTML;
  BlockWriteAI.toEmbed = toEmbed;
  BlockWriteAI.registerTool = function (type, definition) {
    type = String(type || "").trim();
    if (!type || !definition) return;
    if (BLOCK_TYPES.indexOf(type) < 0) BLOCK_TYPES.push(type);
    PLUGIN_TOOLS[type] = definition;
    TOOL_LABELS[type] = Object.assign(
      {
        label: definition.label || type,
        hint: definition.hint || "Plugin block",
        icon: definition.icon || "puzzle-piece",
        premiumFeature: cleanPremiumFeature(definition.premiumFeature || definition.premium || PREMIUM_TOOL_FEATURES[type] || "")
      },
      TOOL_LABELS[type] || {},
      definition.menu || {}
    );
  };
  BlockWriteAI.getTool = function (type) {
    return PLUGIN_TOOLS[type] || null;
  };
  BlockWriteAI.configure = function (options) {
    Object.assign(GLOBAL_OPTIONS, options || {});
    return BlockWriteAI;
  };
  BlockWriteAI.loadPremiumPlugins = function (options) {
    return loadPremiumPluginBundle(options || {});
  };
  BlockWriteAI.helpers = pluginHelpers();

  BlockWriteAI.prototype.resolveTools = function (tools) {
    if (!tools) return BLOCK_TYPES.slice();
    if (Array.isArray(tools)) {
      return tools.filter(function (type) {
        return BLOCK_TYPES.indexOf(type) >= 0;
      });
    }
    return BLOCK_TYPES.filter(function (type) {
      return tools[type] !== false;
    });
  };

  BlockWriteAI.prototype.setPremiumState = function (state) {
    state = state || {};
    if (!this) return initialPremiumState({});
    this.licenseState = Object.assign({}, this.licenseState || initialPremiumState(this.premiumOptions || {}), state);
    this.licenseState.features = Object.assign(premiumFeatures(false), this.licenseState.features || {});
    if (this.root) {
      this.root.dataset.premiumStatus = this.licenseState.status || "missing";
      this.root.dispatchEvent(
        new CustomEvent("rbe:premium-change", {
          bubbles: true,
          detail: this.getPremiumState()
        })
      );
    }
    this.refreshPremiumUI();
    return this.licenseState;
  };

  BlockWriteAI.prototype.getPremiumState = function () {
    var editor = this || {};
    return cloneJSON(editor.licenseState || initialPremiumState(editor.premiumOptions || {}));
  };

  BlockWriteAI.prototype.getLicenseMemberInfo = function () {
    var state = this && this.licenseState ? this.licenseState : {};
    return normalizeLicenseMember(state.member || {});
  };

  BlockWriteAI.prototype.getPremiumFeatureForTool = function (type) {
    return premiumFeatureForTool(type);
  };

  BlockWriteAI.prototype.loadPremiumPlugins = function (features) {
    var options = this.premiumOptions || resolvePremiumOptions(this.options);
    return loadPremiumPluginBundle({
      licenseKey: options.licenseKey,
      authorizationToken: options.authorizationToken,
      pluginEndpoint: options.pluginEndpoint,
      sessionToken: options.sessionToken,
      features: features || options.pluginFeatures,
      serverMode: options.serverMode,
      mode: options.serverMode ? "server" : ""
    }).then(
      function (result) {
        this.availableTools = this.resolveTools(this.options.tools);
        this.refreshPremiumUI();
        return result;
      }.bind(this)
    );
  };

  BlockWriteAI.prototype.isPremiumFeatureEnabled = function (feature) {
    feature = cleanPremiumFeature(feature);
    if (!feature) return true;
    if (!this) return false;
    if (this.premiumOptions && this.premiumOptions.disabled) return true;
    var state = this.licenseState || {};
    if (state.valid && memberHasFeature(state.member, feature)) return true;
    if (!(state.valid && state.features && state.features[feature])) return false;
    var subscription = state.subscription || {};
    if (!subscription.trial) return true;
    var usage = state.usage || {};
    if (usage._trial_usage_date && usage._trial_usage_date !== todayKey()) return true;
    if (feature === "ai") {
      var aiRemaining = usage.ai_actions_remaining_today;
      if (aiRemaining === undefined) aiRemaining = usage.ai_prompts_remaining_today;
      return aiRemaining === undefined || Number(aiRemaining) > 0;
    }
    var remaining = usage.premium_features_remaining_today || {};
    return remaining[feature] === undefined || Number(remaining[feature]) > 0;
  };

  BlockWriteAI.prototype.canUseTool = function (type) {
    if (!this) return false;
    var feature = this.getPremiumFeatureForTool(type);
    return !feature || this.isPremiumFeatureEnabled(feature);
  };

  BlockWriteAI.prototype.shouldShowToolInMenu = function (type) {
    if (!this) return true;
    var feature = this.getPremiumFeatureForTool(type);
    if (!feature || this.premiumOptions && this.premiumOptions.disabled) return true;
    var state = this.licenseState || {};
    if (!state.valid || !state.features) return true;
    if (memberHasFeature(state.member, feature)) return true;
    return state.features[feature] !== false;
  };

  BlockWriteAI.prototype.premiumMessage = function (feature) {
    feature = cleanPremiumFeature(feature);
    var state = this && this.licenseState ? this.licenseState : {};
    if (state.status === "pending") return premiumFeatureLabel(feature) + " is checking your license. Please try again in a moment.";
    if (state.subscription && state.subscription.status === "expired") return premiumFeatureLabel(feature) + " is locked due to trial is expired.";
    if (state.message && !/license verified/i.test(state.message)) return premiumFeatureLabel(feature) + " is locked. " + state.message;
    return premiumFeatureLabel(feature) + " is locked. Please add an active BlockWriteAI premium license.";
  };

  BlockWriteAI.prototype.premiumStorageKey = function (suffix) {
    var state = this && this.licenseState ? this.licenseState : {};
    var options = this && this.premiumOptions ? this.premiumOptions : {};
    var license = (state.license && state.license.prefix) || (options.licenseKey ? options.licenseKey.slice(0, 18) : "no-license");
    var org = state.organization && state.organization.id ? "org-" + state.organization.id : "org-unknown";
    return "blockwriteai:" + org + ":" + license + ":" + suffix;
  };

  BlockWriteAI.prototype.premiumUpgradeTitle = function (feature, reason) {
    return "Unlock Premium Blocks";
  };

  BlockWriteAI.prototype.premiumUpgradeMessage = function (feature, reason) {
    return "Get Drawing, Mermaid, Signature, Signature Flow, and AI Credits with a premium plan.";
  };

  BlockWriteAI.prototype.createPremiumModalInfo = function () {
    var info = el("div", "rbe-premium-modal-info", {});
    info.innerHTML =
      '<section><h3>Already purchased a license?</h3><ol>' +
      "<li>Create your BlockWriteAI account.</li>" +
      "<li>Verify your email.</li>" +
      "<li>Login to your account.</li>" +
      "<li>Enter or activate your license.</li>" +
      "<li>Premium features will unlock automatically.</li>" +
      "</ol></section>" +
      "<section><h3>Need a license?</h3><p>Compare plans or purchase a subscription from BlockWriteAI.</p></section>";
    return info;
  };

  BlockWriteAI.prototype.showPremiumUpgradeModal = function (feature, reason) {
    if (typeof document === "undefined") return;
    var editor = this || {};
    feature = cleanPremiumFeature(feature || "ai");
    reason = String(reason || "").toLowerCase();
    var existing = document.querySelector(".rbe-premium-modal-backdrop");
    if (existing && existing.parentNode) existing.parentNode.removeChild(existing);

    var titleId = uid("premium-title");
    var backdrop = el("div", "rbe-premium-modal-backdrop", {});
    var card = el("section", "rbe-premium-modal-card", {
      role: "dialog",
      "aria-modal": "true",
      "aria-labelledby": titleId
    });
    var close = el("button", "rbe-premium-modal-close", {
      type: "button",
      title: "Close",
      html: svgIcon("xmark", "Close")
    });
    var iconName = reason === "limit" ? "gauge-high" : reason === "trial" ? "clock" : "lock";
    var titleText = typeof editor.premiumUpgradeTitle === "function" ? editor.premiumUpgradeTitle(feature, reason) : premiumFeatureLabel(feature);
    var messageText = typeof editor.premiumUpgradeMessage === "function" ? editor.premiumUpgradeMessage(feature, reason) : premiumFeatureLabel(feature) + " is locked. Please add an active BlockWriteAI premium license.";
    var title = el("h2", "", { id: titleId, text: titleText });
    var message = el("p", "", { text: messageText });
    var actions = el("div", "rbe-premium-modal-actions", {});
    var primary = el("a", "rbe-premium-primary", {
      href: "https://blockwriteai.in/",
      target: "_blank",
      rel: "noopener noreferrer",
      text: "Upgrade Now"
    });
    var secondary = el("a", "rbe-premium-secondary", {
      href: "https://blockwriteai.in/pricing",
      target: "_blank",
      rel: "noopener noreferrer",
      text: "Compare Plans"
    });

    function dismiss() {
      backdrop.classList.remove("is-visible");
      backdrop.classList.add("is-closing");
      setTimeout(function () {
        if (backdrop.parentNode) backdrop.parentNode.removeChild(backdrop);
      }, 180);
    }

    close.addEventListener("click", dismiss);
    backdrop.addEventListener("mousedown", function (event) {
      if (event.target === backdrop) dismiss();
    });
    document.addEventListener(
      "keydown",
      function onKeydown(event) {
        if (event.key !== "Escape") return;
        document.removeEventListener("keydown", onKeydown);
        dismiss();
      },
      { once: true }
    );

    actions.appendChild(secondary);
    actions.appendChild(primary);
    card.appendChild(close);
    card.appendChild(el("div", "rbe-premium-modal-icon", { html: svgIcon(iconName, "Premium") }));
    card.appendChild(title);
    card.appendChild(message);
    if (typeof editor.createPremiumModalInfo === "function") card.appendChild(editor.createPremiumModalInfo());
    card.appendChild(actions);
    backdrop.appendChild(card);
    document.body.appendChild(backdrop);
    requestAnimationFrame(function () {
      backdrop.classList.add("is-visible");
      primary.focus();
    });
  };

  BlockWriteAI.prototype.maybeShowTrialUpgradeModal = function () {
    if (!this) return;
    var state = this.licenseState || {};
    var subscription = state.subscription || {};
    if (!state.valid || !subscription.trial || subscription.status !== "trialing") return;
    var key = this.premiumStorageKey("trial-nudge:" + todayKey());
    if (localStorageGet(key) === "1") return;
    localStorageSet(key, "1");
    setTimeout(
      function () {
        this.showPremiumUpgradeModal("ai", "trial");
      }.bind(this),
      650
    );
  };

  BlockWriteAI.prototype.showToast = function (message, type) {
    if (!message || typeof document === "undefined") return;
    var stack = document.querySelector(".rbe-toast-stack");
    if (!stack) {
      stack = el("div", "rbe-toast-stack", { role: "status", "aria-live": "polite" });
      document.body.appendChild(stack);
    }
    var toastType = type === "error" ? "error" : type === "success" ? "success" : "warning";
    var icon = toastType === "error" ? "circle-exclamation" : toastType === "success" ? "circle-check" : "lock";
    var toast = el("div", "rbe-toast is-" + toastType, {});
    toast.innerHTML =
      svgIcon(icon, toastType) +
      "<div><strong>" +
      escapeHTML(toastType === "error" ? "BlockWriteAI error" : toastType === "success" ? "BlockWriteAI" : "Premium feature") +
      "</strong><span>" +
      escapeHTML(message) +
      "</span></div>";
    stack.appendChild(toast);
    requestAnimationFrame(function () {
      toast.classList.add("is-show");
    });
    setTimeout(function () {
      toast.classList.remove("is-show");
      toast.classList.add("is-hiding");
      setTimeout(function () {
        if (toast.parentNode) toast.parentNode.removeChild(toast);
        if (stack && !stack.children.length && stack.parentNode) stack.parentNode.removeChild(stack);
      }, 260);
    }, 5000);
  };

  BlockWriteAI.prototype.showPremiumLocked = function (feature) {
    if (!this) return;
    this.showToast(this.premiumMessage(feature), "warning");
    this.showPremiumUpgradeModal(feature, "locked");
  };

  BlockWriteAI.prototype.refreshPremiumUI = function () {
    if (!this) return;
    if (!this.root) return;
    Array.prototype.slice.call(this.root.querySelectorAll(".rbe-menu-item[data-type]")).forEach(
      function (button) {
        var feature = this.getPremiumFeatureForTool(button.dataset.type);
        button.hidden = !this.shouldShowToolInMenu(button.dataset.type);
        var locked = !!feature && !this.isPremiumFeatureEnabled(feature);
        button.classList.toggle("is-premium-locked", locked);
        if (locked) button.setAttribute("data-premium-locked", "true");
        else button.removeAttribute("data-premium-locked");
        if (locked) button.title = this.premiumMessage(feature);
        else if (button.dataset.unlockedTitle) button.title = button.dataset.unlockedTitle;
      }.bind(this)
    );
    Array.prototype.slice.call(this.root.querySelectorAll("[data-premium-feature]")).forEach(
      function (node) {
        var feature = cleanPremiumFeature(node.dataset.premiumFeature);
        var locked = !!feature && !this.isPremiumFeatureEnabled(feature);
        node.classList.toggle("is-premium-locked", locked);
        if (locked) node.setAttribute("data-premium-locked", "true");
        else node.removeAttribute("data-premium-locked");
        if (locked) node.title = this.premiumMessage(feature);
        else if (node.dataset.unlockedTitle) node.title = node.dataset.unlockedTitle;
      }.bind(this)
    );
  };

  BlockWriteAI.prototype.schedulePremiumAuthorizationRefresh = function (expiresAt) {
    if (!this) return;
    if (this.premiumRefreshTimer) {
      clearTimeout(this.premiumRefreshTimer);
      this.premiumRefreshTimer = null;
    }
    var expiresTime = expiresAt ? Date.parse(expiresAt) : 0;
    var delay = Number.isFinite(expiresTime) && expiresTime > Date.now() ? expiresTime - Date.now() - 5 * 60 * 1000 : 50 * 60 * 1000;
    delay = Math.max(60 * 1000, Math.min(delay, 50 * 60 * 1000));
    this.premiumRefreshTimer = setTimeout(
      function () {
        this.premiumRefreshTimer = null;
        this.verifyPremiumLicense();
      }.bind(this),
      delay
    );
  };

  BlockWriteAI.prototype.verifyPremiumLicense = function () {
    if (!this) return Promise.resolve(initialPremiumState({}));
    var options = this.premiumOptions || resolvePremiumOptions(this.options);
    if (options.disabled) {
      this.setPremiumState({ status: "disabled", valid: true, message: "Premium checks are disabled.", features: premiumFeatures(true) });
      return Promise.resolve(this.getPremiumState());
    }
    if ((!options.serverMode && !options.authorizationToken && !options.licenseKey) || !options.verifyEndpoint || typeof fetch !== "function") {
      this.setPremiumState(initialPremiumState(options));
      return Promise.resolve(this.getPremiumState());
    }
    this.setPremiumState({ status: "pending", valid: false, message: "License verification is pending.", features: premiumFeatures(false) });
    return fetch(options.verifyEndpoint, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json"
      },
      body: JSON.stringify({
        authorization_token: options.serverMode ? undefined : options.authorizationToken || undefined,
        license_key: options.serverMode || options.authorizationToken ? undefined : options.licenseKey,
        session_token: options.sessionToken,
        domain: typeof window !== "undefined" && window.location ? window.location.origin : ""
      })
    })
      .then(function (response) {
        return response.json().catch(function () {
          return {};
        }).then(function (body) {
          if (!response.ok || body.ok === false) {
            throw new Error(body.message || body.error || "License verification failed.");
          }
          return body;
        });
      })
      .then(
        function (body) {
          if (body.expires_at) this.schedulePremiumAuthorizationRefresh(body.expires_at);
          else if (body.valid) this.schedulePremiumAuthorizationRefresh("");
          if (body.token && !options.serverMode) {
            options.authorizationToken = body.token;
            options.licenseKey = "";
            try {
              ["bwai_license_key", "blockwriteai_license_key"].forEach(function (key) {
                localStorage.removeItem(key);
                sessionStorage.removeItem(key);
              });
            } catch (error) {}
            if (typeof window !== "undefined") {
              window.BlockWriteAIAuthorizationToken = body.token;
              window.BlockWriteAILicenseKey = "";
            }
          }
          this.setPremiumState({
            status: body.valid ? "valid" : "invalid",
            valid: !!body.valid,
            message: body.message || (body.valid ? "License verified." : "License is not active."),
            features: Object.assign(premiumFeatures(false), body.features || {}),
            limits: body.limits || {},
            usage: body.usage || {},
            member: body.member || null,
            organization: body.organization || null,
            subscription: body.subscription || null,
            license: body.license || null
          });
          this.maybeShowTrialUpgradeModal();
          if (body.valid && options.pluginEndpoint && options.autoLoadPlugins) {
            return this.loadPremiumPlugins(options.pluginFeatures)
              .then(
                function () {
                  return this.getPremiumState();
                }.bind(this)
              )
              .catch(
                function (error) {
                  this.showToast(error && error.message ? error.message : "Premium plugins could not be loaded.", "error");
                  return this.getPremiumState();
                }.bind(this)
              );
          }
          return this.getPremiumState();
        }.bind(this)
      )
      .catch(
        function (error) {
          this.setPremiumState({
            status: "invalid",
            valid: false,
            message: error && error.message ? error.message : "License verification failed.",
            features: premiumFeatures(false)
          });
          return this.getPremiumState();
        }.bind(this)
      );
  };

  BlockWriteAI.prototype.consumePremiumUsage = function (feature, metadata) {
    feature = cleanPremiumFeature(feature);
    if (!this || typeof this.isPremiumFeatureEnabled !== "function") {
      var missingContextError = new Error(premiumFeatureLabel(feature) + " is locked. Please add an active BlockWriteAI premium license.");
      missingContextError.status = 402;
      return Promise.reject(missingContextError);
    }
    if (!feature || this.premiumOptions && this.premiumOptions.disabled) return Promise.resolve(this.getPremiumState());
    if (!this.isPremiumFeatureEnabled(feature)) {
      this.showPremiumUpgradeModal(feature, "locked");
      return Promise.reject(new Error(this.premiumMessage(feature)));
    }
    if (!this.premiumOptions || typeof fetch !== "function") {
      return Promise.resolve(this.getPremiumState());
    }
    var endpoint = feature === "ai" ? this.premiumOptions.usageEndpoint : this.premiumOptions.featureUsageEndpoint;
    if (!endpoint) return Promise.resolve(this.getPremiumState());
    var usageLicenseKey = this.premiumOptions.serverMode ? "" : this.premiumOptions.licenseKey || localStorageGet("blockwriteai_license_key") || localStorageGet("bwai_license_key") || "";
    if (!this.premiumOptions.authorizationToken && usageLicenseKey && !this.premiumOptions.licenseKey) {
      this.premiumOptions.licenseKey = usageLicenseKey;
    }
    var usagePayload = Object.assign(
      {},
      metadata || {},
      this.premiumOptions.serverMode
        ? {}
        : {
            authorization_token: this.premiumOptions.authorizationToken || undefined,
            license_key: this.premiumOptions.authorizationToken ? undefined : usageLicenseKey
          }
    );
    usagePayload.feature = feature;
    if (this.premiumOptions.sessionToken) usagePayload.session_token = this.premiumOptions.sessionToken;
    return fetch(endpoint, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json"
      },
      body: JSON.stringify(usagePayload)
    })
      .then(
        function (response) {
          return response.json().catch(function () {
            return {};
          }).then(function (body) {
            if (!response.ok || body.ok === false) {
              var message = body.error || body.message || "AI usage limit check failed.";
              if (body.limit !== undefined || body.used !== undefined || body.remaining !== undefined) {
                var usage = Object.assign({}, this.licenseState.usage || {});
                var limits = Object.assign({}, this.licenseState.limits || {});
                usage._trial_usage_date = todayKey();
                if (body.limit_type === "ai_credits") {
                  if (body.used !== undefined) usage.ai_credits_used_month = body.used;
                  if (body.remaining !== undefined) usage.ai_credits_remaining_month = body.remaining;
                  if (body.limit !== undefined) limits.ai_credits_per_month = body.limit;
                } else if (feature === "ai") {
                  if (body.used !== undefined) {
                    usage.ai_actions_today = body.used;
                    usage.ai_prompts_today = body.used;
                  }
                  if (body.remaining !== undefined) {
                    usage.ai_actions_remaining_today = body.remaining;
                    usage.ai_prompts_remaining_today = body.remaining;
                  }
                  if (body.limit !== undefined) {
                    limits.ai_trial_actions_per_day = body.limit;
                    limits.ai_prompts_per_day = body.limit;
                  }
                } else {
                  usage.premium_features_today = Object.assign({}, usage.premium_features_today || {});
                  usage.premium_features_remaining_today = Object.assign({}, usage.premium_features_remaining_today || {});
                  limits.premium_features_per_day = Object.assign({}, limits.premium_features_per_day || {});
                  if (body.used !== undefined) usage.premium_features_today[feature] = body.used;
                  if (body.remaining !== undefined) usage.premium_features_remaining_today[feature] = body.remaining;
                  if (body.limit !== undefined) limits.premium_features_per_day[feature] = body.limit;
                }
                this.setPremiumState({ usage: usage, limits: limits });
              }
              if (response.status === 429 || /daily (ai|premium)/i.test(message)) {
                this.showPremiumUpgradeModal(feature, "limit");
              } else if (response.status === 402 || /not available|expired|subscription|enough ai credits/i.test(message)) {
                this.showPremiumUpgradeModal(feature, "locked");
              }
              var error = new Error(message);
              error.status = response.status;
              error.details = body;
              throw error;
            }
            return body;
          }.bind(this));
        }.bind(this)
      )
      .then(
        function (body) {
          var usage = Object.assign({}, this.licenseState.usage || {});
          var limits = Object.assign({}, this.licenseState.limits || {});
          usage._trial_usage_date = todayKey();
          if (body.limit_type === "ai_credits") {
            usage.ai_credits_used_month = body.used;
            usage.ai_credits_remaining_month = body.remaining;
            limits.ai_credits_per_month = body.limit;
          } else if (feature === "ai") {
            usage.ai_actions_today = body.used;
            usage.ai_actions_remaining_today = body.remaining;
            usage.ai_prompts_today = body.used;
            usage.ai_prompts_remaining_today = body.remaining;
            limits.ai_trial_actions_per_day = body.limit;
            limits.ai_prompts_per_day = body.limit;
          } else {
            usage.premium_features_today = Object.assign({}, usage.premium_features_today || {});
            usage.premium_features_remaining_today = Object.assign({}, usage.premium_features_remaining_today || {});
            limits.premium_features_per_day = Object.assign({}, limits.premium_features_per_day || {});
            usage.premium_features_today[feature] = body.used;
            usage.premium_features_remaining_today[feature] = body.remaining;
            limits.premium_features_per_day[feature] = body.limit;
          }
          this.setPremiumState({ usage: usage, limits: limits });
          this.lastPremiumUsageResult = body;
          return Object.assign({}, this.getPremiumState(), {
            usage_event_id: body.event_id || null,
            usage_result: body
          });
        }.bind(this)
      );
  };

  BlockWriteAI.prototype.createShell = function () {
    var options = this.shellOptions || {};
    var shell = el("div", "rbe-shell" + (options.className ? " " + options.className : ""));
    if (options.maxWidth) shell.style.setProperty("--rbe-shell-max-width", options.maxWidth);

    var brand = null;
    if (options.showHeader !== false) {
      brand = el("header", "rbe-shell-brand");
      if (options.logo !== false) {
        var logo = el("img", "rbe-shell-logo", {
          src: options.logo || inlineBlockWriteAILogoDataUrl(),
          alt: options.title || "BlockWriteAI",
          decoding: "async"
        });
        brand.appendChild(logo);
      } else {
        brand.appendChild(el("strong", "rbe-shell-title", { text: options.title || "BlockWriteAI" }));
      }
      if (options.subtitle !== false) {
        brand.appendChild(el("p", "rbe-shell-subtitle", { text: options.subtitle || "" }));
      }
    }

    var panel = el("section", "rbe-shell-panel");
    var titleRow = el("div", "rbe-shell-panel-title");
    titleRow.appendChild(el("span", "rbe-shell-heading", { text: options.heading || "Editor" }));
    this.shellProgress = el("span", "rbe-shell-progress", { hidden: true });
    this.shellProgressText = el("span", "rbe-shell-progress-text", { text: "Working with BlockWriteAI..." });
    this.shellProgress.appendChild(this.shellProgressText);
    this.shellProgress.appendChild(el("span", "rbe-shell-progress-track", { html: "<span></span>", "aria-hidden": "true" }));
    titleRow.appendChild(this.shellProgress);
    this.shellStatus = el("span", "rbe-shell-status", { text: options.status || "Ready" });
    titleRow.appendChild(this.shellStatus);
    this.editorMount = el("div", "rbe-shell-editor-wrap");
    panel.appendChild(titleRow);
    panel.appendChild(this.editorMount);
    if (brand) shell.appendChild(brand);
    shell.appendChild(panel);
    return shell;
  };

  BlockWriteAI.prototype.bindShellEvents = function () {
    if (!this.shell || !this.root) return;
    var editor = this;
    this.root.addEventListener("rbe:ai-progress", function (event) {
      var detail = (event && event.detail) || {};
      editor.setShellProgress(!!detail.active, detail.message || "Working with BlockWriteAI...");
    });
    this.root.addEventListener("rbe:save", function () {
      editor.setShellStatus("Saved " + new Date().toLocaleTimeString());
    });
    this.root.addEventListener("rbe:save-error", function (event) {
      var message = (event.detail && event.detail.message) || event.detail || "Save failed";
      editor.setShellStatus(String(message));
    });
  };

  BlockWriteAI.prototype.setShellStatus = function (text) {
    if (this.shellStatus) this.shellStatus.textContent = text || "";
  };

  BlockWriteAI.prototype.setShellProgress = function (active, message) {
    if (!this.shellProgress) return;
    if (this.shellProgressText && message) this.shellProgressText.textContent = message;
    this.shellProgress.hidden = !active;
  };

  BlockWriteAI.prototype.bindEvents = function () {
    var self = this;
    var pointerDragState = null;
    var dragAutoScroll = {
      frame: 0,
      speed: 0,
      target: null
    };

    function clearPendingDragBlock() {
      if (self.pendingDragBlock && self.pendingDragBlock !== self.draggingBlock) {
        self.pendingDragBlock.removeAttribute("draggable");
      }
      self.pendingDragBlock = null;
    }

    function scrollableDragParent(node) {
      var current = node;
      while (current && current !== document.body && current !== document.documentElement) {
        if (current.nodeType === 1) {
          var style = window.getComputedStyle ? window.getComputedStyle(current) : null;
          var overflow = style ? (style.overflowY || "") : "";
          if (/(auto|scroll|overlay)/.test(overflow) && current.scrollHeight > current.clientHeight + 1) {
            return current;
          }
        }
        current = current.parentNode;
      }
      return window;
    }

    function applyDragAutoScroll() {
      if (!dragAutoScroll.speed) {
        dragAutoScroll.frame = 0;
        return;
      }
      var target = dragAutoScroll.target || window;
      if (target === window) {
        window.scrollBy(0, dragAutoScroll.speed);
      } else {
        target.scrollTop += dragAutoScroll.speed;
      }
      dragAutoScroll.frame = window.requestAnimationFrame(applyDragAutoScroll);
    }

    function updateDragAutoScroll(clientX, clientY) {
      var threshold = 86;
      var maxSpeed = 24;
      var viewportHeight = window.innerHeight || document.documentElement.clientHeight || 0;
      var speed = 0;
      if (clientY < threshold) {
        speed = -Math.ceil(((threshold - clientY) / threshold) * maxSpeed);
      } else if (clientY > viewportHeight - threshold) {
        speed = Math.ceil(((clientY - (viewportHeight - threshold)) / threshold) * maxSpeed);
      }
      dragAutoScroll.speed = speed;
      var viewportWidth = window.innerWidth || document.documentElement.clientWidth || 1;
      var hoverX = Math.max(0, Math.min(viewportWidth - 1, clientX));
      var hoverY = Math.max(0, Math.min(viewportHeight - 1, clientY));
      var hoverTarget = document.elementFromPoint ? document.elementFromPoint(hoverX, hoverY) : self.canvas;
      dragAutoScroll.target = scrollableDragParent(hoverTarget);
      if (speed && !dragAutoScroll.frame) {
        dragAutoScroll.frame = window.requestAnimationFrame(applyDragAutoScroll);
      }
      if (!speed && dragAutoScroll.frame) {
        window.cancelAnimationFrame(dragAutoScroll.frame);
        dragAutoScroll.frame = 0;
      }
    }

    function stopDragAutoScroll() {
      dragAutoScroll.speed = 0;
      dragAutoScroll.target = null;
      if (dragAutoScroll.frame) {
        window.cancelAnimationFrame(dragAutoScroll.frame);
        dragAutoScroll.frame = 0;
      }
    }

    function nestedDragContainer(target) {
      return target && target.closest ? target.closest(".rbe-toggle-nested-canvas, .rbe-letterhead-nested-canvas, .rbe-signature-row-canvas") : null;
    }

    function dragContainerForBlock(block) {
      if (!block) return null;
      var parent = block.parentElement;
      if (parent && (parent.classList.contains("rbe-toggle-nested-canvas") || parent.classList.contains("rbe-letterhead-nested-canvas") || parent.classList.contains("rbe-signature-row-canvas"))) return parent;
      return self.canvas;
    }

    function dragContainerForEvent(event) {
      var nested = nestedDragContainer(event.target);
      if (nested) return nested;
      return self.canvas && self.canvas.contains(event.target) ? self.canvas : null;
    }

    function clearDragState(changed) {
      stopDragAutoScroll();
      if (self.draggingBlock) {
        self.draggingBlock.classList.remove("rbe-is-dragging");
        self.draggingBlock.removeAttribute("draggable");
      }
      self.draggingBlock = null;
      self.draggingContainer = null;
      pointerDragState = null;
      clearPendingDragBlock();
      if (changed) self.changed();
    }

    document.addEventListener("selectionchange", this.boundSelectionChange);

    this.canvas.addEventListener("input", function (event) {
      self.handleInput(event);
    });

    this.canvas.addEventListener("keydown", function (event) {
      self.handleKeydown(event);
    });

    this.canvas.addEventListener("paste", function (event) {
      self.handlePaste(event);
    });

    this.canvas.addEventListener(
      "dragover",
      function (event) {
        if (self.readOnly) return;
        if (event.dataTransfer && (event.dataTransfer.files && event.dataTransfer.files.length || Array.prototype.indexOf.call(event.dataTransfer.types || [], "Files") >= 0)) {
          event.preventDefault();
          event.dataTransfer.dropEffect = "copy";
        }
      },
      true
    );

    this.canvas.addEventListener("drop", function (event) {
      self.handleDrop(event);
    });

    this.canvas.addEventListener("dragover", function (event) {
      if (self.readOnly) return;
      var dragging = self.draggingBlock;
      if (!dragging) return;
      var container = dragContainerForEvent(event);
      if (!container || container !== self.draggingContainer) return;
      event.preventDefault();
      updateDragAutoScroll(event.clientX, event.clientY);
      var after = self.getDragAfterElement(event.clientY, container);
      if (after == null) container.appendChild(dragging);
      else container.insertBefore(dragging, after);
    });

    this.root.addEventListener("click", function (event) {
      self.handleRootClick(event);
    });

    this.root.addEventListener("mouseover", function (event) {
      self.handleCommentMouseOver(event);
    });

    this.root.addEventListener("click", function (event) {
      self.handleCommentClick(event);
    });

    this.root.addEventListener("mouseout", function (event) {
      self.handleCommentMouseOut(event);
    });

    this.root.addEventListener("focusin", function (event) {
      self.handleCommentFocusIn(event);
    });

    this.root.addEventListener("focusout", function (event) {
      self.handleCommentFocusOut(event);
    });

    this.root.addEventListener("mousedown", function (event) {
      if (event.target.closest(".rbe-list-action, .rbe-list-add, .rbe-list-remove, .rbe-table-action")) {
        event.preventDefault();
      }
    });

    this.root.addEventListener("pointerdown", function (event) {
      var handle = event.target.closest(".rbe-drag-handle");
      if (!handle || self.readOnly) return;
      if (event.button != null && event.button !== 0) return;
      var block = closestBlock(handle);
      if (!block) return;
      event.preventDefault();
      self.pendingDragBlock = block;
      block.setAttribute("draggable", "true");
      pointerDragState = {
        pointerId: event.pointerId,
        startX: event.clientX,
        startY: event.clientY,
        block: block,
        container: dragContainerForBlock(block),
        started: false
      };
      if (handle.setPointerCapture && event.pointerId != null) {
        try {
          handle.setPointerCapture(event.pointerId);
        } catch (error) {}
      }
    });

    this.root.addEventListener("pointermove", function (event) {
      if (!pointerDragState || (pointerDragState.pointerId != null && event.pointerId !== pointerDragState.pointerId)) return;
      if (!pointerDragState.started) {
        var dx = Math.abs(event.clientX - pointerDragState.startX);
        var dy = Math.abs(event.clientY - pointerDragState.startY);
        if (dx < 4 && dy < 4) return;
        pointerDragState.started = true;
        self.draggingBlock = pointerDragState.block;
        self.draggingContainer = pointerDragState.container;
        self.draggingBlock.classList.add("rbe-is-dragging");
      }
      var container = pointerDragState.container;
      if (!container) return;
      event.preventDefault();
      updateDragAutoScroll(event.clientX, event.clientY);
      var after = self.getDragAfterElement(event.clientY, container);
      if (after == null) container.appendChild(pointerDragState.block);
      else container.insertBefore(pointerDragState.block, after);
    });

    this.root.addEventListener("pointerup", function (event) {
      if (pointerDragState && pointerDragState.pointerId != null && event.pointerId !== pointerDragState.pointerId) return;
      var changed = !!(pointerDragState && pointerDragState.started);
      if (changed) clearDragState(true);
      else {
        stopDragAutoScroll();
        clearPendingDragBlock();
      }
      pointerDragState = null;
    });

    this.root.addEventListener("pointercancel", function (event) {
      if (pointerDragState && pointerDragState.pointerId != null && event.pointerId !== pointerDragState.pointerId) return;
      clearDragState(!!(pointerDragState && pointerDragState.started));
    });

    this.root.addEventListener("change", function (event) {
      self.handleRootChange(event);
    });

    this.root.addEventListener("dragstart", function (event) {
      var handle = event.target.closest(".rbe-drag-handle");
      var block = handle ? closestBlock(handle) : self.pendingDragBlock;
      if (!block || self.readOnly) return;
      if (!handle && (!self.pendingDragBlock || !block.contains(event.target))) return;
      self.draggingBlock = block;
      self.draggingContainer = dragContainerForBlock(block);
      if (event.dataTransfer) {
        event.dataTransfer.effectAllowed = "move";
        event.dataTransfer.setData("text/plain", block.dataset.id || "");
      }
      setTimeout(function () {
        block.classList.add("rbe-is-dragging");
      }, 0);
    });

    this.root.addEventListener("dragend", function () {
      stopDragAutoScroll();
      if (self.draggingBlock) {
        clearDragState(true);
      }
    });

    document.addEventListener("mousedown", this.boundDocumentMouseDown);
    window.addEventListener(
      "resize",
      debounce(
        function () {
          this.updateMediaActionAvailability(this.root);
        }.bind(this),
        160
      )
    );
  };

  BlockWriteAI.prototype.createTopToolbar = function () {
    var toolbar = el("div", "rbe-editor-toolbar", { role: "toolbar", "aria-label": "Editor actions" });
    if (this.options.themeToggle === true) {
      toolbar.appendChild(this.createThemeToggleButton());
    }
    toolbar.appendChild(
        el("button", "rbe-toolbar-btn rbe-clear-editor-btn", {
          type: "button",
          html: svgIcon("trash-can", "Clear editor"),
          title: "Clear editor",
          "aria-label": "Clear editor",
          dataset: { rbeClear: "true" }
        })
    );
    if (this.options.saveButton !== false) {
      toolbar.appendChild(
        el("button", "rbe-toolbar-btn rbe-save-btn", {
          type: "button",
          html: svgIcon("floppy-disk", "Save"),
          title: "Save",
          "aria-label": "Save",
          dataset: { rbeSave: "true" }
        })
      );
    }
    var showExportMenu = this.options.exportButton !== false;
    var showExportHtml = this.options.exportHtmlButton !== false;
    if (showExportMenu || showExportHtml) {
      var toolbarEditor = this;
      if (showExportMenu) {
        var exportWrap = el("div", "rbe-export-menu-wrap");
        var exportToggleButton = el("button", "rbe-toolbar-btn rbe-export-btn", {
          type: "button",
          html: svgIcon("file-export", "Export"),
          title: "Export saved preview",
          "aria-label": "Export saved preview",
          disabled: true,
          dataset: { rbeExportToggle: "true" }
        });
        exportToggleButton.addEventListener("click", function (event) {
          event.preventDefault();
          event.stopPropagation();
          toolbarEditor.toggleExportMenu(exportToggleButton);
        });
        exportWrap.appendChild(exportToggleButton);
        var exportMenu = el("div", "rbe-export-menu", {
          hidden: true,
          role: "menu",
          "aria-label": "Export options"
        });
        [
          ["json", "file-code", "Export to JSON File"],
          ["pdf", "file-export", "Generate PDF"],
          ["word", "file-lines", "Generate Word"]
        ].forEach(function (item) {
          var optionButton = el("button", "rbe-export-option", {
            type: "button",
            html: svgIcon(item[1], item[2]),
            title: item[2],
            "aria-label": item[2],
            role: "menuitem",
            dataset: { rbeExport: item[0] }
          });
          optionButton.addEventListener("click", function (event) {
            event.preventDefault();
            event.stopPropagation();
            toolbarEditor.handleExport(item[0]);
          });
          exportMenu.appendChild(optionButton);
        });
        exportWrap.appendChild(exportMenu);
        toolbar.appendChild(exportWrap);
      }
      if (showExportHtml) {
        toolbar.appendChild(
          el("button", "rbe-toolbar-btn rbe-sheet-dashboard-btn", {
            type: "button",
            html: svgIcon("chart-column", "Show Dashboard"),
            title: "Show Dashboard",
            "aria-label": "Show Dashboard",
            disabled: true,
            hidden: true,
            dataset: { rbeShowDashboard: "true" }
          })
        );
        toolbar.appendChild(
          el("button", "rbe-toolbar-btn rbe-export-html-btn", {
            type: "button",
            html: svgIcon("file-lines", "Export HTML"),
            title: "Export HTML preview",
            "aria-label": "Export HTML preview",
            disabled: true,
            dataset: { rbeExportHtml: "true" }
          })
        );
      }
    }
    return toolbar;
  };

  BlockWriteAI.prototype.resolveThemeMode = function (theme) {
    return String(theme || "").toLowerCase() === "dark" ? "dark" : "light";
  };

  BlockWriteAI.prototype.createThemeToggleButton = function () {
    var mode = this.themeMode || "light";
    return el("button", "rbe-toolbar-btn rbe-theme-toggle-btn", {
      type: "button",
      html: svgIcon(mode === "dark" ? "sun" : "moon", mode === "dark" ? "Switch to light mode" : "Switch to dark mode"),
      title: mode === "dark" ? "Switch to light mode" : "Switch to dark mode",
      "aria-label": mode === "dark" ? "Switch to light mode" : "Switch to dark mode",
      "aria-pressed": mode === "dark" ? "true" : "false",
      dataset: { rbeThemeToggle: "true" }
    });
  };

  BlockWriteAI.prototype.updateThemeToggleButton = function () {
    if (!this.root) return;
    var button = this.root.querySelector("[data-rbe-theme-toggle='true']");
    if (!button) return;
    var dark = this.themeMode === "dark";
    var label = dark ? "Switch to light mode" : "Switch to dark mode";
    button.innerHTML = svgIcon(dark ? "sun" : "moon", label);
    button.title = label;
    button.setAttribute("aria-label", label);
    button.setAttribute("aria-pressed", dark ? "true" : "false");
  };

  BlockWriteAI.prototype.applyTheme = function (theme) {
    this.themeMode = this.resolveThemeMode(theme);
    if (this.root) {
      this.root.dataset.theme = this.themeMode;
      this.root.classList.toggle("rbe-theme-dark", this.themeMode === "dark");
    }
    if (this.shell) {
      this.shell.dataset.theme = this.themeMode;
      this.shell.classList.toggle("rbe-theme-dark", this.themeMode === "dark");
    }
    if (this.commentPopover) {
      this.commentPopover.classList.toggle("blockwriteai-preview-dark", this.themeMode === "dark");
    }
    this.updateThemeToggleButton();
  };

  BlockWriteAI.prototype.toggleTheme = function () {
    this.applyTheme(this.themeMode === "dark" ? "light" : "dark");
  };

  BlockWriteAI.prototype.handleDocumentMouseDown = function (event) {
    if (!this.root) return;
    if (!event.target.closest(".rbe-image-stage, .rbe-attachment-card")) {
      this.closeMediaMenus();
    }
    if (!event.target.closest(".rbe-export-menu-wrap")) this.hideExportMenu();
    if (!event.target.closest(".rbe-comment-mark, .rbe-comment-popover")) {
      this.pinnedCommentMark = null;
      this.hideCommentPopover();
    }
    if (event.target.closest(".rbe-menu, .rbe-block-add, .rbe-inline-toolbar, .rbe-export-menu-wrap, .select2-container, .select2-dropdown, .rbe-comment-modal, .rbe-link-modal, .rbe-comment-popover")) return;
    this.hideMenus();
  };

  BlockWriteAI.prototype.closeMediaMenus = function (except) {
    if (!this.root) return;
    Array.prototype.slice.call(this.root.querySelectorAll(".is-media-menu-open")).forEach(function (item) {
      if (except && item === except) return;
      item.classList.remove("is-media-menu-open");
    });
  };

  BlockWriteAI.prototype.toggleMediaMenu = function (button) {
    if (!button) return;
    var item = button.closest(".rbe-image-stage, .rbe-attachment-card");
    if (!item) return;
    var isOpen = item.classList.contains("is-media-menu-open");
    this.closeMediaMenus(item);
    item.classList.toggle("is-media-menu-open", !isOpen);
    this.updateMediaActionAvailability(item.closest(".rbe-image-gallery, .rbe-attachment-grid") || item.parentElement || this.root);
  };

  BlockWriteAI.prototype.createCommentModal = function () {
    var modal = el("div", "modal rbe-comment-modal", {
      hidden: true,
      role: "dialog",
      "aria-modal": "true",
      "aria-label": "Add comment"
    });
    modal.innerHTML =
      '<div class="modal-dialog rbe-comment-dialog"><div class="modal-content rbe-comment-content"><div class="modal-header rbe-comment-header"><strong>Add comment</strong><button type="button" class="rbe-comment-close" aria-label="Close">' +
      svgIcon("xmark", "Close") +
      '</button></div><div class="modal-body rbe-comment-body"><textarea class="rbe-comment-input" placeholder="Write comment..."></textarea></div><div class="modal-footer rbe-comment-footer"><button type="button" class="rbe-small-btn rbe-comment-cancel">Cancel</button><button type="button" class="rbe-small-btn rbe-comment-save">Save comment</button></div></div></div>';
    return modal;
  };

  BlockWriteAI.prototype.createCommentPopover = function () {
    return el("div", "rbe-comment-popover", { hidden: true });
  };

  BlockWriteAI.prototype.createLinkModal = function () {
    var modal = el("div", "modal rbe-comment-modal rbe-link-modal", {
      hidden: true,
      role: "dialog",
      "aria-modal": "true",
      "aria-label": "Add link"
    });
    modal.innerHTML =
      '<div class="modal-dialog rbe-comment-dialog"><div class="modal-content rbe-comment-content"><div class="modal-header rbe-comment-header"><strong>Add link</strong><button type="button" class="rbe-comment-close rbe-link-close" aria-label="Close">' +
      svgIcon("xmark", "Close") +
      '</button></div><div class="modal-body rbe-comment-body"><label class="rbe-link-modal-label">URL</label><input class="rbe-link-input" type="url" placeholder="https://example.com"></div><div class="modal-footer rbe-comment-footer"><button type="button" class="rbe-small-btn rbe-link-cancel">Cancel</button><button type="button" class="rbe-small-btn rbe-link-save">Save link</button></div></div></div>';
    return modal;
  };

  BlockWriteAI.prototype.createInlineToolbar = function () {
    var toolbar = el("div", "rbe-inline-toolbar", { hidden: true, role: "toolbar" });
    var family = el("select", "rbe-inline-select rbe-font-family-select", {
      title: "Font family",
      dataset: { command: "fontFamily" }
    });
    [
      ["Arial, sans-serif", "Arial"],
      ["Inter, sans-serif", "Inter"],
      ["Georgia, serif", "Georgia"],
      ['"Times New Roman", serif', "Times"],
      ['"Courier New", monospace', "Courier"],
      ["Verdana, sans-serif", "Verdana"]
    ].forEach(function (item) {
      family.appendChild(el("option", "", { value: item[0], text: item[1] }));
    });
    family.value = "Arial, sans-serif";
    family.style.width = "112px";
    var size = el("select", "rbe-inline-select rbe-font-size-select", {
      title: "Font size",
      dataset: { command: "fontSize" }
    });
    ["10", "12", "14", "16", "18", "20", "24", "28", "32", "36", "48"].forEach(function (item) {
      size.appendChild(el("option", "", { value: item, text: item }));
    });
    size.value = "16";
    size.style.width = "66px";
    toolbar.appendChild(family);
    toolbar.appendChild(size);
    INLINE_BUTTONS.forEach(function (button) {
      toolbar.appendChild(
        el("button", "rbe-inline-btn", {
          type: "button",
          title: button.title,
          html: button.icon ? svgIcon(button.icon, button.title) : escapeHTML(button.label),
          dataset: { command: button.command, value: button.value || "" }
        })
      );
    });

    var color = el("input", "rbe-color-input", {
      type: "color",
      title: "Text color",
      value: "#1d4ed8",
      dataset: { command: "foreColor" }
    });
    var highlight = el("input", "rbe-color-input", {
      type: "color",
      title: "Highlight",
      value: "#fff2a8",
      dataset: { command: "hiliteColor" }
    });
    var colorWrap = el("label", "rbe-color-control", { title: "Text color" });
    colorWrap.appendChild(el("span", "rbe-color-icon", { text: "A" }));
    colorWrap.appendChild(color);
    colorWrap.appendChild(
      el("button", "rbe-color-reset", {
        type: "button",
        title: "Reset text color",
        html: svgIcon("rotate-left", "Reset text color"),
        dataset: { command: "foreColorReset" }
      })
    );
    var highlightWrap = el("label", "rbe-color-control", { title: "Highlight" });
    highlightWrap.appendChild(el("span", "rbe-color-icon", { html: svgIcon("highlighter", "Highlight") }));
    highlightWrap.appendChild(highlight);
    highlightWrap.appendChild(
      el("button", "rbe-color-reset", {
        type: "button",
        title: "Reset highlight",
        html: svgIcon("rotate-left", "Reset highlight"),
        dataset: { command: "hiliteColorReset" }
      })
    );
    toolbar.appendChild(colorWrap);
    toolbar.appendChild(highlightWrap);

    var self = this;
    toolbar.addEventListener("mousedown", function (event) {
      self.keepInlineToolbarOpen();
      if (!event.target.matches("input, select, option") && !event.target.closest(".select2-container")) event.preventDefault();
    });
    return toolbar;
  };

  BlockWriteAI.prototype.claimBlockId = function (id) {
    var base = String(id || "").trim();
    if (!base || this.claimedBlockIds[base]) base = uid("block");
    while (this.claimedBlockIds[base]) base = uid("block");
    this.claimedBlockIds[base] = true;
    return base;
  };

  BlockWriteAI.prototype.createBlock = function (type, data, id) {
    var block = el("article", "rbe-block", {
      dataset: {
        id: this.claimBlockId(id),
        type: type
      }
    });
    if (type === "attaches") block.__rbeBlockData = cloneJSON(data || {});
    var controls = el("div", "rbe-block-controls");
    var add = el("button", "rbe-control-btn rbe-block-add", {
      type: "button",
      title: "Insert block",
      html: svgIcon("circle-plus", "Insert block")
    });
    var drag = el("button", "rbe-control-btn rbe-drag-handle", {
      type: "button",
      title: "Drag block",
      html: svgIcon("grip-vertical", "Drag block"),
      draggable: true
    });
    controls.appendChild(add);
    controls.appendChild(drag);
    var actions = this.createBlockActions();

    var body = el("div", "rbe-block-body");
    this.renderBlockBody(body, type, data || {});
    block.appendChild(controls);
    block.appendChild(body);
    block.appendChild(actions);
    if (this.readOnly) this.makeBlockReadonly(block);
    return block;
  };

  BlockWriteAI.prototype.createBlockActions = function () {
    var actions = el("div", "rbe-block-actions", { "aria-label": "Block actions" });
    [
      ["move-up", "arrow-up", "Move up"],
      ["move-down", "arrow-down", "Move down"],
      ["duplicate", "copy", "Duplicate"],
      ["delete", "trash-can", "Delete block"]
    ].forEach(function (item) {
      actions.appendChild(
        el("button", "rbe-block-action", {
          type: "button",
          title: item[2],
          html: svgIcon(item[1], item[2]),
          dataset: { action: item[0] },
          "aria-label": item[2]
        })
      );
    });
    return actions;
  };

  BlockWriteAI.prototype.renderMissingPluginBlock = function (body, type, data) {
    var editor = this;
    var meta = TOOL_LABELS[type] || { label: type || "Plugin block", icon: "puzzle-piece" };
    body.innerHTML = "";
    var card = el("div", "rbe-plugin-loader", {
      html:
        '<div class="rbe-plugin-loader__title">' +
        svgIcon(meta.icon || "puzzle-piece", meta.label) +
        "<strong>" +
        escapeHTML(meta.label || "Plugin block") +
        "</strong></div>" +
        '<p class="rbe-plugin-loader__message">Loading block plugin...</p>'
    });
    body.appendChild(card);
    ensureBuiltinPluginTool(type, function (ready) {
      if (ready) {
        editor.renderBlockBody(body, type, data || editor.defaultBlockData(type));
        return;
      }
      var message = card.querySelector(".rbe-plugin-loader__message");
      if (message) message.textContent = "This block plugin could not be loaded. Please include the matching plugin file in production.";
    });
  };

  BlockWriteAI.prototype.renderBlockBody = function (body, type, data) {
    body.innerHTML = "";
    body.__rbeBlockData = type === "attaches" ? cloneJSON(data || {}) : null;
    if (type === "paragraph") this.renderParagraph(body, data);
    else if (type === "heading") this.renderHeading(body, data);
    else if (type === "quote") this.renderQuote(body, data);
    else if (type === "list") this.renderList(body, data);
    else if (type === "checklist") this.renderChecklist(body, data);
    else if (type === "code") this.renderCode(body, data);
    else if (type === "image") this.renderImage(body, data);
    else if (type === "table") this.renderTable(body, data);
    else if (type === "embed") this.renderEmbed(body, data);
    else if (type === "callout") this.renderCallout(body, data);
    else if (type === "delimiter") this.renderDelimiter(body, data);
    else if (type === "button") this.renderButton(body, data);
    else if (type === "toggle") this.renderToggle(body, data);
    else if (type === "signatureRow") this.renderSignatureRow(body, data);
    else if (type === "linkPreview") this.renderLinkPreview(body, data);
    else if (type === "raw") this.renderRaw(body, data);
    else if (type === "attaches") this.renderAttachment(body, data);
    else if (PLUGIN_TOOLS[type] && typeof PLUGIN_TOOLS[type].render === "function") PLUGIN_TOOLS[type].render.call(this, body, data || {}, pluginHelpers());
    else if (BUILTIN_PLUGIN_SCRIPTS[type]) this.renderMissingPluginBlock(body, type, data || {});
    else this.renderParagraph(body, data);
  };

  BlockWriteAI.prototype.editable = function (className, html, placeholder) {
    return el("div", "rbe-editable " + (className || ""), {
      contentEditable: !this.readOnly,
      html: sanitizeHTML(html || ""),
      dataset: { placeholder: placeholder || this.options.placeholder }
    });
  };

  BlockWriteAI.prototype.renderParagraph = function (body, data) {
    var editable = this.editable("rbe-paragraph", data.text, this.options.placeholder);
    editable.style.textAlign = cleanAlignment(data.alignment);
    body.appendChild(editable);
  };

  BlockWriteAI.prototype.renderHeading = function (body, data) {
    var level = Math.min(4, Math.max(1, Number(data.level || 2)));
    var wrap = el("div", "rbe-heading-wrap");
    var select = el("select", "rbe-heading-level", {
      title: "Heading level",
      dataset: { role: "heading-level" }
    });
    [1, 2, 3, 4].forEach(function (item) {
      select.appendChild(el("option", "", { value: String(item), text: "H" + item, selected: item === level }));
    });
    var heading = el("div", "rbe-editable rbe-heading rbe-h" + level, {
      contentEditable: !this.readOnly,
      html: sanitizeHTML(data.text || ""),
      dataset: { placeholder: "Heading", level: level }
    });
    heading.style.textAlign = cleanAlignment(data.alignment);
    wrap.appendChild(select);
    wrap.appendChild(heading);
    body.appendChild(wrap);
  };

  BlockWriteAI.prototype.renderQuote = function (body, data) {
    var quote = el("div", "rbe-quote");
    var quoteText = this.editable("rbe-quote-text", data.text, "Quote");
    quoteText.style.textAlign = cleanAlignment(data.alignment);
    quote.appendChild(quoteText);
    quote.appendChild(
      el("input", "rbe-quote-caption", {
        type: "text",
        value: data.caption || "",
        placeholder: "Caption",
        disabled: this.readOnly
      })
    );
    body.appendChild(quote);
  };

  BlockWriteAI.prototype.renderList = function (body, data) {
    var style = data.style === "ordered" ? "ordered" : "unordered";
    var wrap = el("div", "rbe-list-wrap", { dataset: { style: style } });
    var tools = el("div", "rbe-list-tools");
    var select = el("select", "rbe-list-style", {
      title: "List style",
      dataset: { role: "list-style" }
    });
    select.appendChild(el("option", "", { value: "unordered", text: "Bulleted", selected: style === "unordered" }));
    select.appendChild(el("option", "", { value: "ordered", text: "Numbered", selected: style === "ordered" }));
    tools.appendChild(select);
    tools.appendChild(
      el("button", "rbe-mini-btn rbe-list-action rbe-list-nest-action", {
        type: "button",
        html: svgIcon("indent", "Indent list item"),
        title: "Indent list item",
        dataset: { action: "indent-list" },
        disabled: this.readOnly
      })
    );
    tools.appendChild(
      el("button", "rbe-mini-btn rbe-list-action rbe-list-nest-action", {
        type: "button",
        html: svgIcon("outdent", "Outdent list item"),
        title: "Outdent list item",
        dataset: { action: "outdent-list" },
        disabled: this.readOnly
      })
    );
    tools.appendChild(
      el("button", "rbe-mini-btn rbe-list-action rbe-list-style-action", {
        type: "button",
        html: svgIcon("list-check", "Switch selected item bullet/number"),
        title: "Switch selected item bullet/number",
        dataset: { action: "toggle-list-style" },
        disabled: this.readOnly
      })
    );
    var list = el("div", "rbe-list", { dataset: { alignment: cleanAlignment(data.alignment) } });
    list.style.textAlign = cleanAlignment(data.alignment);
    var items = Array.isArray(data.items) && data.items.length ? data.items : [""];
    items.forEach(
      function (item) {
        list.appendChild(this.createListItem(item));
      }.bind(this)
    );
    wrap.appendChild(tools);
    wrap.appendChild(list);
    wrap.appendChild(
      el("button", "rbe-icon-btn rbe-row-add rbe-list-add", {
        type: "button",
        html: svgIcon("circle-plus", "Add list item"),
        title: "Add list item",
        "aria-label": "Add list item",
        disabled: this.readOnly
      })
    );
    this.updateListMarkers(wrap);
    body.appendChild(wrap);
  };

  BlockWriteAI.prototype.createListItem = function (item) {
    item = normalizeListItem(item);
    var row = el("div", "rbe-list-item", {
      dataset: { level: item.level, style: item.style || "" }
    });
    row.appendChild(el("span", "rbe-list-marker", { "aria-hidden": "true" }));
    row.appendChild(
      el("span", "rbe-list-text rbe-editable", {
        contentEditable: !this.readOnly,
        html: item.text,
        dataset: { placeholder: "List item" }
      })
    );
    row.querySelector(".rbe-list-text").style.textAlign = cleanAlignment(item.alignment);
    row.appendChild(
      el("button", "rbe-icon-btn rbe-row-remove rbe-list-remove", {
        type: "button",
        html: svgIcon("trash-can", "Remove list item"),
        title: "Remove list item",
        "aria-label": "Remove list item",
        disabled: this.readOnly
      })
    );
    return row;
  };

  BlockWriteAI.prototype.renderChecklist = function (body, data) {
    var wrap = el("div", "rbe-checklist-wrap");
    var tools = el("div", "rbe-checklist-tools");
    tools.appendChild(
      el("button", "rbe-small-btn rbe-list-nest-action rbe-checklist-action", {
        type: "button",
        html: svgIcon("indent", "Indent checklist item"),
        title: "Indent checklist item",
        dataset: { action: "indent-checklist" },
        disabled: this.readOnly
      })
    );
    tools.appendChild(
      el("button", "rbe-small-btn rbe-list-nest-action rbe-checklist-action", {
        type: "button",
        html: svgIcon("outdent", "Outdent checklist item"),
        title: "Outdent checklist item",
        dataset: { action: "outdent-checklist" },
        disabled: this.readOnly
      })
    );
    var checklist = el("div", "rbe-checklist");
    var items = Array.isArray(data.items) && data.items.length ? data.items : [{ text: "", checked: false }];
    var self = this;
    items.forEach(function (item) {
      checklist.appendChild(self.createChecklistItem(item));
    });
    checklist.appendChild(
      el("button", "rbe-icon-btn rbe-row-add rbe-checklist-add", {
        type: "button",
        html: svgIcon("circle-plus", "Add checklist item"),
        title: "Add checklist item",
        "aria-label": "Add checklist item",
        disabled: this.readOnly
      })
    );
    wrap.appendChild(tools);
    wrap.appendChild(checklist);
    body.appendChild(wrap);
  };

  BlockWriteAI.prototype.createChecklistItem = function (item) {
    item = normalizeChecklistItem(item);
    var row = el("div", "rbe-check-item", {
      dataset: {
        level: item.level,
        checked: item.checked ? "true" : "false"
      }
    });
    row.appendChild(
      el("button", "rbe-check-toggle", {
        type: "button",
        disabled: this.readOnly,
        dataset: { checked: item.checked ? "true" : "false" },
        "aria-pressed": item.checked ? "true" : "false",
        "aria-label": item.checked ? "Mark item incomplete" : "Mark item complete"
      })
    );
    row.appendChild(
      el("span", "rbe-check-text rbe-editable", {
        contentEditable: !this.readOnly,
        html: item.text,
        dataset: { placeholder: "Task" }
      })
    );
    row.appendChild(
      el("button", "rbe-icon-btn rbe-row-remove rbe-check-remove", {
        type: "button",
        html: svgIcon("trash-can", "Remove item"),
        title: "Remove item",
        "aria-label": "Remove item",
        disabled: this.readOnly
      })
    );
    this.updateChecklistItemIcon(row);
    return row;
  };

  BlockWriteAI.prototype.renderCode = function (body, data) {
    var language = normalizeCodeLanguage(data.language);
    var headerColor = codeHeaderColor(data);
    var backgroundColor = codeBackgroundColor(data);
    if (this.themeMode === "dark" && backgroundColor === "#d7dee8") backgroundColor = "#30363d";
    var wrap = el("div", "rbe-code-wrap");
    applyCodeColorVars(wrap, { headerColor: headerColor, backgroundColor: backgroundColor });
    var header = el("div", "rbe-code-header");
    var languageSelect = el("select", "rbe-code-language", {
      title: "Code language",
      disabled: this.readOnly
    });
    languageSelect.style.width = "176px";
    CODE_LANGUAGES.forEach(function (item) {
      languageSelect.appendChild(el("option", "", { value: item[0], text: item[1], selected: item[0] === language }));
    });
    header.appendChild(languageSelect);
    var headerColorLabel = el("label", "rbe-code-color-label", { title: "Header color" });
    headerColorLabel.appendChild(el("span", "", { text: "Header" }));
    headerColorLabel.appendChild(el("input", "rbe-code-header-color", { type: "color", value: headerColor, disabled: this.readOnly }));
    header.appendChild(headerColorLabel);
    var backgroundColorLabel = el("label", "rbe-code-color-label", { title: "Editor background" });
    backgroundColorLabel.appendChild(el("span", "", { text: "Background" }));
    backgroundColorLabel.appendChild(el("input", "rbe-code-bg-color", { type: "color", value: backgroundColor, disabled: this.readOnly }));
    header.appendChild(backgroundColorLabel);
    header.appendChild(
      el("button", "rbe-small-btn rbe-code-format", {
        type: "button",
        title: "Format code",
        "aria-label": "Format code",
        html: svgIcon("wand-magic-sparkles", "Format code"),
        disabled: this.readOnly
      })
    );
    header.appendChild(
      el("button", "rbe-small-btn rbe-code-copy", {
        type: "button",
        title: "Copy code",
        "aria-label": "Copy code",
        html: svgIcon("copy", "Copy code"),
        disabled: false
      })
    );
    wrap.appendChild(header);
    var editorShell = el("div", "rbe-code-editor");
    var input = el("textarea", "rbe-code-input", {
      value: data.code || "",
      placeholder: "Write code...",
      spellcheck: false,
      disabled: this.readOnly
    });
    editorShell.appendChild(input);
    wrap.appendChild(editorShell);
    body.appendChild(wrap);
  };

  BlockWriteAI.prototype.bindUploadDropTarget = function (target, kind) {
    if (!target || this.readOnly) return;
    var editor = this;
    var dropTargets = [target].concat(Array.prototype.slice.call(target.querySelectorAll("input[type='file']")));
    function filesFromEvent(event) {
      return Array.prototype.slice.call(event.dataTransfer && event.dataTransfer.files ? event.dataTransfer.files : []);
    }
    function hasFileTransfer(event) {
      return !!(event.dataTransfer && (event.dataTransfer.files && event.dataTransfer.files.length || Array.prototype.indexOf.call(event.dataTransfer.types || [], "Files") >= 0));
    }
    dropTargets.forEach(function (dropTarget) {
      dropTarget.addEventListener("dragenter", function (event) {
        if (!hasFileTransfer(event)) return;
        event.preventDefault();
        event.stopPropagation();
        target.classList.add("is-dragging");
      }, true);
      dropTarget.addEventListener("dragover", function (event) {
        if (!hasFileTransfer(event)) return;
        event.preventDefault();
        event.stopPropagation();
        if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
        target.classList.add("is-dragging");
      }, true);
      dropTarget.addEventListener("dragleave", function (event) {
        event.stopPropagation();
        target.classList.remove("is-dragging");
      }, true);
      dropTarget.addEventListener("drop", function (event) {
        var files = filesFromEvent(event);
        if (!files.length) return;
        event.preventDefault();
        event.stopPropagation();
        target.classList.remove("is-dragging");
        var block = closestBlock(target) || closestBlock(event.target);
        if (!block) return;
        if (kind === "image") editor.applyImageFiles(block, files);
        else editor.applyAttachmentFiles(block, files);
      }, true);
    });
  };

  BlockWriteAI.prototype.renderImage = function (body, data) {
    var wrap = el("figure", "rbe-image", {
      dataset: {
        caption: data.caption || "",
        alt: data.alt || data.caption || ""
      }
    });
    var images = normalizeImageItems(data);
    var canEdit = !this.readOnly;
    if (images.length) {
      var editor = this;
      var gallery = el("div", "rbe-image-gallery", {
        title: this.readOnly ? "" : "Drop image files here to add more images"
      });
      images.forEach(function (item) {
        var itemAlignment = cleanAlignment(item.alignment);
        var stage = el("div", "rbe-image-stage" + (canEdit ? " is-editable" : ""), {
          dataset: {
            url: item.url,
            alt: item.alt || "",
            caption: item.caption || "",
            alignment: itemAlignment,
            displayWidth: item.displayWidth > 0 ? Math.round(item.displayWidth) : "",
            minWidth: item.minWidth > 0 ? Math.round(item.minWidth) : "",
            naturalWidth: item.naturalWidth > 0 ? Math.round(item.naturalWidth) : "",
            naturalHeight: item.naturalHeight > 0 ? Math.round(item.naturalHeight) : "",
            rotation: item.rotation || "",
            crop: item.crop || "free",
            cropRatio: item.cropRatio || "",
            cropX: imageCropPercent(item.cropX),
            cropY: imageCropPercent(item.cropY),
            cropZoom: imageCropZoom(item.cropZoom)
          }
        });
        stage.__rbeMediaItem = cloneJSON(item);
        var imagePreview = el("img", "rbe-image-preview", {
          src: item.url,
          alt: item.alt || item.caption || ""
        });
        function refreshImageNaturalSize() {
          if (!imagePreview.naturalWidth) return;
          stage.dataset.naturalWidth = String(imagePreview.naturalWidth);
          stage.dataset.naturalHeight = String(imagePreview.naturalHeight || "");
          var displayWidth = Number(stage.dataset.displayWidth || 0);
          if (displayWidth > imagePreview.naturalWidth) stage.dataset.displayWidth = String(imagePreview.naturalWidth);
          editor.applyImageStageState(stage);
        }
        imagePreview.addEventListener("load", refreshImageNaturalSize);
        stage.appendChild(imagePreview);
        if (canEdit) {
          var imageTools = el("div", "rbe-media-gear-menu");
          imageTools.appendChild(
            el("button", "rbe-media-gear", {
              type: "button",
              title: "Image settings",
              html: svgIcon("gear", "Image settings"),
              "aria-label": "Image settings"
            })
          );
          imageTools.appendChild(
            el("button", "rbe-media-action rbe-media-action-top rbe-image-crop-toggle", {
              type: "button",
              title: "Crop image",
              "aria-label": "Crop image",
              "aria-pressed": item.crop === "custom" ? "true" : "false",
              html: svgIcon("crop-simple", "Crop image")
            })
          );
          imageTools.appendChild(
            el("button", "rbe-media-action rbe-media-action-bottom rbe-media-delete rbe-image-delete", {
              type: "button",
              title: "Remove image",
              "aria-label": "Remove image",
              html: svgIcon("trash-can", "Remove image")
            })
          );
          stage.appendChild(imageTools);
          ["w", "e", "nw", "ne", "sw", "se"].forEach(function (handle) {
            stage.appendChild(el("span", "rbe-image-resize-handle rbe-image-resize-" + handle, { dataset: { resizeHandle: handle }, title: "Drag to resize image" }));
          });
          ["e", "s", "se"].forEach(function (handle) {
            stage.appendChild(el("span", "rbe-image-crop-handle rbe-image-crop-" + handle, { dataset: { cropHandle: handle }, title: "Drag to crop image" }));
          });
        }
        editor.applyImageStageState(stage);
        if (imagePreview.complete) setTimeout(refreshImageNaturalSize, 0);
        gallery.appendChild(stage);
        if (canEdit) editor.bindImageStageMouseControls(stage);
      });
      if (!this.readOnly) {
        gallery.appendChild(el("span", "rbe-image-replace-hint", { text: "Drop more image files to add" }));
      }
      this.bindUploadDropTarget(gallery, "image");
      wrap.appendChild(gallery);
    } else {
      var imageDropzone = el("label", "rbe-upload-dropzone rbe-image-dropzone");
      imageDropzone.appendChild(
        el("input", "rbe-image-file rbe-upload-input", {
          type: "file",
          accept: "image/*",
          multiple: true,
          disabled: this.readOnly
        })
      );
      imageDropzone.appendChild(el("span", "rbe-upload-icon", { html: svgIcon("cloud-arrow-up", "Upload image") }));
      imageDropzone.appendChild(el("strong", "", { text: "Drag and drop or click here" }));
      imageDropzone.appendChild(el("span", "rbe-upload-main", { text: "to upload image" }));
      imageDropzone.appendChild(el("small", "", { text: "Upload any image from desktop" }));
      this.bindUploadDropTarget(imageDropzone, "image");
      wrap.appendChild(imageDropzone);
    }
    body.appendChild(wrap);
    if (canEdit && images.length) {
      setTimeout(
        function () {
          this.updateMediaActionAvailability(wrap);
        }.bind(this),
        0
      );
    }
  };

  BlockWriteAI.prototype.renderTable = function (body, data) {
    var align = data.align === "center" || data.align === "right" ? data.align : "left";
    var width = Math.min(100, Math.max(40, Number(data.width || 100)));
    var borderStyle = cleanTableBorderStyle(data.borderStyle);
    var borderWidth = cleanTableBorderWidth(data.borderWidth);
    var wrap = el("div", "rbe-table-wrap", {
      dataset: { align: align }
    });
    var toolbar = el("div", "rbe-table-toolbar");
    [
      ["add-row", "table-list", "Add row"],
      ["add-col", "table-columns", "Add column"],
      ["select-row", "grip-lines", "Select row"],
      ["select-col", "grip-lines-vertical", "Select column"],
      ["clear-selection", "xmark", "Clear table selection"],
      ["remove-row", "trash-can|arrow-right|table-list", "Remove row"],
      ["remove-col", "trash-can|arrow-right|table-columns", "Remove column"],
      ["cell-align-left", "align-left", "Align selected cells left"],
      ["cell-align-center", "align-center", "Align selected cells center"],
      ["cell-align-right", "align-right", "Align selected cells right"]
    ].forEach(function (item) {
      var iconHtml = item[1].indexOf("|") >= 0
        ? '<span class="rbe-combo-icon" aria-hidden="true">' + item[1].split("|").map(function (icon) {
            return svgIcon(icon, item[2]);
          }).join("") + '</span><span class="rbe-sr-only">' + escapeHTML(item[2]) + "</span>"
        : svgIcon(item[1], item[2]);
      toolbar.appendChild(
        el("button", "rbe-small-btn rbe-table-action", {
          type: "button",
          title: item[2],
          html: iconHtml,
          "aria-label": item[2],
          dataset: { action: item[0] }
        })
      );
    });
    wrap.appendChild(toolbar);

    var layout = el("div", "rbe-table-layout");
    var widthLabel = el("label", "rbe-table-width-label");
    widthLabel.appendChild(el("span", "", { text: "Width" }));
    widthLabel.appendChild(
      el("input", "rbe-table-width", {
        type: "range",
        min: "40",
        max: "100",
        step: "5",
        value: String(width),
        disabled: this.readOnly
      })
    );
    widthLabel.appendChild(el("span", "rbe-table-width-value", { text: width + "%" }));
    layout.appendChild(widthLabel);
    var alignLabel = el("label", "rbe-table-align-label");
    alignLabel.appendChild(el("span", "", { text: "Align" }));
    var alignSelect = el("select", "rbe-table-align", {
      title: "Table alignment",
      disabled: this.readOnly
    });
    [
      ["left", "Left"],
      ["center", "Center"],
      ["right", "Right"]
    ].forEach(function (item) {
      alignSelect.appendChild(el("option", "", { value: item[0], text: item[1], selected: item[0] === align }));
    });
    alignLabel.appendChild(alignSelect);
    layout.appendChild(alignLabel);

    var borderStyleLabel = el("label", "rbe-table-border-style-label");
    borderStyleLabel.appendChild(el("span", "", { text: "Border" }));
    var borderSelect = el("select", "rbe-table-border-style", {
      title: "Table border style",
      disabled: this.readOnly
    });
    [
      ["solid", "Solid"],
      ["dashed", "Dashed"],
      ["dotted", "Dotted"],
      ["double", "Double"]
    ].forEach(function (item) {
      borderSelect.appendChild(el("option", "", { value: item[0], text: item[1], selected: item[0] === borderStyle }));
    });
    borderStyleLabel.appendChild(borderSelect);
    layout.appendChild(borderStyleLabel);

    var borderWidthLabel = el("label", "rbe-table-border-width-label");
    borderWidthLabel.appendChild(el("span", "", { text: "Weight" }));
    borderWidthLabel.appendChild(
      el("input", "rbe-table-border-width", {
        type: "range",
        min: "1",
        max: "6",
        step: "1",
        value: String(borderWidth),
        disabled: this.readOnly
      })
    );
    borderWidthLabel.appendChild(el("span", "rbe-table-border-width-value", { text: borderWidth + "px" }));
    layout.appendChild(borderWidthLabel);
    wrap.appendChild(layout);

    var table = el("table", "rbe-table");
    table.style.width = width + "%";
    table.style.setProperty("--rbe-table-border-width", borderWidth + "px");
    table.style.setProperty("--rbe-table-border-style", borderStyle);
    var rows = Array.isArray(data.rows) && data.rows.length ? data.rows : [["", ""], ["", ""]];
    rows.forEach(
      function (row, rowIndex) {
        var tr = el("tr");
        var cols = row.length ? row : [""];
        cols.forEach(
          function (cell, colIndex) {
            var tag = data.withHeadings && rowIndex === 0 ? "th" : "td";
            var tableCell = el(tag, "", {
              contentEditable: !this.readOnly,
              html: sanitizeHTML(cellText(cell)),
              dataset: { row: rowIndex, col: colIndex }
            });
            tableCell.style.textAlign = cellAlignment(cell);
            tr.appendChild(tableCell);
          }.bind(this)
        );
        table.appendChild(tr);
      }.bind(this)
    );
    var stage = el("div", "rbe-table-stage");
    stage.appendChild(table);
    wrap.appendChild(stage);
    var headingToggle = el("label", "rbe-table-heading-toggle");
    headingToggle.appendChild(
      el("input", "", {
        type: "checkbox",
        checked: !!data.withHeadings,
        dataset: { role: "table-heading" },
        disabled: this.readOnly
      })
    );
    headingToggle.appendChild(document.createTextNode(" First row is heading"));
    wrap.appendChild(headingToggle);
    body.appendChild(wrap);
    this.bindTableSelection(wrap);
  };

  BlockWriteAI.prototype.renderEmbed = function (body, data) {
    var wrap = el("div", "rbe-embed");
    var tools = el("div", "rbe-media-tools");
    tools.appendChild(
      el("input", "rbe-embed-url", {
        type: "url",
        value: data.source || "",
        placeholder: "Paste YouTube, Vimeo, map, or iframe URL",
        disabled: this.readOnly
      })
    );
    tools.appendChild(
      el("button", "rbe-small-btn rbe-embed-apply", {
        type: "button",
        text: "Embed",
        disabled: this.readOnly
      })
    );
    wrap.appendChild(tools);
    if (data.embed) {
      wrap.appendChild(
        el("iframe", "rbe-embed-frame", {
          src: data.embed,
          title: data.service ? data.service + " embed" : "Embedded content",
          referrerPolicy: "strict-origin-when-cross-origin",
          allow:
            "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
          allowFullscreen: true
        })
      );
    } else {
      wrap.appendChild(el("div", "rbe-dropzone", { text: "Paste a URL and press Embed" }));
    }
    wrap.appendChild(
      el("input", "rbe-embed-caption", {
        type: "text",
        value: data.caption || "",
        placeholder: "Caption",
        disabled: this.readOnly
      })
    );
    body.appendChild(wrap);
  };

  BlockWriteAI.prototype.renderCallout = function (body, data) {
    var wrap = el("div", "rbe-callout", {
      dataset: { tone: data.tone || "info" }
    });
    var select = el("select", "rbe-callout-tone", {
      title: "Callout tone",
      disabled: this.readOnly
    });
    ["info", "success", "warning", "danger"].forEach(function (tone) {
      select.appendChild(el("option", "", { value: tone, text: tone, selected: (data.tone || "info") === tone }));
    });
    wrap.appendChild(select);
    var text = this.editable("rbe-callout-text", data.text, "Callout text");
    text.style.textAlign = cleanAlignment(data.alignment);
    wrap.appendChild(text);
    body.appendChild(wrap);
  };

  BlockWriteAI.prototype.renderDelimiter = function (body, data) {
    var style = data.style === "dashed" || data.style === "dotted" || data.style === "thick" ? data.style : "line";
    var wrap = el("div", "rbe-delimiter-wrap", { dataset: { style: style } });
    wrap.addEventListener("mouseleave", function () {
      wrap.classList.remove("rbe-divider-toolbar-dismissed");
    });
    if (!this.readOnly) {
      var toolbar = el("div", "rbe-divider-toolbar", { role: "toolbar", "aria-label": "Divider style" });
      [
        ["line", "minus", "Line"],
        ["dashed", "grip-lines", "Dashed"],
        ["dotted", "ellipsis", "Dotted"],
        ["thick", "bars-staggered", "Bold line"]
      ].forEach(function (item) {
        toolbar.appendChild(
          el("button", "rbe-divider-style-btn" + (item[0] === style ? " is-active" : ""), {
            type: "button",
            title: item[2],
            html: svgIcon(item[1], item[2]),
            dataset: { style: item[0] },
            "aria-pressed": item[0] === style ? "true" : "false"
          })
        );
      });
      wrap.appendChild(toolbar);
    }
    wrap.appendChild(el("hr", "rbe-delimiter"));
    body.appendChild(wrap);
  };

  BlockWriteAI.prototype.renderButton = function (body, data) {
    var palette = {
      primary: ["#2563eb", "#ffffff"],
      secondary: ["#ffffff", "#172033"],
      success: ["#07845f", "#ffffff"],
      dark: ["#111827", "#ffffff"]
    };
    var legacy = palette[data.style] || palette.primary;
    var background = data.background || legacy[0];
    var color = data.color || legacy[1];
    var alignment = cleanAlignment(data.alignment);
    var wrap = el("div", "rbe-button-block", { dataset: { align: alignment } });
    var controls = el("div", "rbe-button-controls");
    controls.appendChild(
      el("input", "rbe-button-text", {
        type: "text",
        value: data.text || "",
        placeholder: "Button text",
        disabled: this.readOnly
      })
    );
    controls.appendChild(
      el("input", "rbe-button-url", {
        type: "url",
        value: data.url || "",
        placeholder: "https://example.com",
        disabled: this.readOnly
      })
    );
    var backgroundLabel = el("label", "rbe-button-color-label", { title: "Button background" });
    backgroundLabel.appendChild(el("span", "", { text: "Fill" }));
    backgroundLabel.appendChild(el("input", "rbe-button-bg", { type: "color", value: background, disabled: this.readOnly }));
    var colorLabel = el("label", "rbe-button-color-label", { title: "Button text color" });
    colorLabel.appendChild(el("span", "", { text: "Text" }));
    colorLabel.appendChild(el("input", "rbe-button-color", { type: "color", value: color, disabled: this.readOnly }));
    var alignSelect = el("select", "rbe-button-align", { title: "Button alignment", disabled: this.readOnly });
    ["left", "center", "right"].forEach(function (item) {
      alignSelect.appendChild(el("option", "", { value: item, text: item.charAt(0).toUpperCase() + item.slice(1), selected: item === alignment }));
    });
    controls.appendChild(backgroundLabel);
    controls.appendChild(colorLabel);
    controls.appendChild(alignSelect);
    wrap.appendChild(controls);
    var preview = el("div", "rbe-button-preview");
    preview.style.textAlign = alignment;
    preview.appendChild(
      el("a", "rbe-button-link", {
        href: isSafeUrl(data.url || "") ? data.url : "#",
        target: "_blank",
        rel: "noopener noreferrer",
        text: data.text || "Button"
      })
    );
    preview.querySelector(".rbe-button-link").style.backgroundColor = background;
    preview.querySelector(".rbe-button-link").style.color = color;
    wrap.appendChild(preview);
    body.appendChild(wrap);
  };

  BlockWriteAI.prototype.renderToggle = function (body, data) {
    var details = el("details", "rbe-toggle", { open: data.open !== false });
    var summary = el("summary", "rbe-toggle-summary");
    var toggleButton = el("button", "rbe-toggle-arrow", {
      type: "button",
      title: "Toggle section",
      html: svgIcon("chevron-down", "Toggle section"),
      "aria-expanded": data.open !== false ? "true" : "false"
    });
    var titleInput = el("input", "rbe-toggle-title", {
      type: "text",
      value: data.title || "",
      placeholder: "Toggle title",
      disabled: this.readOnly
    });
    toggleButton.addEventListener(
      "click",
      function (event) {
        event.preventDefault();
        event.stopPropagation();
        details.open = !details.open;
        toggleButton.setAttribute("aria-expanded", details.open ? "true" : "false");
        this.changed(true);
      }.bind(this)
    );
    titleInput.addEventListener("click", function (event) {
      event.stopPropagation();
    });
    titleInput.addEventListener("keydown", function (event) {
      event.stopPropagation();
    });
    summary.addEventListener("click", function (event) {
      event.preventDefault();
      event.stopPropagation();
    });
    summary.addEventListener("keydown", function (event) {
      if (event.key === " " || event.key === "Enter") event.preventDefault();
    });
    summary.appendChild(toggleButton);
    summary.appendChild(titleInput);
    details.appendChild(summary);
    var content = el("div", "rbe-toggle-content");
    var nestedCanvas = el("div", "rbe-toggle-nested-canvas");
    var nestedBlocks = Array.isArray(data.blocks) && data.blocks.length ? data.blocks : data.text ? [{ type: "paragraph", data: { text: data.text } }] : [{ type: "paragraph", data: { text: "" } }];
    nestedBlocks.forEach(
      function (child) {
        var childType = BLOCK_TYPES.indexOf(child.type) >= 0 && child.type !== "toggle" ? child.type : "paragraph";
        var nestedBlock = this.createBlock(childType, child.data || this.defaultBlockData(childType), child.id || uid("block"));
        nestedBlock.classList.add("rbe-nested-block");
        nestedCanvas.appendChild(nestedBlock);
      }.bind(this)
    );
    content.appendChild(nestedCanvas);
    details.appendChild(content);
    body.appendChild(details);
  };

  BlockWriteAI.prototype.renderSignatureRow = function (body, data) {
    data = data || {};
    var wrap = el("div", "rbe-signature-row-editor rbe-multi-row-editor");
    var head = el("div", "rbe-signature-row-head");
    head.appendChild(
      el("div", "rbe-signature-row-title", {
        html: svgIcon("table-columns", "Multiple blocks in a row") + "<span>Multiple blocks in a row</span>"
      })
    );
    wrap.appendChild(head);

    var canvas = el("div", "rbe-signature-row-canvas", {
      title: this.readOnly ? "" : "Insert up to 3 blocks in this row"
    });
    var nestedBlocks = Array.isArray(data.blocks) && data.blocks.length ? data.blocks.slice(0, MAX_BLOCKS_PER_ROW) : [{ type: "paragraph", data: { text: "" } }];
    nestedBlocks.forEach(
      function (child) {
        var childType = BLOCK_TYPES.indexOf(child.type) >= 0 ? child.type : "paragraph";
        var nestedBlock = this.createBlock(childType, child.data || this.defaultBlockData(childType), child.id || uid("block"));
        nestedBlock.classList.add("rbe-nested-block");
        canvas.appendChild(nestedBlock);
      }.bind(this)
    );
    ensureRowPlaceholder(this, canvas);
    wrap.appendChild(canvas);
    body.appendChild(wrap);
  };

  BlockWriteAI.prototype.renderLinkPreview = function (body, data) {
    var wrap = el("div", "rbe-link-preview");
    var fields = el("div", "rbe-link-preview-fields");
    fields.appendChild(
      el("input", "rbe-link-preview-url", {
        type: "url",
        value: data.url || "",
        placeholder: "https://example.com",
        disabled: this.readOnly
      })
    );
    fields.appendChild(
      el("input", "rbe-link-preview-title", {
        type: "text",
        value: data.title || "",
        placeholder: "Link title",
        disabled: this.readOnly
      })
    );
    fields.appendChild(
      el("input", "rbe-link-preview-description", {
        type: "text",
        value: data.description || "",
        placeholder: "Short description",
        disabled: this.readOnly
      })
    );
    wrap.appendChild(fields);
    var card = el("a", "rbe-link-preview-card", {
      target: "_blank",
      rel: "noopener noreferrer"
    });
    card.setAttribute("href", isSafeUrl(data.url || "") ? data.url : "#");
    card.appendChild(el("span", "rbe-link-preview-icon", { html: svgIcon("arrow-up-right-from-square", "Open link") }));
    card.appendChild(el("strong", "rbe-link-preview-card-title", { text: data.title || data.url || "Link preview" }));
    card.appendChild(el("span", "rbe-link-preview-card-description", { text: data.description || "Add a URL, title, and description for this link." }));
    wrap.appendChild(card);
    body.appendChild(wrap);
  };

  BlockWriteAI.prototype.renderRaw = function (body, data) {
    var wrap = el("div", "rbe-raw-wrap");
    wrap.__rbeRawHtml = data.html || "";
    if (!this.readOnly) {
      var header = el("div", "rbe-raw-header");
      header.appendChild(el("strong", "", { html: svgIcon("file-code", "Raw HTML") + "<span>Raw HTML</span>" }));
      header.appendChild(
        el("button", "rbe-small-btn rbe-raw-format", {
          type: "button",
          title: "Format HTML",
          "aria-label": "Format HTML",
          html: svgIcon("wand-magic-sparkles", "Format HTML")
        })
      );
      header.appendChild(
        el("button", "rbe-small-btn rbe-raw-copy", {
          type: "button",
          title: "Copy HTML",
          "aria-label": "Copy HTML",
          html: svgIcon("copy", "Copy HTML")
        })
      );
      wrap.appendChild(header);
      var editorShell = el("div", "rbe-raw-editor");
      var input = el("textarea", "rbe-raw-input", {
        value: data.html || "",
        placeholder: "<div>Trusted HTML</div>",
        spellcheck: false,
        disabled: this.readOnly
      });
      editorShell.appendChild(input);
      input.addEventListener("input", function () {
        wrap.__rbeRawHtml = input.value;
      });
      wrap.appendChild(editorShell);
    }
    var preview = el("div", "rbe-raw-preview");
    updateRawPreview(preview, data.html);
    wrap.appendChild(preview);
    body.appendChild(wrap);
  };

  BlockWriteAI.prototype.renderAttachment = function (body, data) {
    var wrap = el("div", "rbe-attachment");
    var items = normalizeAttachmentItems(data);
    wrap.__rbeAttachmentItems = cloneJSON(items);
    var state = el("textarea", "rbe-attachment-items-json", {
      value: JSON.stringify(items),
      hidden: true,
      "aria-hidden": "true",
      tabindex: "-1"
    });
    wrap.appendChild(state);
    var canEdit = !this.readOnly;
    if (!items.length) {
      var fileDropzone = el("label", "rbe-upload-dropzone rbe-attachment-dropzone");
      fileDropzone.appendChild(
        el("input", "rbe-attachment-file rbe-upload-input", {
          type: "file",
          accept: ATTACHMENT_ACCEPT,
          multiple: true,
          disabled: this.readOnly
        })
      );
      fileDropzone.appendChild(el("span", "rbe-upload-icon", { html: svgIcon("cloud-arrow-up", "Upload attachment") }));
      fileDropzone.appendChild(el("strong", "", { text: "Drag and drop or click here" }));
      fileDropzone.appendChild(el("span", "rbe-upload-main", { text: "to upload attachment" }));
      fileDropzone.appendChild(el("small", "", { text: "Audio, videos, PDF, Word, Excel, PowerPoint, CSV, TXT, and RTF only" }));
      this.bindUploadDropTarget(fileDropzone, "attachment");
      wrap.appendChild(fileDropzone);
    } else {
      var grid = el("div", "rbe-attachment-grid", {
        title: this.readOnly ? "" : "Drop files here to add more attachments"
      });
      items.forEach(function (item) {
        var kind = attachmentKindKey(item.type, item.name);
        var itemAlignment = cleanAlignment(item.alignment);
        var card = el("div", "rbe-attachment-card" + (canEdit ? " is-editable" : ""), {
          title: item.name || "Open attachment",
          dataset: {
            name: item.name,
            url: item.url,
            type: item.type,
            size: item.size,
            kind: kind,
            alignment: itemAlignment
          }
        });
        card.__rbeMediaItem = cloneJSON(item);
        var link = el("a", "rbe-attachment-card-link", {
          href: item.url,
          target: "_blank",
          rel: "noopener noreferrer",
          title: item.name || "Open attachment"
        });
        link.appendChild(el("span", "rbe-attachment-card-icon", { html: svgIcon(attachmentIconName(item.type, item.name), "Attachment") }));
        link.appendChild(el("strong", "rbe-attachment-card-type", { text: attachmentKindLabel(item.type, item.name) }));
        link.appendChild(el("span", "rbe-attachment-card-name", { text: item.name || "Open attachment" }));
        card.appendChild(link);
        if (canEdit) {
          var attachmentTools = el("div", "rbe-attachment-delete-menu");
          attachmentTools.appendChild(
            el("button", "rbe-media-action rbe-media-action-bottom rbe-media-delete rbe-attachment-delete", {
              type: "button",
              title: "Remove attachment",
              "aria-label": "Remove attachment",
              html: svgIcon("trash-can", "Remove attachment")
            })
          );
          card.appendChild(attachmentTools);
        }
        grid.appendChild(card);
      });
      if (!this.readOnly) grid.appendChild(el("small", "rbe-attachment-replace-hint", { text: "Drop more files to add" }));
      this.bindUploadDropTarget(grid, "attachment");
      wrap.appendChild(grid);
    }
    body.appendChild(wrap);
    if (canEdit && items.length) {
      setTimeout(
        function () {
          this.updateMediaActionAvailability(wrap);
        }.bind(this),
        0
      );
    }
  };

  BlockWriteAI.prototype.handleRootClick = function (event) {
    var target = event.target;
    if (event.detail > 1 && target.closest(".rbe-editable, [contenteditable='true'], textarea, input")) {
      return;
    }

    var mediaGearButton = target.closest(".rbe-media-gear");
    if (mediaGearButton) {
      event.preventDefault();
      event.stopPropagation();
      this.toggleMediaMenu(mediaGearButton);
      return;
    }

    var mediaAlignButton = target.closest(".rbe-media-align");
    if (mediaAlignButton) {
      event.preventDefault();
      event.stopPropagation();
      this.alignMediaItem(mediaAlignButton, mediaAlignButton.dataset.align);
      return;
    }

    var imageCropButton = target.closest(".rbe-image-crop-toggle");
    if (imageCropButton) {
      event.preventDefault();
      event.stopPropagation();
      var imageStage = imageCropButton.closest(".rbe-image-stage");
      this.setImageCropMode(imageStage, !(imageStage && imageStage.classList.contains("is-image-crop-mode")));
      this.changed(true);
      return;
    }

    var mediaDeleteButton = target.closest(".rbe-media-delete");
    if (mediaDeleteButton) {
      event.preventDefault();
      event.stopPropagation();
      this.removeMediaItem(mediaDeleteButton);
      return;
    }

    var clearButton = target.closest("[data-rbe-clear='true']");
    if (clearButton) {
      event.preventDefault();
      event.stopPropagation();
      this.handleClearButton(clearButton);
      return;
    }

    var themeToggle = target.closest("[data-rbe-theme-toggle='true']");
    if (themeToggle) {
      event.preventDefault();
      event.stopPropagation();
      this.toggleTheme();
      return;
    }

    var saveButton = target.closest("[data-rbe-save='true']");
    if (saveButton) {
      event.preventDefault();
      event.stopPropagation();
      this.handleSaveButton(saveButton);
      return;
    }

    var exportToggle = target.closest("[data-rbe-export-toggle='true']");
    if (exportToggle) {
      event.preventDefault();
      event.stopPropagation();
      this.toggleExportMenu(exportToggle);
      return;
    }

    var exportOption = target.closest("[data-rbe-export]");
    if (exportOption) {
      event.preventDefault();
      event.stopPropagation();
      this.handleExport(exportOption.dataset.rbeExport);
      return;
    }

    var exportHtmlButton = target.closest("[data-rbe-export-html='true']");
    if (exportHtmlButton) {
      event.preventDefault();
      event.stopPropagation();
      this.handleExportHTML();
      return;
    }

    var dashboardButton = target.closest("[data-rbe-show-dashboard='true']");
    if (dashboardButton) {
      event.preventDefault();
      event.stopPropagation();
      this.handleShowDashboard();
      return;
    }

    var inlineButton = target.closest(".rbe-inline-btn");
    if (inlineButton) {
      this.formatInline(inlineButton.dataset.command, inlineButton.dataset.value);
      return;
    }

    var colorReset = target.closest(".rbe-color-reset");
    if (colorReset) {
      event.preventDefault();
      event.stopPropagation();
      this.formatInline(colorReset.dataset.command);
      return;
    }

    if (target.closest(".rbe-comment-save")) {
      this.saveCommentFromModal();
      return;
    }

    if (target.closest(".rbe-comment-cancel, .rbe-comment-close") && !target.closest(".rbe-link-modal")) {
      this.hideCommentModal();
      return;
    }

    if (target.closest(".rbe-link-save")) {
      this.saveLinkFromModal();
      return;
    }

    if (target.closest(".rbe-link-cancel, .rbe-link-close")) {
      this.hideLinkModal();
      return;
    }

    var dividerButton = target.closest(".rbe-divider-style-btn");
    if (dividerButton) {
      var divider = dividerButton.closest(".rbe-delimiter-wrap");
      if (divider) {
        divider.dataset.style = dividerButton.dataset.style || "line";
        Array.prototype.slice.call(divider.querySelectorAll(".rbe-divider-style-btn")).forEach(function (button) {
          var active = button === dividerButton;
          button.classList.toggle("is-active", active);
          button.setAttribute("aria-pressed", active ? "true" : "false");
        });
        divider.classList.add("rbe-divider-toolbar-dismissed");
        dividerButton.blur();
        this.changed(true);
      }
      return;
    }

    var block = closestBlock(target);
    if (target.closest(".rbe-toggle-add-block")) {
      this.addToggleNestedBlock(block);
      return;
    }

    var blockAction = target.closest(".rbe-block-action");
    if (blockAction) {
      this.handleBlockAction(block, blockAction.dataset.action);
      return;
    }

    if (target.closest(".rbe-block-add")) {
      this.showBlockMenu(block, target);
      return;
    }

    var menuItem = target.closest(".rbe-menu-item");
    if (menuItem) {
      this.handleMenuItem(menuItem);
      return;
    }

    var checklistAction = target.closest(".rbe-checklist-action");
    if (checklistAction) {
      this.applyChecklistNestAction(block, checklistAction.dataset.action);
      return;
    }

    if (target.closest(".rbe-check-toggle")) {
      this.toggleChecklistItem(block, target.closest(".rbe-check-item"));
      return;
    }

    if (target.closest(".rbe-checklist-add")) {
      this.addChecklistItem(block);
      return;
    }

    if (target.closest(".rbe-check-remove")) {
      this.removeChecklistItem(block, target.closest(".rbe-check-item"));
      return;
    }

    if (target.closest(".rbe-code-copy")) {
      this.copyCode(target.closest(".rbe-code-wrap"));
      return;
    }

    if (target.closest(".rbe-raw-copy")) {
      this.copyRaw(block);
      return;
    }

    if (target.closest(".rbe-code-format")) {
      this.formatCodeBlock(block);
      return;
    }

    if (target.closest(".rbe-raw-format")) {
      this.formatRawBlock(block);
      return;
    }

    if (target.closest(".rbe-image-apply")) {
      this.applyImageUrl(block);
      return;
    }

    if (target.closest(".rbe-embed-apply")) {
      this.applyEmbed(block);
      return;
    }

    var listNestAction = target.closest(".rbe-list-action");
    if (listNestAction) {
      this.applyListNestAction(block, listNestAction.dataset.action);
      return;
    }

    if (target.closest(".rbe-list-add")) {
      this.addListItem(block);
      return;
    }

    if (target.closest(".rbe-list-remove")) {
      this.removeListItem(block, target.closest(".rbe-list-item"));
      return;
    }

    var tableAction = target.closest(".rbe-table-action");
    if (tableAction) {
      this.applyTableAction(block, tableAction.dataset.action);
      return;
    }
  };

  BlockWriteAI.prototype.handleRootChange = function (event) {
    var target = event.target;

    if (target.dataset && (target.dataset.command === "foreColor" || target.dataset.command === "hiliteColor")) {
      this.formatInline(target.dataset.command, target.value);
      return;
    }

    if (target.dataset && (target.dataset.command === "fontFamily" || target.dataset.command === "fontSize")) {
      this.formatInline(target.dataset.command, target.value);
      return;
    }

    var block = closestBlock(target);
    if (!block) return;

    if (target.matches(".rbe-heading-level")) {
      var heading = block.querySelector(".rbe-heading");
      heading.dataset.level = target.value;
      heading.className = "rbe-editable rbe-heading rbe-h" + target.value;
    }

    if (target.matches(".rbe-list-style")) {
      this.switchListStyle(block, target.value);
    }

    if (target.matches(".rbe-callout-tone")) {
      var callout = block.querySelector(".rbe-callout");
      callout.dataset.tone = target.value;
    }

    if (target.matches(".rbe-button-bg, .rbe-button-color, .rbe-button-align")) {
      this.refreshButtonPreview(block);
    }

    if (target.matches(".rbe-code-language, .rbe-code-header-color, .rbe-code-bg-color")) {
      this.refreshCodeBlock(block);
    }

    if (target.matches(".rbe-image-file")) {
      this.applyImageFiles(block, Array.prototype.slice.call(target.files || []));
    }

    if (target.matches(".rbe-attachment-file")) {
      this.applyAttachmentFiles(block, Array.prototype.slice.call(target.files || []));
      return;
    }

    if (target.dataset.role === "table-heading") {
      this.refreshTableHeading(block);
    }

    if (target.matches(".rbe-table-align, .rbe-table-width, .rbe-table-border-style, .rbe-table-border-width")) {
      this.updateTableLayout(block);
    }

    this.changed();
  };

  BlockWriteAI.prototype.handleInput = function (event) {
    if (this.silent) return;
    var block = closestBlock(event.target);
    if (!block) return;
    this.handleMarkdownShortcut(block, event.target);
    this.handleSlashMenu(block, event.target);
    if (event.target.matches(".rbe-raw-input")) {
      var preview = block.querySelector(".rbe-raw-preview");
      updateRawPreview(preview, event.target.value);
    }
    if (event.target.matches(".rbe-code-input")) {
      this.refreshCodeBlock(block);
    }
    if (event.target.matches(".rbe-code-header-color, .rbe-code-bg-color")) {
      this.refreshCodeBlock(block);
    }
    if (event.target.matches(".rbe-table-width, .rbe-table-border-width")) {
      this.updateTableLayout(block);
    }
    if (event.target.matches(".rbe-button-text, .rbe-button-url, .rbe-button-bg, .rbe-button-color")) {
      this.refreshButtonPreview(block);
    }
    if (event.target.matches(".rbe-link-preview-url, .rbe-link-preview-title, .rbe-link-preview-description")) {
      this.refreshLinkPreview(block);
    }
    this.changed();
  };

  BlockWriteAI.prototype.handleKeydown = function (event) {
    if (this.readOnly) return;

    if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "k") {
      event.preventDefault();
      this.formatInline("createLink");
      return;
    }

    if (this.menu && !this.menu.hidden && (event.key === "ArrowDown" || event.key === "ArrowUp" || event.key === "Enter" || event.key === "Escape")) {
      this.handleMenuKeyboard(event);
      return;
    }

    var target = event.target;
    var block = closestBlock(target);
    if (!block) return;

    if (event.key === "Enter" && !event.shiftKey && this.shouldCreateNewBlock(target, block)) {
      event.preventDefault();
      var nestedContainer = block.parentElement && (block.parentElement.classList.contains("rbe-toggle-nested-canvas") || block.parentElement.classList.contains("rbe-letterhead-nested-canvas") || block.parentElement.classList.contains("rbe-signature-row-canvas")) ? block.parentElement : null;
      if (isRowCanvas(nestedContainer)) {
        if (isEmptyRowPlaceholderBlock(block) || rowContentBlocks(nestedContainer).length >= MAX_BLOCKS_PER_ROW) {
          if (!isEmptyRowPlaceholderBlock(block)) this.showToast("Maximum 3 blocks are allowed in one row.", "warning");
          return;
        }
      }
      var next = nestedContainer ? this.createBlock("paragraph", { text: "" }, uid("block")) : this.insertBlock("paragraph", { text: "" }, this.indexOfBlock(block) + 1);
      if (nestedContainer) {
        next.classList.add("rbe-nested-block");
        nestedContainer.insertBefore(next, block.nextElementSibling);
        this.scheduleSelectEnhance();
      }
      this.focusBlock(next);
      this.changed(true);
      return;
    }

    if (event.key === "Backspace" && event.isTrusted !== false && this.isEmptyEditable(target)) {
      return;
    }
  };

  BlockWriteAI.prototype.handlePaste = function (event) {
    if (this.readOnly) return;
    var block = closestBlock(event.target);
    if (!block || !event.clipboardData) return;

    var files = Array.prototype.slice.call(event.clipboardData.files || []);
    var images = files.filter(isImageFile);
    if (images.length) {
      event.preventDefault();
      var imageBlock = block.dataset.type === "image" ? block : this.insertBlock("image", {}, this.indexOfBlock(block) + 1);
      this.applyImageFiles(imageBlock, images);
      return;
    }

    var text = event.clipboardData.getData("text/plain");
    if (/^https?:\/\/\S+$/.test(text.trim()) && (block.textContent || "").trim() === "") {
      event.preventDefault();
      this.transformBlock(block, "embed", toEmbed(text.trim()));
      return;
    }

    if (text && text.indexOf("\n\n") > -1 && block.dataset.type === "paragraph" && (block.textContent || "").trim() === "") {
      event.preventDefault();
      var blocks = textToParagraphs(text);
      var start = this.indexOfBlock(block);
      block.remove();
      blocks.forEach(
        function (item, offset) {
          this.insertBlock(item.type, item.data, start + offset, item.id);
        }.bind(this)
      );
      this.changed();
    }
  };

  BlockWriteAI.prototype.handleDrop = function (event) {
    if (this.readOnly) return;
    var files = Array.prototype.slice.call(event.dataTransfer && event.dataTransfer.files ? event.dataTransfer.files : []);
    if (!files.length) return;
    var block = closestBlock(event.target) || this.getCurrentBlock();
    var first = files[0];
    var images = files.filter(isImageFile);
    var attachments = files.filter(isAllowedAttachmentFile);
    event.preventDefault();

    if (block && block.dataset.type === "attaches") {
      if (!attachments.length || isImageFile(first)) {
        alert(attachmentRestrictionMessage());
        return;
      }
      this.applyAttachmentFiles(block, attachments);
      return;
    }

    if (block && block.dataset.type === "image") {
      if (!images.length) {
        alert("The Image block only accepts image files.");
        return;
      }
      this.applyImageFiles(block, images);
      return;
    }

    if (images.length) {
      var imageBlock = this.insertBlock("image", {}, block ? this.indexOfBlock(block) + 1 : this.canvas.children.length);
      this.applyImageFiles(imageBlock, images);
      return;
    }

    if (attachments.length) {
      var attachBlock = this.insertBlock("attaches", {}, block ? this.indexOfBlock(block) + 1 : this.canvas.children.length);
      this.applyAttachmentFiles(attachBlock, attachments);
      return;
    }

    alert(attachmentRestrictionMessage());
  };

  BlockWriteAI.prototype.handleSelectionChange = function () {
    if (!this.root || this.readOnly) return;
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || selection.isCollapsed) {
      if (this.shouldKeepInlineToolbarOpen()) return;
      this.inlineToolbar.hidden = true;
      return;
    }
    var anchor = selection.anchorNode;
    if (!this.root.contains(anchor)) {
      this.inlineToolbar.hidden = true;
      return;
    }
    var range = selection.getRangeAt(0);
    this.savedRange = range.cloneRange();
    var rect = range.getBoundingClientRect();
    if (!rect || (!rect.width && !rect.height)) return;
    this.inlineToolbar.hidden = false;
    this.inlineToolbar.style.transform = "none";
    if (this.inlineToolbarPinnedUntil && Date.now() < this.inlineToolbarPinnedUntil && this.inlineToolbarPinnedPosition) {
      this.inlineToolbar.style.left = this.inlineToolbarPinnedPosition.left;
      this.inlineToolbar.style.top = this.inlineToolbarPinnedPosition.top;
      this.updateInlineToolbarState();
      return;
    }
    this.inlineToolbarPinnedPosition = null;
    var toolbarRect = this.inlineToolbar.getBoundingClientRect();
    var desiredLeft = rect.left + rect.width / 2 - toolbarRect.width / 2;
    var block = closestBlock(anchor);
    var bodyRect = block && block.querySelector(".rbe-block-body") ? block.querySelector(".rbe-block-body").getBoundingClientRect() : null;
    var canvasRect = this.canvas ? this.canvas.getBoundingClientRect() : null;
    var minLeft = bodyRect ? Math.max(8, bodyRect.left) : canvasRect ? Math.max(8, canvasRect.left) : 8;
    var rightBoundary = canvasRect ? Math.min(window.innerWidth - 8, canvasRect.right) : window.innerWidth - 8;
    if (bodyRect) rightBoundary = Math.min(rightBoundary, bodyRect.right);
    var maxLeft = Math.max(minLeft, rightBoundary - toolbarRect.width);
    var left = Math.min(maxLeft, Math.max(minLeft, desiredLeft));
    this.inlineToolbar.style.left = left + "px";
    this.inlineToolbar.style.top = Math.max(8, rect.top - toolbarRect.height - 8) + "px";
    this.updateInlineToolbarState();
  };

  BlockWriteAI.prototype.keepInlineToolbarOpen = function () {
    this.inlineToolbarStickyUntil = Date.now() + 900;
  };

  BlockWriteAI.prototype.pinInlineToolbarPosition = function () {
    if (!this.inlineToolbar || this.inlineToolbar.hidden) return;
    this.inlineToolbarPinnedPosition = {
      left: this.inlineToolbar.style.left || this.inlineToolbar.getBoundingClientRect().left + "px",
      top: this.inlineToolbar.style.top || this.inlineToolbar.getBoundingClientRect().top + "px"
    };
    this.inlineToolbarPinnedUntil = Date.now() + 700;
  };

  BlockWriteAI.prototype.shouldKeepInlineToolbarOpen = function () {
    return this.inlineToolbar && !this.inlineToolbar.hidden && Date.now() < this.inlineToolbarStickyUntil;
  };

  BlockWriteAI.prototype.updateInlineToolbarState = function () {
    if (!this.inlineToolbar || this.inlineToolbar.hidden) return;
    var editable = this.getSelectionEditable();
    var selection = window.getSelection && window.getSelection();
    var selectedNode = selection && selection.anchorNode ? selection.anchorNode : null;
    selectedNode = selectedNode && selectedNode.nodeType === Node.TEXT_NODE ? selectedNode.parentElement : selectedNode;
    var align = editable && editable.style && editable.style.textAlign ? cleanAlignment(editable.style.textAlign) : "left";
    Array.prototype.slice.call(this.inlineToolbar.querySelectorAll(".rbe-inline-btn")).forEach(function (button) {
      var command = button.dataset.command;
      var active = false;
      if (command === "bold" || command === "italic" || command === "underline" || command === "strikeThrough") {
        try {
          active = document.queryCommandState(command);
        } catch (error) {
          active = false;
        }
      }
      if (command === "justifyLeft") active = align === "left";
      if (command === "justifyCenter") active = align === "center";
      if (command === "justifyRight") active = align === "right";
      if (command === "formatBlock" && button.dataset.value === "blockquote") {
        var node = selection && selection.anchorNode ? selection.anchorNode : null;
        node = node && node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
        active = !!(node && node.closest && node.closest(".rbe-inline-quote"));
      }
      button.classList.toggle("is-active", !!active);
      button.setAttribute("aria-pressed", active ? "true" : "false");
    });

    var styleTarget = editable;
    var cursor = selectedNode;
    while (cursor && editable && cursor !== editable && editable.contains(cursor)) {
      if (cursor.style && (cursor.style.fontFamily || cursor.style.fontSize || cursor.style.color || cursor.style.backgroundColor)) {
        styleTarget = cursor;
        break;
      }
      cursor = cursor.parentElement;
    }
    var computed = styleTarget ? window.getComputedStyle(styleTarget) : null;
    var family = this.inlineToolbar.querySelector(".rbe-font-family-select");
    var size = this.inlineToolbar.querySelector(".rbe-font-size-select");
    if (family && computed && computed.fontFamily) {
      var computedFamily = computed.fontFamily.toLowerCase();
      var familyValue = Array.prototype.slice.call(family.options).find(function (option) {
        var optionValue = String(option.value || "").toLowerCase();
        var firstFamily = optionValue.split(",")[0].replace(/"/g, "").trim();
        return computedFamily === optionValue || (firstFamily && computedFamily.indexOf(firstFamily) >= 0) || computedFamily.indexOf(option.text.toLowerCase()) >= 0;
      });
      syncSelectValue(family, familyValue ? familyValue.value : "Arial, sans-serif");
    }
    var headingSelection = !!(editable && editable.classList && editable.classList.contains("rbe-heading"));
    setSelectVisible(size, !headingSelection);
    if (size && computed && computed.fontSize) {
      var px = String(Math.round(parseFloat(computed.fontSize) || 16));
      syncSelectValue(size, Array.prototype.slice.call(size.options).some(function (option) { return option.value === px; }) ? px : "16");
    }
  };

  BlockWriteAI.prototype.handleMenuKeyboard = function (event) {
    var items = Array.prototype.slice.call(this.menu.querySelectorAll(".rbe-menu-item"));
    if (!items.length) return;
    var current = this.menu.querySelector(".is-active");
    var index = Math.max(0, items.indexOf(current));

    if (event.key === "Escape") {
      event.preventDefault();
      this.hideMenus();
      return;
    }

    if (event.key === "ArrowDown" || event.key === "ArrowUp") {
      event.preventDefault();
      if (current) current.classList.remove("is-active");
      index = event.key === "ArrowDown" ? (index + 1) % items.length : (index - 1 + items.length) % items.length;
      items[index].classList.add("is-active");
      items[index].scrollIntoView({ block: "nearest" });
      return;
    }

    if (event.key === "Enter") {
      event.preventDefault();
      (current || items[0]).click();
    }
  };

  BlockWriteAI.prototype.handleMarkdownShortcut = function (block, target) {
    if (!target.isContentEditable || block.dataset.type !== "paragraph") return;
    var text = target.textContent || "";
    var map = [
      [/^#\s$/, "heading", { level: 1, text: "" }],
      [/^##\s$/, "heading", { level: 2, text: "" }],
      [/^###\s$/, "heading", { level: 3, text: "" }],
      [/^>\s$/, "quote", { text: "", caption: "" }],
      [/^-\s$/, "list", { style: "unordered", items: [""] }],
      [/^1\.\s$/, "list", { style: "ordered", items: [""] }],
      [/^\[\]\s$/, "checklist", { items: [{ text: "", checked: false }] }],
      [/^```\s?$/, "code", { code: "", language: "" }]
    ];

    for (var i = 0; i < map.length; i += 1) {
      if (map[i][0].test(text)) {
        this.transformBlock(block, map[i][1], map[i][2]);
        this.focusBlock(block);
        return;
      }
    }
  };

  BlockWriteAI.prototype.handleSlashMenu = function (block, target) {
    if (!target.isContentEditable || block.dataset.type !== "paragraph") return;
    var text = (target.textContent || "").trim();
    if (text.indexOf("/") === 0) {
      this.slashBlock = block;
      this.showToolMenu(block, target, text.slice(1).toLowerCase(), true);
    } else if (this.slashBlock === block) {
      this.hideMenus();
      this.slashBlock = null;
    }
  };

  BlockWriteAI.prototype.handleMenuItem = function (menuItem) {
    var action = menuItem.dataset.action;
    var type = menuItem.dataset.type;
    var block = this.currentMenuBlock || this.slashBlock || this.getCurrentBlock();
    var slashSource = this.slashBlock;
    this.hideMenus();
    if (!block && action !== "insert") return;

    if (action === "insert") {
      var insertFeature = this.getPremiumFeatureForTool(type);
      if (!this.canUseTool(type)) {
        this.showPremiumLocked(insertFeature);
        return;
      }
      var targetRowCanvas = null;
      var targetRowReference = slashSource || block;
      if (block && block.parentElement && isRowCanvas(block.parentElement)) {
        targetRowCanvas = block.parentElement;
      }
      if (slashSource && slashSource.parentElement && isRowCanvas(slashSource.parentElement)) {
        targetRowCanvas = slashSource.parentElement;
        targetRowReference = slashSource;
      }
      var replacingRowPlaceholder = targetRowCanvas && targetRowReference && isEmptyRowPlaceholderBlock(targetRowReference);
      if (targetRowCanvas && !replacingRowPlaceholder && rowContentBlocks(targetRowCanvas).length >= MAX_BLOCKS_PER_ROW) {
        this.showToast("Maximum 3 blocks are allowed in one row.", "warning");
        return;
      }
      var runInsert = function () {
        var index = block ? this.indexOfBlock(block) + 1 : this.canvas.children.length;
        var nestedContainer = block && block.parentElement && (block.parentElement.classList.contains("rbe-toggle-nested-canvas") || block.parentElement.classList.contains("rbe-letterhead-nested-canvas") || block.parentElement.classList.contains("rbe-signature-row-canvas")) ? block.parentElement : null;
        var nestedReference = slashSource || block;
        if (slashSource) {
          nestedContainer = slashSource.parentElement && (slashSource.parentElement.classList.contains("rbe-toggle-nested-canvas") || slashSource.parentElement.classList.contains("rbe-letterhead-nested-canvas") || slashSource.parentElement.classList.contains("rbe-signature-row-canvas")) ? slashSource.parentElement : nestedContainer;
          index = this.indexOfBlock(slashSource) + 1;
          this.clearSlashCommand(slashSource);
        }
        if (isRowCanvas(nestedContainer) && !nestedReference) {
          nestedReference = Array.prototype.slice.call(nestedContainer.children).find(isEmptyRowPlaceholderBlock) || null;
        }
        var replaceRootPlaceholder = !nestedContainer && isOnlyRootPlaceholderBlock(this, slashSource || block);
        var inserted = nestedContainer || replaceRootPlaceholder ? this.createBlock(type, this.defaultBlockData(type), uid("block")) : this.insertBlock(type, this.defaultBlockData(type), index);
        if (nestedContainer) {
          inserted.classList.add("rbe-nested-block");
          if (isRowCanvas(nestedContainer) && nestedReference && isEmptyRowPlaceholderBlock(nestedReference)) nestedContainer.replaceChild(inserted, nestedReference);
          else if (nestedReference && nestedReference.parentElement === nestedContainer) nestedContainer.insertBefore(inserted, nestedReference.nextElementSibling);
          else nestedContainer.appendChild(inserted);
          this.scheduleSelectEnhance();
        } else if (replaceRootPlaceholder) {
          this.canvas.replaceChild(inserted, slashSource || block);
          this.scheduleSelectEnhance();
        }
        this.focusBlock(inserted);
        this.changed(true);
      }.bind(this);
      if (insertFeature && insertFeature !== "ai") {
        this.consumePremiumUsage(insertFeature, { action: "insert", source: "editor_menu", tool: type }).then(runInsert).catch(function (error) {
          this.showToast(error && error.message ? error.message : "Premium usage limit check failed.", "warning");
        }.bind(this));
      } else {
        runInsert();
      }
      return;
    }

    if (action === "convert") {
      var convertFeature = this.getPremiumFeatureForTool(type);
      if (!this.canUseTool(type)) {
        this.showPremiumLocked(convertFeature);
        return;
      }
      var runConvert = function () {
        this.transformBlock(block, type, this.dataFromBlockForType(block, type));
        this.focusBlock(block);
        this.changed(true);
      }.bind(this);
      if (convertFeature && convertFeature !== "ai") {
        this.consumePremiumUsage(convertFeature, { action: "convert", source: "editor_menu", tool: type }).then(runConvert).catch(function (error) {
          this.showToast(error && error.message ? error.message : "Premium usage limit check failed.", "warning");
        }.bind(this));
      } else {
        runConvert();
      }
      return;
    }

    this.handleBlockAction(block, action);
  };

  BlockWriteAI.prototype.clearSlashCommand = function (block) {
    var editable = block && block.querySelector(".rbe-paragraph");
    if (!editable) return;
    var text = (editable.textContent || "").trim();
    if (text.indexOf("/") === 0) editable.innerHTML = "";
  };

  BlockWriteAI.prototype.handleBlockAction = function (block, action) {
    if (!block || !action) return;
    var nestedContainer = block.parentElement && (block.parentElement.classList.contains("rbe-toggle-nested-canvas") || block.parentElement.classList.contains("rbe-letterhead-nested-canvas") || block.parentElement.classList.contains("rbe-signature-row-canvas")) ? block.parentElement : null;
    var container = nestedContainer || this.canvas;
    if (action === "duplicate") {
      var data = this.serializeBlock(block);
      if (isRowCanvas(nestedContainer) && !isEmptyRowPlaceholderBlock(block) && rowContentBlocks(nestedContainer).length >= MAX_BLOCKS_PER_ROW) {
        this.showToast("Maximum 3 blocks are allowed in one row.", "warning");
        return;
      }
      var duplicateFeature = this.getPremiumFeatureForTool(data.type);
      var runDuplicate = function () {
        data.id = uid("block");
        var copy = nestedContainer ? this.createBlock(data.type, data.data, data.id) : this.insertBlock(data.type, data.data, this.indexOfBlock(block) + 1, data.id);
        if (nestedContainer) {
          copy.classList.add("rbe-nested-block");
          nestedContainer.insertBefore(copy, block.nextElementSibling);
          this.scheduleSelectEnhance();
        }
        this.focusBlock(copy);
        this.changed(true);
      }.bind(this);
      if (duplicateFeature && duplicateFeature !== "ai") {
        this.consumePremiumUsage(duplicateFeature, { action: "duplicate", source: "block_action", tool: data.type }).then(runDuplicate).catch(function (error) {
          this.showToast(error && error.message ? error.message : "Premium usage limit check failed.", "warning");
        }.bind(this));
      } else {
        runDuplicate();
      }
      return;
    }

    if (action === "delete") {
      var focus = block.previousElementSibling || block.nextElementSibling;
      block.remove();
      if (!container.children.length) {
        var empty = this.createBlock("paragraph", {}, uid("block"));
        if (nestedContainer) empty.classList.add("rbe-nested-block");
        container.appendChild(empty);
      }
      if (isRowCanvas(nestedContainer)) ensureRowPlaceholder(this, nestedContainer);
      if (focus) this.focusBlock(focus);
      this.changed(true);
      return;
    }

    if (action === "move-up" || action === "move-down") {
      var isHeavyAttachment = block.dataset.type === "attaches";
      var sibling = action === "move-up" ? block.previousElementSibling : block.nextElementSibling;
      if (sibling && action === "move-up") container.insertBefore(block, sibling);
      if (sibling && action === "move-down") container.insertBefore(sibling, block);
      if (isHeavyAttachment) {
        block.scrollIntoView({ block: "nearest" });
        this.changed();
      } else {
        this.focusBlock(block);
        this.changed(true);
      }
    }
  };

  BlockWriteAI.prototype.addToggleNestedBlock = function (block) {
    if (!block || block.dataset.type !== "toggle" || this.readOnly) return;
    var nestedCanvas = block.querySelector(".rbe-toggle-nested-canvas");
    var select = block.querySelector(".rbe-toggle-block-type");
    if (!nestedCanvas || !select) return;
    var type = BLOCK_TYPES.indexOf(select.value) >= 0 && select.value !== "toggle" ? select.value : "paragraph";
    if (!this.canUseTool(type)) {
      this.showPremiumLocked(this.getPremiumFeatureForTool(type));
      return;
    }
    var nestedBlock = this.createBlock(type, this.defaultBlockData(type), uid("block"));
    nestedBlock.classList.add("rbe-nested-block");
    nestedCanvas.appendChild(nestedBlock);
    this.scheduleSelectEnhance();
    this.focusBlock(nestedBlock);
    this.changed(true);
  };

  BlockWriteAI.prototype.showBlockMenu = function (block, anchor) {
    this.currentMenuBlock = block;
    this.slashBlock = null;
    this.menu.innerHTML = "";

    var group = el("div", "rbe-menu-section");
    group.appendChild(el("div", "rbe-menu-title", { text: "Insert block" }));
    this.availableTools.filter(
      function (type) {
        return this.shouldShowToolInMenu(type);
      }.bind(this)
    ).forEach(
      function (type) {
        group.appendChild(this.createMenuButton(type, "insert"));
      }.bind(this)
    );
    this.menu.appendChild(group);

    this.positionMenu(anchor || block);
  };

  BlockWriteAI.prototype.showToolMenu = function (block, anchor, query, slash) {
    this.currentMenuBlock = block;
    this.menu.innerHTML = "";
    var list = el("div", "rbe-menu-section");
    var matches = this.availableTools.filter(function (type) {
      var meta = TOOL_LABELS[type];
      return this.shouldShowToolInMenu(type) && (!query || meta.label.toLowerCase().indexOf(query) >= 0 || type.indexOf(query) >= 0);
    }.bind(this));
    if (!matches.length) {
      list.appendChild(el("div", "rbe-empty-menu", { text: "No blocks found" }));
    } else {
      matches.forEach(
        function (type, index) {
          var button = this.createMenuButton(type, "insert");
          if (index === 0) button.classList.add("is-active");
          list.appendChild(button);
        }.bind(this)
      );
    }
    this.menu.appendChild(list);
    this.positionMenu(anchor || block);
    if (slash) this.slashBlock = block;
  };

  BlockWriteAI.prototype.createMenuButton = function (type, action) {
    var meta = TOOL_LABELS[type] || TOOL_LABELS.paragraph;
    var feature = this.getPremiumFeatureForTool(type);
    var locked = !!feature && !this.isPremiumFeatureEnabled(feature);
    return el("button", "rbe-menu-item" + (locked ? " is-premium-locked" : ""), {
      type: "button",
      title: locked ? this.premiumMessage(feature) : meta.label,
      "data-premium-locked": locked ? "true" : "false",
      dataset: { type: type, action: action, premiumFeature: feature || "" },
      html:
        '<span class="rbe-menu-icon">' +
        svgIcon(meta.icon, meta.label) +
        '</span><span><strong>' +
        escapeHTML(meta.label) +
        (locked ? '<em class="rbe-premium-badge">' + svgIcon("lock", "Locked premium feature") + "<span>Premium</span></em>" : "") +
        "</strong><small>" +
        escapeHTML(locked ? this.premiumMessage(feature) : meta.hint) +
        "</small></span>"
    });
  };

  BlockWriteAI.prototype.positionMenu = function (anchor) {
    var rect = anchor.getBoundingClientRect ? anchor.getBoundingClientRect() : this.root.getBoundingClientRect();
    this.menu.hidden = false;
    var menuWidth = Math.min(340, Math.max(260, window.innerWidth - 24));
    var left = Math.max(12, Math.min(rect.left, window.innerWidth - menuWidth - 12));
    this.menu.style.width = menuWidth + "px";
    this.menu.style.maxHeight = "min(440px, calc(100vh - 24px))";

    var below = Math.max(0, window.innerHeight - rect.bottom - 12);
    var above = Math.max(0, rect.top - 12);
    var openUp = below < 260 && above > below;
    var available = openUp ? above : below;
    var maxHeight = Math.max(180, Math.min(440, available - 6, window.innerHeight - 24));
    this.menu.style.maxHeight = maxHeight + "px";

    var menuHeight = Math.min(this.menu.scrollHeight || maxHeight, maxHeight);
    var top = openUp ? rect.top - menuHeight - 6 : rect.bottom + 6;
    top = Math.max(12, Math.min(top, window.innerHeight - menuHeight - 12));
    this.menu.dataset.placement = openUp ? "top" : "bottom";
    this.menu.style.left = left + "px";
    this.menu.style.top = top + "px";
  };

  BlockWriteAI.prototype.hideMenus = function () {
    this.menu.hidden = true;
    this.currentMenuBlock = null;
    this.slashBlock = null;
  };

  BlockWriteAI.prototype.formatInline = function (command, value) {
    this.pinInlineToolbarPosition();
    this.restoreSelection();
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0) return;

    if (command === "formatBlock" && value === "blockquote") {
      this.quoteSelection();
      this.changed();
      this.updateInlineToolbarState();
      return;
    }

    if (command === "removeFormat") {
      this.clearFormattingPreserveTypography();
      this.changed();
      this.updateInlineToolbarState();
      return;
    }

    if (command === "justifyLeft" || command === "justifyCenter" || command === "justifyRight") {
      var editable = this.getSelectionEditable();
      if (!editable) return;
      var align = command === "justifyCenter" ? "center" : command === "justifyRight" ? "right" : "left";
      editable.style.textAlign = align;
      this.changed(true);
      this.updateInlineToolbarState();
      return;
    }

    if (command === "createLink") {
      if (this.removeLinksInSelection()) {
        this.changed();
        this.updateInlineToolbarState();
        return;
      }
      this.openLinkModal();
      return;
    }

    if (command === "fontFamily") {
      if (!value) return;
      this.wrapSelectionWithSpan({ fontFamily: value });
      this.changed();
      this.updateInlineToolbarState();
      return;
    }

    if (command === "fontSize") {
      if (!value) return;
      var sizeEditable = this.getSelectionEditable();
      if (sizeEditable && sizeEditable.classList && sizeEditable.classList.contains("rbe-heading")) return;
      this.applyFontSizeToSelection(value);
      this.changed();
      this.updateInlineToolbarState();
      return;
    }

    if (command === "foreColorReset" || command === "hiliteColorReset") {
      this.resetSelectionInlineStyle(command === "foreColorReset" ? "color" : "highlight");
      this.changed();
      this.updateInlineToolbarState();
      return;
    }

    if (command === "foreColor" || command === "hiliteColor") {
      var safeColor = cleanColorValue(value);
      if (!safeColor) return;
      this.wrapSelectionWithSpan(command === "foreColor" ? { color: safeColor } : { backgroundColor: safeColor });
      this.changed();
      this.updateInlineToolbarState();
      return;
    }

    if (command === "insertComment") {
      this.openCommentModal();
      return;
    }

    if (command === "removeComment") {
      this.removeSelectedComment();
      this.changed();
      this.updateInlineToolbarState();
      return;
    }

    document.execCommand(command, false, value || null);
    this.changed();
    this.updateInlineToolbarState();
  };

  BlockWriteAI.prototype.quoteSelection = function () {
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
    var node = selection.anchorNode && selection.anchorNode.nodeType === Node.TEXT_NODE ? selection.anchorNode.parentElement : selection.anchorNode;
    var existing = node && node.closest ? node.closest(".rbe-inline-quote") : null;
    if (existing && this.root.contains(existing)) {
      this.unwrapInlineQuote(existing);
      return;
    }
    var range = selection.getRangeAt(0);
    var span = document.createElement("span");
    span.className = "rbe-inline-quote";
    span.style.fontStyle = "italic";
    span.appendChild(document.createTextNode('"'));
    span.appendChild(range.extractContents());
    span.appendChild(document.createTextNode('"'));
    range.insertNode(span);
    selection.removeAllRanges();
    var next = document.createRange();
    next.selectNodeContents(span);
    selection.addRange(next);
    this.savedRange = next.cloneRange();
  };

  BlockWriteAI.prototype.openCommentModal = function () {
    if (!this.commentModal) return;
    this.restoreSelection();
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
    this.savedRange = selection.getRangeAt(0).cloneRange();
    this.commentModal.hidden = false;
    this.commentModal.classList.add("show");
    var input = this.commentModal.querySelector(".rbe-comment-input");
    if (input) {
      input.value = "";
      setTimeout(function () {
        input.focus();
      }, 0);
    }
  };

  BlockWriteAI.prototype.hideCommentModal = function () {
    if (!this.commentModal) return;
    this.commentModal.hidden = true;
    this.commentModal.classList.remove("show");
  };

  BlockWriteAI.prototype.openLinkModal = function () {
    if (!this.linkModal) return;
    this.restoreSelection();
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
    this.savedRange = selection.getRangeAt(0).cloneRange();
    this.linkModal.hidden = false;
    this.linkModal.classList.add("show");
    var input = this.linkModal.querySelector(".rbe-link-input");
    if (input) {
      input.value = "";
      input.classList.remove("is-invalid");
      setTimeout(function () {
        input.focus();
      }, 0);
    }
  };

  BlockWriteAI.prototype.hideLinkModal = function () {
    if (!this.linkModal) return;
    this.linkModal.hidden = true;
    this.linkModal.classList.remove("show");
  };

  BlockWriteAI.prototype.buildCommentPopoverHTML = function (mark) {
    if (!mark) return "";
    var comment = mark.getAttribute("data-rbe-comment") || mark.getAttribute("title") || "";
    var status = normalizeCommentStatus(mark.getAttribute("data-rbe-comment-status"));
    var reason = mark.getAttribute("data-rbe-comment-reason") || "";
    var createdAt = mark.getAttribute("data-rbe-comment-date") || "";
    var updatedAt = mark.getAttribute("data-rbe-comment-updated") || "";
    var replyText = mark.getAttribute("data-rbe-comment-reply") || "";
    var replyDate = mark.getAttribute("data-rbe-comment-reply-date") || "";
    var threadRaw = mark.getAttribute("data-rbe-comment-thread") || "";
    var fallbackMember = this.getLicenseMemberInfo();
    var memberName = mark.getAttribute("data-rbe-comment-member-name") || fallbackMember.name || "";
    var memberRole = mark.getAttribute("data-rbe-comment-member-role") || fallbackMember.role || "";
    var updatedMemberName = mark.getAttribute("data-rbe-comment-updated-member-name") || memberName;
    var updatedMemberRole = mark.getAttribute("data-rbe-comment-updated-member-role") || memberRole;
    return buildAnnotationCardHTML(comment, status, reason, { interactive: false, canReply: status === "declined", createdAt: createdAt, updatedAt: updatedAt, replyText: replyText, replyDate: replyDate, threadRaw: threadRaw, memberName: memberName, memberRole: memberRole, updatedMemberName: updatedMemberName, updatedMemberRole: updatedMemberRole });
  };

  BlockWriteAI.prototype.showCommentPopover = function (mark) {
    if (!this.commentPopover || !mark || !document.body.contains(mark)) return;
    mark.removeAttribute("title");
    this.activeCommentMark = mark;
    this.commentPopover.innerHTML = this.buildCommentPopoverHTML(mark);
    this.commentPopover.hidden = false;
    this.commentPopover.classList.add("is-visible");
    var rect = mark.getBoundingClientRect();
    var popoverRect = this.commentPopover.getBoundingClientRect();
    var left = rect.left + rect.width / 2 - popoverRect.width / 2;
    left = Math.max(12, Math.min(left, window.innerWidth - popoverRect.width - 12));
    var top = rect.top - popoverRect.height - 14;
    if (top < 12) top = rect.bottom + 14;
    this.commentPopover.style.left = Math.round(left) + "px";
    this.commentPopover.style.top = Math.round(top) + "px";
  };

  BlockWriteAI.prototype.hideCommentPopover = function () {
    if (!this.commentPopover) return;
    this.activeCommentMark = null;
    this.commentPopover.hidden = true;
    this.commentPopover.classList.remove("is-visible");
    this.commentPopover.innerHTML = "";
  };

  BlockWriteAI.prototype.handleCommentMouseOver = function (event) {
    var mark = event.target.closest && event.target.closest(".rbe-comment-mark");
    if (!mark || !this.root.contains(mark)) return;
    if (this.pinnedCommentMark && this.pinnedCommentMark !== mark) this.pinnedCommentMark = null;
    this.showCommentPopover(mark);
  };

  BlockWriteAI.prototype.handleCommentClick = function (event) {
    var mark = event.target.closest && event.target.closest(".rbe-comment-mark");
    if (!mark || !this.root.contains(mark)) return;
    this.pinnedCommentMark = mark;
    this.showCommentPopover(mark);
  };

  BlockWriteAI.prototype.handleCommentMouseOut = function (event) {
    var mark = event.target.closest && event.target.closest(".rbe-comment-mark");
    if (!mark) return;
    if (event.relatedTarget && mark.contains(event.relatedTarget)) return;
    if (this.commentPopover && event.relatedTarget && this.commentPopover.contains(event.relatedTarget)) return;
    if (this.pinnedCommentMark === mark) return;
    if (this.activeCommentMark === mark) this.hideCommentPopover();
  };

  BlockWriteAI.prototype.handleCommentFocusIn = function (event) {
    var mark = event.target.closest && event.target.closest(".rbe-comment-mark");
    if (!mark || !this.root.contains(mark)) return;
    this.showCommentPopover(mark);
  };

  BlockWriteAI.prototype.handleCommentFocusOut = function (event) {
    var mark = event.target.closest && event.target.closest(".rbe-comment-mark");
    if (!mark) return;
    if (event.relatedTarget && mark.contains(event.relatedTarget)) return;
    if (this.pinnedCommentMark === mark) return;
    if (this.activeCommentMark === mark) this.hideCommentPopover();
  };

  BlockWriteAI.prototype.handleCommentPopoverClick = function (event) {
    if (!this.commentPopover || !this.activeCommentMark) return;
    var toggle = event.target.closest(".rbe-output-annotation-reply-toggle");
    if (toggle) {
      event.preventDefault();
      var form = this.commentPopover.querySelector(".rbe-output-annotation-reply-form");
      if (form) {
        form.hidden = !form.hidden;
        if (!form.hidden) {
          var input = form.querySelector(".rbe-output-annotation-reply-input");
          if (input) input.focus();
        }
      }
      return;
    }
    var save = event.target.closest(".rbe-output-annotation-reply-save");
    if (save) {
      event.preventDefault();
      var input = this.commentPopover.querySelector(".rbe-output-annotation-reply-input");
      this.updateCommentReply(this.activeCommentMark.getAttribute("data-rbe-comment-id"), input ? input.value : "");
      return;
    }
    var cancel = event.target.closest(".rbe-output-annotation-reply-cancel");
    if (cancel) {
      event.preventDefault();
      var form = this.commentPopover.querySelector(".rbe-output-annotation-reply-form");
      var input = this.commentPopover.querySelector(".rbe-output-annotation-reply-input");
      if (input) input.value = "";
      if (form) form.hidden = true;
    }
  };

  BlockWriteAI.prototype.updateCommentState = function (commentId, status, reason, member) {
    if (!commentId || !this.root) return;
    status = normalizeCommentStatus(status);
    reason = String(reason || "");
    var updatedAt = new Date().toISOString();
    member = normalizeLicenseMember(member && (member.name || member.role) ? member : this.getLicenseMemberInfo());
    Array.prototype.slice.call(this.root.querySelectorAll(".rbe-comment-mark")).filter(function (mark) {
      return mark.getAttribute("data-rbe-comment-id") === commentId;
    }).forEach(function (mark) {
      mark.removeAttribute("title");
      mark.setAttribute("data-rbe-comment-status", status);
      mark.setAttribute("data-rbe-comment-updated", updatedAt);
      mark.setAttribute("data-rbe-comment-updated-member-name", member.name || "");
      mark.setAttribute("data-rbe-comment-updated-member-role", member.role || "");
      if (reason) mark.setAttribute("data-rbe-comment-reason", reason);
      else mark.removeAttribute("data-rbe-comment-reason");
    });
    if (this.activeCommentMark && this.activeCommentMark.getAttribute("data-rbe-comment-id") === commentId) {
      this.showCommentPopover(this.activeCommentMark);
    }
    this.persistCommentChange(status === "declined" ? "reject" : status === "resolved" ? "accept" : "state", commentId, { reason: reason, status: status });
    this.changed(true);
  };

  BlockWriteAI.prototype.updateCommentReply = function (commentId, replyText, member) {
    if (!commentId || !this.root) return;
    replyText = String(replyText || "").trim();
    if (!replyText) return;
    var replyDate = new Date().toISOString();
    member = normalizeLicenseMember(member && (member.name || member.role) ? member : this.getLicenseMemberInfo());
    Array.prototype.slice.call(this.root.querySelectorAll(".rbe-comment-mark")).filter(function (mark) {
      return mark.getAttribute("data-rbe-comment-id") === commentId;
    }).forEach(function (mark) {
      var thread = parseCommentThread(mark.getAttribute("data-rbe-comment-thread") || "", mark.getAttribute("data-rbe-comment-reply") || "", mark.getAttribute("data-rbe-comment-reply-date") || "");
      thread.push({ text: replyText, date: replyDate, memberName: member.name || "", memberRole: member.role || "" });
      mark.setAttribute("data-rbe-comment-thread", serializeCommentThread(thread));
      mark.setAttribute("data-rbe-comment-reply", replyText);
      mark.setAttribute("data-rbe-comment-reply-date", replyDate);
    });
    if (this.activeCommentMark && this.activeCommentMark.getAttribute("data-rbe-comment-id") === commentId) {
      this.showCommentPopover(this.activeCommentMark);
    }
    this.persistCommentChange("reply", commentId, { replyText: replyText, replyDate: replyDate, memberName: member.name || "", memberRole: member.role || "" });
    this.changed(true);
  };

  BlockWriteAI.prototype.saveLinkFromModal = function () {
    var input = this.linkModal && this.linkModal.querySelector(".rbe-link-input");
    var url = input ? input.value.trim() : "";
    if (!url || !isSafeUrl(url)) {
      if (input) input.classList.add("is-invalid");
      return;
    }
    this.applyLinkToSelection(url);
    this.hideLinkModal();
    this.changed();
    this.updateInlineToolbarState();
  };

  BlockWriteAI.prototype.applyLinkToSelection = function (url) {
    this.restoreSelection();
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
    var node = selection.anchorNode && selection.anchorNode.nodeType === Node.TEXT_NODE ? selection.anchorNode.parentElement : selection.anchorNode;
    var existing = node && node.closest ? node.closest("a") : null;
    if (existing && this.root.contains(existing)) {
      existing.href = url;
      existing.target = "_blank";
      existing.rel = "noopener noreferrer";
      this.savedRange = selection.getRangeAt(0).cloneRange();
      return;
    }
    var range = selection.getRangeAt(0);
    var link = document.createElement("a");
    link.href = url;
    link.target = "_blank";
    link.rel = "noopener noreferrer";
    link.appendChild(range.extractContents());
    range.insertNode(link);
    selection.removeAllRanges();
    var next = document.createRange();
    next.selectNodeContents(link);
    selection.addRange(next);
    this.savedRange = next.cloneRange();
  };

  BlockWriteAI.prototype.unwrapInlineLink = function (link) {
    if (!link || !link.parentNode) return;
    while (link.firstChild) link.parentNode.insertBefore(link.firstChild, link);
    link.remove();
  };

  BlockWriteAI.prototype.removeLinksInSelection = function () {
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0) return false;
    var range = selection.getRangeAt(0);
    var anchor = selection.anchorNode && selection.anchorNode.nodeType === Node.TEXT_NODE ? selection.anchorNode.parentElement : selection.anchorNode;
    var links = [];
    var direct = anchor && anchor.closest ? anchor.closest("a") : null;
    if (direct && this.root.contains(direct)) links.push(direct);
    Array.prototype.slice.call(this.root.querySelectorAll("a")).forEach(function (link) {
      try {
        if (range.intersectsNode(link) && links.indexOf(link) < 0) links.push(link);
      } catch (error) {
        return;
      }
    });
    links.forEach(this.unwrapInlineLink.bind(this));
    return links.length > 0;
  };

  BlockWriteAI.prototype.saveCommentFromModal = function () {
    var input = this.commentModal && this.commentModal.querySelector(".rbe-comment-input");
    var comment = input ? input.value.trim() : "";
    if (!comment) return;
    this.restoreSelection();
    var member = this.getLicenseMemberInfo();
    this.wrapSelectionWithSpan(null, {
      class: "rbe-comment-mark",
      "data-rbe-comment": comment,
      "data-rbe-comment-id": uid("comment"),
      "data-rbe-comment-status": "pending",
      "data-rbe-comment-reason": "",
      "data-rbe-comment-date": new Date().toISOString(),
      "data-rbe-comment-updated": "",
      "data-rbe-comment-reply": "",
      "data-rbe-comment-reply-date": "",
      "data-rbe-comment-thread": "[]",
      "data-rbe-comment-member-name": member.name || "",
      "data-rbe-comment-member-role": member.role || "",
      "data-rbe-comment-updated-member-name": "",
      "data-rbe-comment-updated-member-role": ""
    });
    this.hideCommentModal();
    this.changed();
    this.updateInlineToolbarState();
  };

  BlockWriteAI.prototype.removeSelectedComment = function () {
    this.restoreSelection();
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0) return;
    var anchor = selection.anchorNode && selection.anchorNode.nodeType === Node.TEXT_NODE ? selection.anchorNode.parentElement : selection.anchorNode;
    var comment = anchor && anchor.closest ? anchor.closest(".rbe-comment-mark") : null;
    if (!comment || !this.root.contains(comment)) {
      var range = selection.getRangeAt(0);
      var marks = Array.prototype.slice.call(this.root.querySelectorAll(".rbe-comment-mark")).filter(function (mark) {
        return range.intersectsNode(mark);
      });
      comment = marks[0];
    }
    if (!comment) return;
    while (comment.firstChild) comment.parentNode.insertBefore(comment.firstChild, comment);
    comment.remove();
  };

  BlockWriteAI.prototype.clearFormattingPreserveTypography = function () {
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
    var editable = this.getSelectionEditable();
    var anchor = selection.anchorNode && selection.anchorNode.nodeType === Node.TEXT_NODE ? selection.anchorNode.parentElement : selection.anchorNode;
    var computedTarget = anchor && anchor.nodeType === Node.ELEMENT_NODE ? anchor : editable;
    var computed = computedTarget ? window.getComputedStyle(computedTarget) : null;
    var fontFamily = computed && computed.fontFamily ? computed.fontFamily : "Arial, sans-serif";
    var fontSize = computed && computed.fontSize ? computed.fontSize : "16px";
    this.removeInlineQuotesInSelection();
    this.removeLinksInSelection();
    document.execCommand("removeFormat", false, null);
    this.wrapSelectionWithSpan({ fontFamily: fontFamily, fontSize: fontSize });
  };

  BlockWriteAI.prototype.unwrapInlineQuote = function (quote) {
    if (!quote || !quote.parentNode) return;
    function firstTextNode(node) {
      if (!node) return null;
      if (node.nodeType === Node.TEXT_NODE) return node;
      for (var child = node.firstChild; child; child = child.nextSibling) {
        var found = firstTextNode(child);
        if (found) return found;
      }
      return null;
    }
    function lastTextNode(node) {
      if (!node) return null;
      if (node.nodeType === Node.TEXT_NODE) return node;
      for (var child = node.lastChild; child; child = child.previousSibling) {
        var found = lastTextNode(child);
        if (found) return found;
      }
      return null;
    }
    var first = firstTextNode(quote);
    var last = lastTextNode(quote);
    if (first) first.textContent = first.textContent.replace(/^"+/, "");
    if (last) last.textContent = last.textContent.replace(/"+$/, "");
    while (quote.firstChild) quote.parentNode.insertBefore(quote.firstChild, quote);
    quote.remove();
  };

  BlockWriteAI.prototype.removeInlineQuotesInSelection = function () {
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0) return;
    var range = selection.getRangeAt(0);
    var anchor = selection.anchorNode && selection.anchorNode.nodeType === Node.TEXT_NODE ? selection.anchorNode.parentElement : selection.anchorNode;
    var quotes = [];
    var direct = anchor && anchor.closest ? anchor.closest(".rbe-inline-quote") : null;
    if (direct && this.root.contains(direct)) quotes.push(direct);
    Array.prototype.slice.call(this.root.querySelectorAll(".rbe-inline-quote")).forEach(function (quote) {
      try {
        if (range.intersectsNode(quote) && quotes.indexOf(quote) < 0) quotes.push(quote);
      } catch (error) {
        return;
      }
    });
    quotes.forEach(this.unwrapInlineQuote.bind(this));
  };

  BlockWriteAI.prototype.resetSelectionInlineStyle = function (kind) {
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
    var editable = this.getSelectionEditable();
    if (!editable) return;
    var range = selection.getRangeAt(0);
    var props = kind === "color" ? ["color"] : ["backgroundColor", "background"];

    function intersects(node) {
      try {
        return range.intersectsNode(node);
      } catch (error) {
        return false;
      }
    }

    function cleanElement(element) {
      if (!element || !element.style) return;
      props.forEach(function (prop) {
        element.style[prop] = "";
      });
      if (!element.getAttribute("style")) element.removeAttribute("style");
      if (kind === "color" && element.tagName && element.tagName.toLowerCase() === "font") element.removeAttribute("color");
    }

    Array.prototype.slice.call(editable.querySelectorAll("[style], font")).forEach(function (element) {
      if (intersects(element)) cleanElement(element);
    });

    var anchor = selection.anchorNode && selection.anchorNode.nodeType === Node.TEXT_NODE ? selection.anchorNode.parentElement : selection.anchorNode;
    while (anchor && anchor !== editable.parentElement) {
      if (anchor.nodeType === Node.ELEMENT_NODE && editable.contains(anchor)) cleanElement(anchor);
      if (anchor === editable) break;
      anchor = anchor.parentElement;
    }
    var focus = selection.focusNode && selection.focusNode.nodeType === Node.TEXT_NODE ? selection.focusNode.parentElement : selection.focusNode;
    while (focus && focus !== editable.parentElement) {
      if (focus.nodeType === Node.ELEMENT_NODE && editable.contains(focus)) cleanElement(focus);
      if (focus === editable) break;
      focus = focus.parentElement;
    }
    this.savedRange = selection.getRangeAt(0).cloneRange();
  };

  BlockWriteAI.prototype.applyFontSizeToSelection = function (value) {
    var size = Number(value);
    if (!size || size < 1) return;
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
    var editable = this.getSelectionEditable();
    if (!editable) return;
    var range = selection.getRangeAt(0);
    var touched = [];

    function remember(element) {
      if (element && element.nodeType === Node.ELEMENT_NODE && element.style && element.style.fontSize && touched.indexOf(element) < 0) {
        touched.push(element);
      }
    }

    function intersects(node) {
      try {
        return range.intersectsNode(node);
      } catch (error) {
        return false;
      }
    }

    Array.prototype.slice.call(editable.querySelectorAll("[style]")).forEach(function (element) {
      if (intersects(element)) remember(element);
    });

    var anchor = selection.anchorNode && selection.anchorNode.nodeType === Node.TEXT_NODE ? selection.anchorNode.parentElement : selection.anchorNode;
    while (anchor && anchor !== editable.parentElement) {
      if (editable.contains(anchor)) remember(anchor);
      if (anchor === editable) break;
      anchor = anchor.parentElement;
    }

    var focus = selection.focusNode && selection.focusNode.nodeType === Node.TEXT_NODE ? selection.focusNode.parentElement : selection.focusNode;
    while (focus && focus !== editable.parentElement) {
      if (editable.contains(focus)) remember(focus);
      if (focus === editable) break;
      focus = focus.parentElement;
    }

    touched.forEach(function (element) {
      element.style.fontSize = "";
      if (!element.getAttribute("style")) element.removeAttribute("style");
    });

    if (size === 16) {
      this.savedRange = selection.getRangeAt(0).cloneRange();
      return;
    }

    this.wrapSelectionWithSpan({ fontSize: size + "px" });
  };

  BlockWriteAI.prototype.restoreSelection = function () {
    if (!this.savedRange || !window.getSelection) return;
    var selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(this.savedRange);
  };

  BlockWriteAI.prototype.wrapSelectionWithSpan = function (styles, attrs) {
    var selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
    var range = selection.getRangeAt(0);
    var span = document.createElement("span");
    Object.keys(styles || {}).forEach(function (key) {
      span.style[key] = styles[key];
    });
    Object.keys(attrs || {}).forEach(function (key) {
      span.setAttribute(key, attrs[key]);
    });
    span.appendChild(range.extractContents());
    range.insertNode(span);
    selection.removeAllRanges();
    var next = document.createRange();
    next.selectNodeContents(span);
    selection.addRange(next);
    this.savedRange = next.cloneRange();
  };

  BlockWriteAI.prototype.getSelectionEditable = function () {
    var selection = window.getSelection();
    if (!selection || !selection.anchorNode || !this.root.contains(selection.anchorNode)) return null;
    var node = selection.anchorNode.nodeType === Node.TEXT_NODE ? selection.anchorNode.parentElement : selection.anchorNode;
    return node && node.closest ? node.closest(".rbe-editable, .rbe-table td, .rbe-table th") : null;
  };

  BlockWriteAI.prototype.shouldCreateNewBlock = function (target, block) {
    if (!target.isContentEditable) return false;
    if (block.dataset.type === "paragraph" || block.dataset.type === "heading" || block.dataset.type === "quote" || block.dataset.type === "callout") {
      return true;
    }
    return false;
  };

  BlockWriteAI.prototype.isEmptyEditable = function (target) {
    if (!target || !target.isContentEditable) return false;
    return (target.textContent || "").trim() === "";
  };

  BlockWriteAI.prototype.shouldKeepEmptyEditableBlock = function (block, target) {
    if (!block || !target) return false;
    if (block.dataset.type === "quote" && target.closest(".rbe-quote-text")) return true;
    if (block.dataset.type === "callout" && target.closest(".rbe-callout .rbe-editable")) return true;
    return false;
  };

  BlockWriteAI.prototype.defaultBlockData = function (type) {
    if (type === "heading") return { text: "", level: 2 };
    if (type === "quote") return { text: "", caption: "" };
    if (type === "list") return { style: "unordered", items: [""] };
    if (type === "checklist") return { items: [{ text: "", checked: false }] };
    if (type === "code") return { code: "", language: "plaintext", headerColor: "#e5e7eb", backgroundColor: "#d7dee8" };
    if (type === "image") return { url: "", caption: "" };
    if (type === "table") return { rows: [["", ""], ["", ""]], withHeadings: false };
    if (type === "embed") return { source: "", embed: "", caption: "" };
    if (type === "callout") return { text: "", tone: "info" };
    if (type === "delimiter") return { style: "line" };
    if (type === "button") return { text: "Button", url: "", background: "#2563eb", color: "#ffffff", alignment: "left" };
    if (type === "toggle") return { title: "", blocks: [{ type: "paragraph", data: { text: "" } }], open: true };
    if (type === "signatureRow") return { blocks: [{ type: "paragraph", data: { text: "" } }] };
    if (type === "linkPreview") return { url: "", title: "", description: "" };
    if (type === "raw") return { html: "" };
    if (type === "attaches") return { name: "", url: "", size: 0 };
    if (PLUGIN_TOOLS[type] && typeof PLUGIN_TOOLS[type].defaultData === "function") return PLUGIN_TOOLS[type].defaultData.call(this, pluginHelpers());
    if (PLUGIN_TOOLS[type] && PLUGIN_TOOLS[type].defaultData) return Object.assign({}, PLUGIN_TOOLS[type].defaultData);
    return { text: "" };
  };

  BlockWriteAI.prototype.dataFromBlockForType = function (block, type) {
    var text = "";
    var editable = block.querySelector(".rbe-editable");
    if (editable) text = editable.innerHTML;
    if (type === "heading") return { text: text, level: 2 };
    if (type === "quote") return { text: text, caption: "" };
    if (type === "list") return { style: "unordered", items: [text || ""] };
    if (type === "checklist") return { items: [{ text: text || "", checked: false }] };
    if (type === "code") return { code: (editable ? editable.textContent : block.textContent || "").trim(), language: "plaintext", headerColor: "#e5e7eb", backgroundColor: "#d7dee8" };
    if (type === "callout") return { text: text, tone: "info" };
    if (type === "button") return { text: text || "Button", url: "", background: "#2563eb", color: "#ffffff", alignment: "left" };
    if (type === "toggle") return { title: text || "", blocks: [{ type: "paragraph", data: { text: "" } }], open: true };
    if (type === "linkPreview") return { url: "", title: text || "", description: "" };
    if (PLUGIN_TOOLS[type] && typeof PLUGIN_TOOLS[type].fromBlock === "function") return PLUGIN_TOOLS[type].fromBlock.call(this, block, text, pluginHelpers());
    return Object.assign(this.defaultBlockData(type), { text: text });
  };

  BlockWriteAI.prototype.insertBlock = function (type, data, index, id) {
    if (!this.silent && !this.renderingData && !this.readOnly && !this.canUseTool(type)) {
      this.showPremiumLocked(this.getPremiumFeatureForTool(type));
      return null;
    }
    if (this.availableTools.indexOf(type) < 0 && BLOCK_TYPES.indexOf(type) >= 0) {
      this.availableTools.push(type);
    }
    var block = this.createBlock(type, data || this.defaultBlockData(type), id);
    var children = Array.prototype.slice.call(this.canvas.children);
    if (typeof index !== "number" || index < 0 || index >= children.length) this.canvas.appendChild(block);
    else this.canvas.insertBefore(block, children[index]);
    this.scheduleSelectEnhance();
    return block;
  };

  BlockWriteAI.prototype.transformBlock = function (block, type, data) {
    if (!this.silent && !this.readOnly && !this.canUseTool(type)) {
      this.showPremiumLocked(this.getPremiumFeatureForTool(type));
      return block;
    }
    var id = block.dataset.id;
    block.dataset.type = type;
    block.__rbeBlockData = type === "attaches" ? cloneJSON(data || this.defaultBlockData(type)) : null;
    var body = block.querySelector(".rbe-block-body");
    this.renderBlockBody(body, type, data || this.defaultBlockData(type));
    this.scheduleSelectEnhance();
    return block;
  };

  BlockWriteAI.prototype.refreshButtonPreview = function (block) {
    if (!block) return;
    var textInput = block.querySelector(".rbe-button-text");
    var urlInput = block.querySelector(".rbe-button-url");
    var background = (block.querySelector(".rbe-button-bg") || {}).value || "#2563eb";
    var color = (block.querySelector(".rbe-button-color") || {}).value || "#ffffff";
    var alignment = cleanAlignment((block.querySelector(".rbe-button-align") || {}).value || "left");
    var wrap = block.querySelector(".rbe-button-block");
    var preview = block.querySelector(".rbe-button-preview");
    var link = block.querySelector(".rbe-button-link");
    if (wrap) {
      wrap.dataset.align = alignment;
    }
    if (preview) preview.style.textAlign = alignment;
    if (link) {
      link.className = "rbe-button-link";
      link.style.backgroundColor = background;
      link.style.color = color;
      link.textContent = (textInput && textInput.value) || "Button";
      link.href = isSafeUrl((urlInput && urlInput.value) || "") ? urlInput.value : "#";
    }
  };

  BlockWriteAI.prototype.refreshLinkPreview = function (block) {
    if (!block) return;
    var url = (block.querySelector(".rbe-link-preview-url") || {}).value || "";
    var title = (block.querySelector(".rbe-link-preview-title") || {}).value || "";
    var description = (block.querySelector(".rbe-link-preview-description") || {}).value || "";
    var card = block.querySelector(".rbe-link-preview-card");
    var cardTitle = block.querySelector(".rbe-link-preview-card-title");
    var cardDescription = block.querySelector(".rbe-link-preview-card-description");
    if (card) card.setAttribute("href", isSafeUrl(url) ? url : "#");
    if (cardTitle) cardTitle.textContent = title || url || "Link preview";
    if (cardDescription) cardDescription.textContent = description || "Add a URL, title, and description for this link.";
  };

  BlockWriteAI.prototype.indexOfBlock = function (block) {
    return Array.prototype.slice.call(this.canvas.children).indexOf(block);
  };

  BlockWriteAI.prototype.focusBlock = function (block) {
    if (!block) return;
    var target = block.querySelector("[contenteditable='true'], textarea, input:not([type='hidden'])");
    if (!target) return;
    setTimeout(function () {
      placeCaretAtEnd(target);
    }, 0);
  };

  BlockWriteAI.prototype.getCurrentBlock = function () {
    var selection = window.getSelection();
    if (selection && selection.anchorNode && this.root.contains(selection.anchorNode)) {
      return closestBlock(selection.anchorNode);
    }
    if (this.root.contains(document.activeElement)) return closestBlock(document.activeElement);
    return this.canvas.lastElementChild;
  };

  BlockWriteAI.prototype.getDragAfterElement = function (y, container) {
    container = container || this.canvas;
    var blocks = Array.prototype.slice.call(container ? container.children : []).filter(function (child) {
      return child.classList && child.classList.contains("rbe-block") && !child.classList.contains("rbe-is-dragging");
    });
    return blocks.reduce(
      function (closest, child) {
        var box = child.getBoundingClientRect();
        var threshold = Math.min(box.height / 2, 48);
        var offset = y - box.top - threshold;
        if (offset < 0 && offset > closest.offset) {
          return { offset: offset, element: child };
        }
        return closest;
      },
      { offset: Number.NEGATIVE_INFINITY, element: null }
    ).element;
  };

  BlockWriteAI.prototype.switchListStyle = function (block, style) {
    var wrap = block.querySelector(".rbe-list-wrap");
    if (wrap) wrap.dataset.style = style;
    Array.prototype.slice.call(block.querySelectorAll(".rbe-list-item")).forEach(function (item) {
      item.dataset.style = "";
    });
    this.updateListMarkers(wrap);
  };

  BlockWriteAI.prototype.copyCode = function (wrap) {
    if (!wrap || !navigator.clipboard) return;
    var textarea = wrap.querySelector(".rbe-code-input");
    navigator.clipboard.writeText(textarea ? textarea.value : "");
  };

  BlockWriteAI.prototype.copyRaw = function (block) {
    if (!block || !navigator.clipboard) return;
    var textarea = block.querySelector(".rbe-raw-input");
    navigator.clipboard.writeText(textarea ? textarea.value : "");
  };

  BlockWriteAI.prototype.formatCodeBlock = function (block) {
    if (!block || block.dataset.type !== "code") return;
    var textarea = block.querySelector(".rbe-code-input");
    var language = (block.querySelector(".rbe-code-language") || {}).value || "plaintext";
    if (!textarea) return;
    textarea.value = formatCodeByLanguage(textarea.value, language);
    this.refreshCodeBlock(block);
    this.changed(true);
  };

  BlockWriteAI.prototype.formatRawBlock = function (block) {
    if (!block || block.dataset.type !== "raw") return;
    var textarea = block.querySelector(".rbe-raw-input");
    if (!textarea) return;
    textarea.value = formatCodeByLanguage(textarea.value, "html");
    updateRawPreview(block.querySelector(".rbe-raw-preview"), textarea.value);
    this.changed(true);
  };

  BlockWriteAI.prototype.refreshCodeBlock = function (block) {
    if (!block) return;
    var wrap = block.querySelector(".rbe-code-wrap");
    var headerColor = normalizeHexColor((block.querySelector(".rbe-code-header-color") || {}).value, "#e5e7eb");
    var backgroundColor = normalizeHexColor((block.querySelector(".rbe-code-bg-color") || {}).value, "#d7dee8");
    applyCodeColorVars(wrap, { headerColor: headerColor, backgroundColor: backgroundColor });
  };

  BlockWriteAI.prototype.applyImageStageState = function (stage) {
    if (!stage) return;
    var image = stage.querySelector(".rbe-image-preview");
    if (image && image.naturalWidth) {
      stage.dataset.naturalWidth = String(image.naturalWidth);
      stage.dataset.naturalHeight = String(image.naturalHeight || "");
    }
    var displayWidth = Number(stage.dataset.displayWidth || 0);
    var minWidth = Number(stage.dataset.minWidth || 0);
    var naturalWidth = Number(stage.dataset.naturalWidth || 0);
    if (displayWidth > 0 && minWidth > 0 && displayWidth < minWidth) {
      displayWidth = minWidth;
      stage.dataset.displayWidth = String(Math.round(displayWidth));
    }
    if (displayWidth > 0 && naturalWidth > 0 && displayWidth > Math.max(minWidth || 0, naturalWidth)) {
      displayWidth = Math.max(minWidth || 0, naturalWidth);
      stage.dataset.displayWidth = String(Math.round(displayWidth));
    }
    var crop = stage.dataset.crop === "custom";
    var ratio = imageCropRatio(stage.dataset);
    var cropX = imageCropPercent(stage.dataset.cropX);
    var cropY = imageCropPercent(stage.dataset.cropY);
    var zoom = imageCropZoom(stage.dataset.cropZoom);
    if (displayWidth > 0) {
      stage.style.width = Math.round(displayWidth) + "px";
    } else {
      stage.style.removeProperty("width");
    }
    if (naturalWidth > 0) {
      stage.style.maxWidth = "min(100%, " + Math.round(naturalWidth) + "px)";
    } else {
      stage.style.maxWidth = "100%";
    }
    stage.classList.toggle("is-custom-crop", crop);
    if (crop) {
      if (!ratio) {
        var rect = stage.getBoundingClientRect();
        ratio = rect.width && rect.height ? rect.width / rect.height : 1;
        stage.dataset.cropRatio = ratio.toFixed(3);
      }
      stage.style.aspectRatio = ratio.toFixed(3) + " / 1";
    } else {
      stage.style.removeProperty("aspect-ratio");
    }
    if (!image) return;
    if (crop) {
      image.style.height = "100%";
      image.style.maxHeight = "none";
      image.style.objectFit = "cover";
      image.style.objectPosition = cropX + "% " + cropY + "%";
      image.style.transformOrigin = cropX + "% " + cropY + "%";
      image.style.setProperty("--rbe-image-zoom", zoom);
    } else {
      image.style.removeProperty("height");
      image.style.removeProperty("max-height");
      image.style.removeProperty("object-fit");
      image.style.removeProperty("object-position");
      image.style.removeProperty("transform-origin");
      image.style.removeProperty("--rbe-image-zoom");
    }
    image.style.transform = "rotate(" + (Number(stage.dataset.rotation || 0) || 0) + "deg) scale(var(--rbe-image-zoom, 1))";
  };

  BlockWriteAI.prototype.setImageCropMode = function (stage, enabled) {
    if (!stage || this.readOnly) return;
    if (enabled && stage.dataset.crop !== "custom") {
      var rect = stage.getBoundingClientRect();
      stage.dataset.crop = "custom";
      stage.dataset.cropRatio = (rect.width && rect.height ? rect.width / rect.height : 1).toFixed(3);
      stage.dataset.cropX = stage.dataset.cropX || "50";
      stage.dataset.cropY = stage.dataset.cropY || "50";
      stage.dataset.cropZoom = stage.dataset.cropZoom || "1";
    }
    stage.classList.toggle("is-image-crop-mode", !!enabled);
    var toggle = stage.querySelector(".rbe-image-crop-toggle");
    if (toggle) {
      toggle.classList.toggle("is-active", !!enabled);
      toggle.setAttribute("aria-pressed", enabled ? "true" : "false");
      toggle.title = enabled ? "Finish crop" : "Crop image";
      toggle.setAttribute("aria-label", enabled ? "Finish crop" : "Crop image");
    }
    this.applyImageStageState(stage);
  };

  BlockWriteAI.prototype.bindImageStageMouseControls = function (stage) {
    if (!stage || this.readOnly || stage.dataset.imageMouseReady === "true") return;
    stage.dataset.imageMouseReady = "true";
    var editor = this;
    Array.prototype.slice.call(stage.querySelectorAll(".rbe-image-resize-handle")).forEach(function (resizeHandle) {
      resizeHandle.addEventListener("pointerdown", function (event) {
        event.preventDefault();
        event.stopPropagation();
        var gallery = stage.closest(".rbe-image-gallery") || stage.parentElement || stage;
        var startRect = stage.getBoundingClientRect();
        var galleryRect = gallery.getBoundingClientRect();
        var minWidth = Number(stage.dataset.minWidth || 0);
        if (!minWidth) {
          minWidth = 80;
          stage.dataset.minWidth = String(minWidth);
        }
        var naturalWidth = Number(stage.dataset.naturalWidth || 0);
        var availableWidth = Math.round(Math.max.apply(Math, [
          galleryRect.width || 0,
          gallery.parentElement ? gallery.parentElement.getBoundingClientRect().width || 0 : 0,
          stage.closest(".rbe-block-body") ? stage.closest(".rbe-block-body").getBoundingClientRect().width || 0 : 0,
          stage.closest(".rbe-block") ? stage.closest(".rbe-block").getBoundingClientRect().width || 0 : 0,
          stage.closest(".rbe-canvas") ? stage.closest(".rbe-canvas").getBoundingClientRect().width || 0 : 0,
          editor.root ? editor.root.getBoundingClientRect().width || 0 : 0,
          startRect.width
        ]));
        var maxWidth = Math.max(minWidth, naturalWidth ? Math.min(naturalWidth, availableWidth) : availableWidth);
        var startX = event.clientX;
        var handleKind = resizeHandle.dataset.resizeHandle || "se";
        function move(moveEvent) {
          var deltaX = moveEvent.clientX - startX;
          if (handleKind.indexOf("w") >= 0) deltaX = -deltaX;
          var nextWidth = clampNumber(startRect.width + deltaX, minWidth, maxWidth, startRect.width);
          stage.dataset.displayWidth = String(Math.round(nextWidth));
          editor.applyImageStageState(stage);
          editor.updateMediaActionAvailability(gallery);
        }
        function up() {
          document.removeEventListener("pointermove", move);
          document.removeEventListener("pointerup", up);
          editor.changed(true);
        }
        document.addEventListener("pointermove", move);
        document.addEventListener("pointerup", up);
      });
    });

    Array.prototype.slice.call(stage.querySelectorAll(".rbe-image-crop-handle")).forEach(function (handle) {
      handle.addEventListener("pointerdown", function (event) {
        event.preventDefault();
        event.stopPropagation();
        editor.setImageCropMode(stage, true);
        var gallery = stage.closest(".rbe-image-gallery") || stage.parentElement || stage;
        var startRect = stage.getBoundingClientRect();
        var galleryRect = gallery.getBoundingClientRect();
        var minWidth = Number(stage.dataset.minWidth || 0);
        if (!minWidth) {
          minWidth = Math.max(80, Math.round(startRect.width));
          stage.dataset.minWidth = String(minWidth);
        }
        var naturalWidth = Number(stage.dataset.naturalWidth || 0);
        var availableWidth = Math.round(galleryRect.width || startRect.width);
        var maxWidth = Math.max(minWidth, naturalWidth ? Math.min(naturalWidth, availableWidth) : availableWidth);
        var startX = event.clientX;
        var startY = event.clientY;
        var startWidth = startRect.width;
        var startHeight = startRect.height || startRect.width;
        var handleKind = handle.dataset.cropHandle || "se";
        function move(moveEvent) {
          var nextWidth = startWidth;
          var nextHeight = startHeight;
          if (handleKind.indexOf("e") >= 0) {
            nextWidth = clampNumber(startWidth + moveEvent.clientX - startX, minWidth, maxWidth, startWidth);
            stage.dataset.displayWidth = String(Math.round(nextWidth));
          }
          if (handleKind.indexOf("s") >= 0) {
            nextHeight = clampNumber(startHeight + moveEvent.clientY - startY, 70, 900, startHeight);
          }
          stage.dataset.crop = "custom";
          stage.dataset.cropRatio = clampNumber(nextWidth / nextHeight, 0.35, 3.5, 1).toFixed(3);
          editor.applyImageStageState(stage);
          editor.updateMediaActionAvailability(gallery);
        }
        function up() {
          document.removeEventListener("pointermove", move);
          document.removeEventListener("pointerup", up);
          editor.changed(true);
        }
        document.addEventListener("pointermove", move);
        document.addEventListener("pointerup", up);
      });
    });

    stage.addEventListener("pointerdown", function (event) {
      if (!stage.classList.contains("is-image-crop-mode")) return;
      if (event.target.closest("button, .rbe-image-resize-handle, .rbe-image-crop-handle")) return;
      event.preventDefault();
      var startRect = stage.getBoundingClientRect();
      var startX = event.clientX;
      var startY = event.clientY;
      var startCropX = imageCropPercent(stage.dataset.cropX);
      var startCropY = imageCropPercent(stage.dataset.cropY);
      function move(moveEvent) {
        var deltaX = ((moveEvent.clientX - startX) / Math.max(1, startRect.width)) * 100;
        var deltaY = ((moveEvent.clientY - startY) / Math.max(1, startRect.height)) * 100;
        stage.dataset.cropX = String(Math.round(imageCropPercent(startCropX - deltaX)));
        stage.dataset.cropY = String(Math.round(imageCropPercent(startCropY - deltaY)));
        editor.applyImageStageState(stage);
      }
      function up() {
        document.removeEventListener("pointermove", move);
        document.removeEventListener("pointerup", up);
        editor.changed(true);
      }
      document.addEventListener("pointermove", move);
      document.addEventListener("pointerup", up);
    });

    stage.addEventListener("wheel", function (event) {
      if (!stage.classList.contains("is-image-crop-mode")) return;
      event.preventDefault();
      var zoom = imageCropZoom(stage.dataset.cropZoom);
      zoom = imageCropZoom(zoom + (event.deltaY < 0 ? 0.08 : -0.08));
      stage.dataset.cropZoom = zoom.toFixed(2);
      editor.applyImageStageState(stage);
      editor.changed(true);
    }, { passive: false });
  };

  BlockWriteAI.prototype.applyImageUrl = function (block) {
    var input = block && block.querySelector(".rbe-image-url");
    if (!input || !isSafeUrl(input.value)) return;
    var data = this.serializeBlock(block).data;
    data.url = input.value.trim();
    this.transformBlock(block, "image", data);
    this.changed();
  };

  BlockWriteAI.prototype.applyImageFile = function (block, file) {
    return this.applyImageFiles(block, file ? [file] : []);
  };

  BlockWriteAI.prototype.applyImageFiles = function (block, files) {
    if (!block || !files || !files.length) return;
    var imageFiles = files.filter(isImageFile);
    if (!imageFiles.length) {
      alert("The Image block only accepts image files.");
      return;
    }
    var self = this;
    Promise.all(imageFiles.map(function (file) {
      return self.uploadFile(file, "image").then(function (result) {
        return {
          url: result.url,
          base64: result.base64 || "",
          upload: cloneJSON(result.upload || null),
          alt: result.alt || file.name,
          caption: result.caption || file.name
        };
      });
    }))
      .then(function (items) {
        var existing = normalizeImageItems(self.serializeBlock(block).data);
        items = existing.concat(items);
        var data = {
          items: items,
          url: items[0] ? items[0].url : "",
          alt: items[0] ? items[0].alt : "",
          caption: items[0] ? items[0].caption : ""
        };
        self.transformBlock(block, "image", data);
        self.changed();
      })
      .catch(function () {
        alert("Image upload failed.");
      });
  };

  BlockWriteAI.prototype.applyAttachmentFile = function (block, file) {
    return this.applyAttachmentFiles(block, file ? [file] : []);
  };

  BlockWriteAI.prototype.applyAttachmentFiles = function (block, files) {
    if (!block || !files || !files.length) return;
    var allowed = files.filter(isAllowedAttachmentFile);
    if (!allowed.length || allowed.length !== files.length) {
      alert(attachmentRestrictionMessage());
      return;
    }
    var self = this;
    var baseItems = normalizeAttachmentItems(self.serializeBlock(block).data);
    var uploadedItems = [];
    var failedItems = [];
    function persistAttachmentItems(items) {
      var data = {
        items: items,
        name: items[0] ? items[0].name : "",
        url: items[0] ? items[0].url : "",
        size: items[0] ? items[0].size : 0,
        type: items[0] ? items[0].type : ""
      };
      self.transformBlock(block, "attaches", {
        items: data.items,
        name: data.name,
        url: data.url,
        size: data.size,
        type: data.type
      });
      block.__rbeBlockData = cloneJSON(data);
      var body = block.querySelector(".rbe-block-body");
      if (body) body.__rbeBlockData = cloneJSON(data);
      var wrap = block.querySelector(".rbe-attachment");
      if (wrap) {
        wrap.__rbeAttachmentItems = cloneJSON(items);
        var state = wrap.querySelector(".rbe-attachment-items-json");
        if (state) state.value = JSON.stringify(items);
      }
      self.changed(true);
      setTimeout(function () {
        if (!self.silent) self.emitChange();
      }, 0);
    }
    function uploadAttachmentWithRetry(file, index, attempt) {
      attempt = attempt || 1;
      self.setShellProgress(true, "Uploading attachment " + (index + 1) + " of " + allowed.length + ": " + file.name + (attempt > 1 ? " (retry " + attempt + ")" : ""));
      self.setShellStatus("Uploading " + file.name);
      return self.uploadFile(file, "file").catch(function (error) {
        if (attempt >= 3) throw error;
        self.setShellProgress(true, "Retrying " + file.name + "...");
        return wait(1000).then(function () {
          return uploadAttachmentWithRetry(file, index, attempt + 1);
        });
      });
    }
    function uploadNext(index) {
      if (index >= allowed.length) return Promise.resolve();
      var file = allowed[index];
      return uploadAttachmentWithRetry(file, index, 1).then(function (result) {
        var url = result.url || "";
        return {
          name: result.name || file.name,
          url: url,
          base64: url ? "" : result.base64 || "",
          upload: cloneJSON(result.upload || null),
          size: result.size || file.size,
          type: result.type || file.type
        };
      }).then(function (item) {
        uploadedItems.push(item);
        persistAttachmentItems(baseItems.concat(uploadedItems));
        self.setShellProgress(true, "Uploaded " + (index + 1) + " of " + allowed.length + ". Preparing next file...");
        return wait(index < allowed.length - 1 ? 1000 : 0).then(function () {
          return uploadNext(index + 1);
        });
      }).catch(function () {
        failedItems.push(file.name);
        self.setShellProgress(true, "Could not upload " + file.name + ". Continuing with the next file...");
        return wait(index < allowed.length - 1 ? 1000 : 0).then(function () {
          return uploadNext(index + 1);
        });
      });
    }
    self.setShellProgress(true, "Uploading " + allowed.length + " attachment" + (allowed.length === 1 ? "" : "s") + "...");
    uploadNext(0)
      .then(function () {
        self.setShellProgress(false, "");
        if (failedItems.length) {
          self.setShellStatus("Uploaded " + uploadedItems.length + " of " + allowed.length + " attachments");
          self.showToast("Uploaded " + uploadedItems.length + " of " + allowed.length + " attachments. Failed: " + failedItems.join(", "), "error");
        } else {
          self.setShellStatus("Uploaded " + uploadedItems.length + " attachment" + (uploadedItems.length === 1 ? "" : "s"));
          self.showToast("Uploaded " + uploadedItems.length + " attachment" + (uploadedItems.length === 1 ? "" : "s"), "success");
        }
      });
  };

  BlockWriteAI.prototype.removeMediaItem = function (button) {
    if (this.readOnly) return;
    var block = closestBlock(button);
    if (!block) return;
    var imageItem = button.closest(".rbe-image-stage");
    var attachmentItem = button.closest(".rbe-attachment-card");
    if (!imageItem && !attachmentItem) return;


    if (imageItem) {
      var imageGallery = imageItem.closest(".rbe-image-gallery");
      imageItem.remove();
      if (!block.querySelector(".rbe-image-stage")) {
        this.transformBlock(block, "image", {});
      } else {
        this.closeMediaMenus();
        this.updateMediaActionAvailability(imageGallery || block);
      }
      this.changed(true);
      return;
    }

    var attachmentGrid = attachmentItem.closest(".rbe-attachment-grid");
    attachmentItem.remove();
    if (!block.querySelector(".rbe-attachment-card")) {
      this.transformBlock(block, "attaches", {});
    } else {
      this.closeMediaMenus();
      this.updateMediaActionAvailability(attachmentGrid || block);
    }
    this.changed(true);
  };

  BlockWriteAI.prototype.alignMediaItem = function (button, alignment) {
    if (this.readOnly) return;
    var item = button.closest(".rbe-image-stage, .rbe-attachment-card");
    if (!item) return;
    alignment = cleanAlignment(alignment);
    item.dataset.alignment = alignment;
    item.classList.remove("rbe-media-align-left", "rbe-media-align-center", "rbe-media-align-right");
    item.classList.add("rbe-media-align-" + alignment);
    this.updateMediaActionAvailability(item.closest(".rbe-image-gallery, .rbe-attachment-grid") || item.parentElement || this.root);
    this.changed(true);
  };

  BlockWriteAI.prototype.updateMediaActionAvailability = function (root) {
    root = root || this.root;
    if (!root || !root.querySelectorAll) return;
    [".rbe-image-gallery", ".rbe-attachment-grid"].forEach(function (selector) {
      var galleries = root.matches && root.matches(selector) ? [root] : [];
      galleries = galleries.concat(Array.prototype.slice.call(root.querySelectorAll(selector)));
      galleries.forEach(function (gallery) {
        var items = Array.prototype.slice.call(gallery.querySelectorAll(".rbe-image-stage, .rbe-attachment-card"));
        items.forEach(function (item) {
          item.classList.remove("rbe-media-row-has-space", "rbe-media-row-full");
        });
        if (!items.length) return;
        var styles = window.getComputedStyle(gallery);
        var gap = parseFloat(styles.columnGap || styles.gap || "10") || 10;
        var templateColumns = (styles.gridTemplateColumns || "").split(" ").filter(function (part) {
          return !!part && part !== "none";
        });
        var firstItem = items[0];
        var itemWidth = firstItem ? firstItem.getBoundingClientRect().width || firstItem.offsetWidth || 1 : 1;
        var maxColumns = Math.max(1, templateColumns.length || Math.floor((gallery.clientWidth + gap) / (itemWidth + gap)));
        var rows = {};
        items.forEach(function (item) {
          var top = Math.round(item.offsetTop || 0);
          rows[top] = rows[top] || [];
          rows[top].push(item);
        });
        Object.keys(rows).forEach(function (key) {
          var allowAlign = rows[key].length < maxColumns;
          rows[key].forEach(function (item) {
            item.classList.toggle("rbe-media-row-has-space", allowAlign);
            item.classList.toggle("rbe-media-row-full", !allowAlign);
          });
        });
      });
    });
  };

  BlockWriteAI.prototype.uploadFile = function (file, kind) {
    var upload = this.options.upload || {};
    var handler = kind === "image" && typeof upload.image === "function" ? upload.image : kind !== "image" && typeof upload.file === "function" ? upload.file : null;
    var output = normalizeUploadOutput(this.options.uploadOutput || this.options["upload output"] || upload.output || upload.uploadOutput || upload["upload output"]);
    var wantsUpload = output.indexOf("upload") >= 0;
    var wantsBase64 = output.indexOf("base64") >= 0 || (!handler && !wantsUpload);
    var base64Promise = wantsBase64 ? readFileAsDataURL(file) : Promise.resolve("");
    var uploadPromise = wantsUpload && handler ? Promise.resolve(handler(file)) : Promise.resolve(null);

    return Promise.all([base64Promise, uploadPromise]).then(function (results) {
      var base64 = results[0];
      var uploaded = results[1] || null;
      if (uploaded && typeof uploaded.json === "function") {
        return uploaded.json().then(function (json) {
          return [base64, json];
        });
      }
      return [base64, uploaded];
    }).then(function (results) {
      var base64 = results[0];
      var uploaded = results[1] || null;
      if (uploaded && uploaded.data && !uploaded.url) uploaded = uploaded.data;
      if (uploaded && uploaded.file && !uploaded.url) uploaded = uploaded.file;
      if (uploaded && uploaded.attachment && !uploaded.url) uploaded = uploaded.attachment;
      if (uploaded && uploaded.result && !uploaded.url) uploaded = uploaded.result;
      var result = Object.assign({}, uploaded || {});
      if (base64) result.base64 = base64;
      if (uploaded && wantsUpload) result.upload = cloneJSON(uploaded);
      if (!result.url) result.url = base64 || (uploaded && uploaded.url) || "";
      if (!result.name) result.name = file.name;
      if (!result.size) result.size = file.size;
      if (!result.type) result.type = file.type;
      return result;
    });
  };

  BlockWriteAI.prototype.applyEmbed = function (block) {
    var input = block && block.querySelector(".rbe-embed-url");
    if (!input || !input.value.trim()) return;
    var data = toEmbed(input.value.trim());
    var caption = block.querySelector(".rbe-embed-caption");
    data.caption = caption ? caption.value : "";
    this.transformBlock(block, "embed", data);
    this.changed();
  };

  BlockWriteAI.prototype.applyListNestAction = function (block, action) {
    if (!block || block.dataset.type !== "list") return;
    var list = block.querySelector(".rbe-list");
    if (!list) return;
    var activeItem = this.getActiveListItem(block);
    if (!activeItem) return;

    if (action === "toggle-list-style") {
      var currentStyle = activeItem.dataset.style || (block.querySelector(".rbe-list-wrap") || {}).dataset.style || "unordered";
      activeItem.dataset.style = currentStyle === "ordered" ? "unordered" : "ordered";
      this.updateListMarkers(block.querySelector(".rbe-list-wrap"));
      placeCaretAtEnd(activeItem.querySelector(".rbe-list-text"));
      this.changed(true);
      return;
    }

    if (action !== "indent-list" && action !== "outdent-list") return;
    var level = Number(activeItem.dataset.level || 0);
    if (action === "indent-list") {
      if (!activeItem.previousElementSibling) return;
      activeItem.dataset.level = Math.min(4, level + 1);
    } else {
      activeItem.dataset.level = Math.max(0, level - 1);
    }
    this.updateListMarkers(block.querySelector(".rbe-list-wrap"));
    placeCaretAtEnd(activeItem.querySelector(".rbe-list-text"));
    this.changed(true);
  };

  BlockWriteAI.prototype.getActiveListItem = function (block) {
    var list = block && block.querySelector(".rbe-list");
    if (!list) return null;
    var selection = window.getSelection();
    var selectionNode = selection && selection.anchorNode ? selection.anchorNode : document.activeElement;
    var activeNode = selectionNode && selectionNode.nodeType === Node.TEXT_NODE ? selectionNode.parentElement : selectionNode;
    var activeItem = activeNode && activeNode.closest ? activeNode.closest(".rbe-list-item") : null;
    if (!activeItem || !list.contains(activeItem)) activeItem = list.querySelector(".rbe-list-item");
    return activeItem;
  };

  BlockWriteAI.prototype.addListItem = function (block) {
    if (!block || block.dataset.type !== "list") return;
    var list = block.querySelector(".rbe-list");
    if (!list) return;
    var active = this.getActiveListItem(block);
    var next = this.createListItem({ text: "", level: active ? Number(active.dataset.level || 0) : 0, style: active ? active.dataset.style || "" : "" });
    if (active && active.parentNode === list) active.insertAdjacentElement("afterend", next);
    else list.appendChild(next);
    this.updateListMarkers(block.querySelector(".rbe-list-wrap"));
    placeCaretAtEnd(next.querySelector(".rbe-list-text"));
    this.changed(true);
  };

  BlockWriteAI.prototype.removeListItem = function (block, item) {
    if (!block || block.dataset.type !== "list" || !item) return;
    var list = block.querySelector(".rbe-list");
    if (!list || !list.contains(item)) return;
    var focusTarget = item.previousElementSibling || item.nextElementSibling;
    if (list.querySelectorAll(".rbe-list-item").length > 1) {
      item.remove();
      if (focusTarget) placeCaretAtEnd(focusTarget.querySelector(".rbe-list-text"));
    } else {
      var text = item.querySelector(".rbe-list-text");
      if (text) text.innerHTML = "";
      item.dataset.level = "0";
      placeCaretAtEnd(text);
    }
    this.updateListMarkers(block.querySelector(".rbe-list-wrap"));
    this.changed(true);
  };

  BlockWriteAI.prototype.updateListMarkers = function (wrap) {
    if (!wrap) return;
    var ordered = wrap.dataset.style === "ordered";
    var counters = [0, 0, 0, 0, 0];
    Array.prototype.slice.call(wrap.querySelectorAll(".rbe-list-item")).forEach(function (item) {
      var level = Math.min(4, Math.max(0, Number(item.dataset.level || 0)));
      var itemOrdered = (item.dataset.style || (ordered ? "ordered" : "unordered")) === "ordered";
      item.dataset.level = String(level);
      if (itemOrdered) counters[level] += 1;
      for (var i = level + 1; i < counters.length; i += 1) counters[i] = 0;
      var marker = item.querySelector(".rbe-list-marker");
      if (marker) marker.textContent = itemOrdered ? counters.slice(0, level + 1).filter(function (count) { return count > 0; }).join(".") + "." : "\u2022";
    });
  };

  BlockWriteAI.prototype.updateChecklistItemIcon = function (item) {
    if (!item) return;
    var toggle = item.querySelector(".rbe-check-toggle");
    if (!toggle) return;
    var level = listItemLevel({ level: item.dataset.level });
    var checked = item.dataset.checked === "true";
    if (item.dataset.checked !== "true" && item.dataset.checked !== "false") checked = toggle.dataset.checked === "true";
    item.dataset.level = String(level);
    item.dataset.checked = checked ? "true" : "false";
    toggle.dataset.checked = checked ? "true" : "false";
    toggle.setAttribute("aria-pressed", checked ? "true" : "false");
    toggle.setAttribute("aria-label", checked ? "Mark item incomplete" : "Mark item complete");
    toggle.title = checked ? "Mark incomplete" : "Mark complete";
    toggle.classList.toggle("is-checked", checked);
    toggle.classList.toggle("is-nested", level > 0);
    var icon = level > 0 ? (checked ? "circle-check" : "circle") : (checked ? "square-check" : "square");
    toggle.innerHTML = svgIcon(icon, checked ? "Checked" : "Unchecked");
  };

  BlockWriteAI.prototype.updateChecklistIcons = function (wrap) {
    if (!wrap) return;
    Array.prototype.slice.call(wrap.querySelectorAll(".rbe-check-item")).forEach(
      function (item) {
        this.updateChecklistItemIcon(item);
      }.bind(this)
    );
  };

  BlockWriteAI.prototype.getActiveChecklistItem = function (block) {
    var checklist = block && block.querySelector(".rbe-checklist");
    if (!checklist) return null;
    var selection = window.getSelection();
    var selectionNode = selection && selection.anchorNode ? selection.anchorNode : document.activeElement;
    var activeNode = selectionNode && selectionNode.nodeType === Node.TEXT_NODE ? selectionNode.parentElement : selectionNode;
    var activeItem = activeNode && activeNode.closest ? activeNode.closest(".rbe-check-item") : null;
    if (!activeItem || !checklist.contains(activeItem)) activeItem = checklist.querySelector(".rbe-check-item");
    return activeItem;
  };

  BlockWriteAI.prototype.applyChecklistNestAction = function (block, action) {
    if (!block || block.dataset.type !== "checklist") return;
    var activeItem = this.getActiveChecklistItem(block);
    if (!activeItem || (action !== "indent-checklist" && action !== "outdent-checklist")) return;
    var level = Number(activeItem.dataset.level || 0);
    if (action === "indent-checklist") {
      if (!activeItem.previousElementSibling) return;
      activeItem.dataset.level = Math.min(4, level + 1);
    } else {
      activeItem.dataset.level = Math.max(0, level - 1);
    }
    this.updateChecklistItemIcon(activeItem);
    placeCaretAtEnd(activeItem.querySelector(".rbe-check-text"));
    this.changed(true);
  };

  BlockWriteAI.prototype.addChecklistItem = function (block) {
    if (!block || block.dataset.type !== "checklist") return;
    var checklist = block.querySelector(".rbe-checklist");
    if (!checklist) return;
    var active = this.getActiveChecklistItem(block);
    var next = this.createChecklistItem({
      text: "",
      checked: false,
      level: active ? Number(active.dataset.level || 0) : 0
    });
    if (active && active.parentNode === checklist) active.insertAdjacentElement("afterend", next);
    else checklist.insertBefore(next, checklist.querySelector(".rbe-checklist-add"));
    placeCaretAtEnd(next.querySelector(".rbe-check-text"));
    this.changed(true);
  };

  BlockWriteAI.prototype.removeChecklistItem = function (block, item) {
    if (!block || block.dataset.type !== "checklist" || !item) return;
    var checklist = block.querySelector(".rbe-checklist");
    if (!checklist || !checklist.contains(item)) return;
    var focusTarget = item.previousElementSibling || item.nextElementSibling;
    if (checklist.querySelectorAll(".rbe-check-item").length > 1) {
      item.remove();
      if (focusTarget && focusTarget.classList.contains("rbe-check-item")) placeCaretAtEnd(focusTarget.querySelector(".rbe-check-text"));
    } else {
      var text = item.querySelector(".rbe-check-text");
      if (text) text.innerHTML = "";
      item.dataset.level = "0";
      item.dataset.checked = "false";
      this.updateChecklistItemIcon(item);
      placeCaretAtEnd(text);
    }
    this.changed(true);
  };

  BlockWriteAI.prototype.toggleChecklistItem = function (block, item) {
    if (!block || block.dataset.type !== "checklist" || !item || this.readOnly) return;
    item.dataset.checked = item.dataset.checked === "true" ? "false" : "true";
    this.updateChecklistItemIcon(item);
    this.changed(true);
  };

  BlockWriteAI.prototype.bindTableSelection = function (wrap) {
    if (!wrap || wrap.dataset.selectionBound === "true" || this.readOnly) return;
    var table = wrap.querySelector(".rbe-table");
    if (!table) return;
    wrap.dataset.selectionBound = "true";
    this.refreshTableCellIndexes(table);

    table.addEventListener(
      "mousedown",
      function (event) {
        var cell = tableCellFromTarget(event.target);
        if (!cell || !table.contains(cell)) return;
        wrap.dataset.selecting = "true";
        if (!event.shiftKey) {
          wrap.dataset.anchorRow = String(cell.parentElement.rowIndex);
          wrap.dataset.anchorCol = String(cell.cellIndex);
        }
        this.selectTableRange(wrap, Number(wrap.dataset.anchorRow || cell.parentElement.rowIndex), Number(wrap.dataset.anchorCol || cell.cellIndex), cell.parentElement.rowIndex, cell.cellIndex);
      }.bind(this)
    );

    table.addEventListener(
      "mouseover",
      function (event) {
        if (wrap.dataset.selecting !== "true") return;
        var cell = tableCellFromTarget(event.target);
        if (!cell || !table.contains(cell)) return;
        this.selectTableRange(wrap, Number(wrap.dataset.anchorRow || cell.parentElement.rowIndex), Number(wrap.dataset.anchorCol || cell.cellIndex), cell.parentElement.rowIndex, cell.cellIndex);
      }.bind(this)
    );

    document.addEventListener("mouseup", function () {
      wrap.dataset.selecting = "false";
    });
  };

  BlockWriteAI.prototype.refreshTableCellIndexes = function (table) {
    if (!table) return;
    Array.prototype.slice.call(table.rows).forEach(function (row, rowIndex) {
      Array.prototype.slice.call(row.cells).forEach(function (cell, colIndex) {
        cell.dataset.row = rowIndex;
        cell.dataset.col = colIndex;
      });
    });
  };

  BlockWriteAI.prototype.clearTableSelection = function (wrap) {
    if (!wrap) return;
    Array.prototype.slice.call(wrap.querySelectorAll(".rbe-table-cell-selected")).forEach(function (cell) {
      cell.classList.remove("rbe-table-cell-selected");
      cell.removeAttribute("aria-selected");
    });
    wrap.dataset.selectionMode = "";
  };

  BlockWriteAI.prototype.selectTableRange = function (wrap, startRow, startCol, endRow, endCol, mode) {
    var table = wrap && wrap.querySelector(".rbe-table");
    if (!table) return [];
    this.refreshTableCellIndexes(table);
    var rows = Array.prototype.slice.call(table.rows);
    var minRow = Math.max(0, Math.min(startRow, endRow));
    var maxRow = Math.min(rows.length - 1, Math.max(startRow, endRow));
    var minCol = Math.max(0, Math.min(startCol, endCol));
    var maxCol = Math.max(startCol, endCol);
    if (mode === "row") {
      minCol = 0;
      maxCol = Math.max.apply(null, rows.map(function (row) { return row.cells.length - 1; }));
    }
    if (mode === "col") {
      minRow = 0;
      maxRow = rows.length - 1;
    }
    this.clearTableSelection(wrap);
    var selected = [];
    rows.forEach(function (row, rowIndex) {
      if (rowIndex < minRow || rowIndex > maxRow) return;
      Array.prototype.slice.call(row.cells).forEach(function (cell, colIndex) {
        if (colIndex < minCol || colIndex > maxCol) return;
        cell.classList.add("rbe-table-cell-selected");
        cell.setAttribute("aria-selected", "true");
        selected.push(cell);
      });
    });
    wrap.dataset.selectionMode = mode || (selected.length > 1 ? "range" : "cell");
    return selected;
  };

  BlockWriteAI.prototype.selectedTableCells = function (wrap, fallbackCell) {
    var cells = wrap ? Array.prototype.slice.call(wrap.querySelectorAll(".rbe-table-cell-selected")) : [];
    if (!cells.length && fallbackCell) cells = [fallbackCell];
    return cells;
  };

  BlockWriteAI.prototype.applyTableAction = function (block, action) {
    if (!block) return;
    var wrap = block.querySelector(".rbe-table-wrap");
    var table = block.querySelector(".rbe-table");
    if (!table) return;
    var rows = Array.prototype.slice.call(table.rows);
    if (!rows.length) return;
    this.refreshTableCellIndexes(table);
    var active = tableCellFromTarget(document.activeElement);
    if (!active || !table.contains(active)) active = wrap && wrap.querySelector(".rbe-table-cell-selected");
    var rowIndex = active && active.parentElement ? active.parentElement.rowIndex : rows.length - 1;
    var colIndex = active ? active.cellIndex : rows[0].cells.length - 1;

    if (action.indexOf("cell-align-") === 0) {
      var align = cleanAlignment(action.replace("cell-align-", ""));
      this.selectedTableCells(wrap, active && table.contains(active) ? active : rows[0].cells[0]).forEach(function (cell) {
        cell.style.textAlign = align;
      });
      this.changed(true);
      return;
    }

    if (action === "select-row" || action === "select-col") {
      var selected = this.selectedTableCells(wrap, active);
      if (selected.length) {
        var selectedRows = selected.map(function (cell) { return cell.parentElement.rowIndex; });
        var selectedCols = selected.map(function (cell) { return cell.cellIndex; });
        this.selectTableRange(wrap, Math.min.apply(null, selectedRows), Math.min.apply(null, selectedCols), Math.max.apply(null, selectedRows), Math.max.apply(null, selectedCols), action === "select-row" ? "row" : "col");
      }
      return;
    }

    if (action === "clear-selection") {
      this.clearTableSelection(wrap);
      return;
    }

    if (action === "add-row") {
      var row = table.insertRow(rowIndex + 1);
      for (var i = 0; i < rows[0].cells.length; i += 1) {
        var cell = row.insertCell(i);
        cell.contentEditable = !this.readOnly;
      }
    }

    if (action === "add-col") {
      rows.forEach(
        function (row) {
          var cell = row.insertCell(colIndex + 1);
          cell.contentEditable = !this.readOnly;
        }.bind(this)
      );
    }

    if (action === "remove-row" && rows.length > 1) {
      table.deleteRow(Math.max(0, rowIndex));
    }

    if (action === "remove-col" && rows[0].cells.length > 1) {
      rows.forEach(function (row) {
        row.deleteCell(Math.max(0, colIndex));
      });
    }

    this.refreshTableHeading(block);
    this.refreshTableCellIndexes(table);
    this.updateTableLayout(block);
    this.changed(true);
  };

  BlockWriteAI.prototype.updateTableLayout = function (block) {
    var wrap = block && block.querySelector(".rbe-table-wrap");
    var table = block && block.querySelector(".rbe-table");
    if (!wrap || !table) return;
    var widthInput = block.querySelector(".rbe-table-width");
    var widthValue = block.querySelector(".rbe-table-width-value");
    var alignSelect = block.querySelector(".rbe-table-align");
    var borderSelect = block.querySelector(".rbe-table-border-style");
    var borderWidthInput = block.querySelector(".rbe-table-border-width");
    var borderWidthValue = block.querySelector(".rbe-table-border-width-value");
    var width = Math.min(100, Math.max(40, Number(widthInput && widthInput.value ? widthInput.value : 100)));
    var align = alignSelect && (alignSelect.value === "center" || alignSelect.value === "right") ? alignSelect.value : "left";
    var borderStyle = cleanTableBorderStyle(borderSelect && borderSelect.value);
    var borderWidth = cleanTableBorderWidth(borderWidthInput && borderWidthInput.value);
    wrap.dataset.align = align;
    table.style.width = width + "%";
    table.style.setProperty("--rbe-table-border-style", borderStyle);
    table.style.setProperty("--rbe-table-border-width", borderWidth + "px");
    if (widthValue) widthValue.textContent = width + "%";
    if (borderWidthValue) borderWidthValue.textContent = borderWidth + "px";
  };

  BlockWriteAI.prototype.refreshTableHeading = function (block) {
    var table = block && block.querySelector(".rbe-table");
    var toggle = block && block.querySelector("[data-role='table-heading']");
    if (!table || !toggle) return;
    var rows = Array.prototype.slice.call(table.rows);
    rows.forEach(
      function (row, rowIndex) {
        Array.prototype.slice.call(row.cells).forEach(
          function (cell) {
            var tag = toggle.checked && rowIndex === 0 ? "th" : "td";
            if (cell.tagName.toLowerCase() !== tag) {
              var newCell = el(tag, "", {
                contentEditable: !this.readOnly,
                html: sanitizeHTML(cell.innerHTML)
              });
              newCell.style.textAlign = cell.style ? cell.style.textAlign || "left" : "left";
              cell.replaceWith(newCell);
            }
          }.bind(this)
        );
      }.bind(this)
    );
  };

  BlockWriteAI.prototype.serializeBlock = function (block) {
    var type = block.dataset.type || "paragraph";
    var data = {};

    if (type === "paragraph") {
      var paragraph = block.querySelector(".rbe-paragraph");
      data.text = sanitizeHTML((paragraph || {}).innerHTML || "");
      data.alignment = cleanAlignment(paragraph && paragraph.style ? paragraph.style.textAlign : "left");
    } else if (type === "heading") {
      var heading = block.querySelector(".rbe-heading");
      data.text = sanitizeHTML(heading ? heading.innerHTML : "");
      data.level = Number((heading && heading.dataset.level) || (block.querySelector(".rbe-heading-level") || {}).value || 2);
      data.alignment = cleanAlignment(heading && heading.style ? heading.style.textAlign : "left");
    } else if (type === "quote") {
      var quoteText = block.querySelector(".rbe-quote-text");
      data.text = sanitizeHTML((quoteText || {}).innerHTML || "");
      data.caption = (block.querySelector(".rbe-quote-caption") || {}).value || "";
      data.alignment = cleanAlignment(quoteText && quoteText.style ? quoteText.style.textAlign : "left");
    } else if (type === "list") {
      var list = block.querySelector(".rbe-list");
      data.style = (block.querySelector(".rbe-list-style") || {}).value || "unordered";
      data.alignment = cleanAlignment(list && list.style ? list.style.textAlign : "left");
      data.items = list
        ? Array.prototype.slice.call(list.querySelectorAll(".rbe-list-item")).map(function (item) {
            return {
              text: sanitizeHTML((item.querySelector(".rbe-list-text") || {}).innerHTML || ""),
              level: listItemLevel({ level: item.dataset.level }),
              alignment: cleanAlignment((item.querySelector(".rbe-list-text") || {}).style ? item.querySelector(".rbe-list-text").style.textAlign : data.alignment),
              style: item.dataset.style || ""
            };
          }).filter(function (item) {
            return !isEmptyListItem(item);
          })
        : [];
      if (!data.items.length) data.items = [{ text: "", level: 0 }];
    } else if (type === "checklist") {
      data.items = Array.prototype.slice.call(block.querySelectorAll(".rbe-check-item")).map(function (item) {
        var toggle = item.querySelector(".rbe-check-toggle");
        var checked = item.dataset.checked === "true";
        if (item.dataset.checked !== "true" && item.dataset.checked !== "false") checked = !!(toggle && toggle.dataset.checked === "true");
        return {
          text: sanitizeHTML((item.querySelector(".rbe-check-text") || {}).innerHTML || ""),
          checked: checked,
          level: listItemLevel({ level: item.dataset.level })
        };
      }).filter(function (item) {
        return !isEmptyListItem(item);
      });
      if (!data.items.length) data.items = [{ text: "", checked: false, level: 0 }];
    } else if (type === "code") {
      data.code = (block.querySelector(".rbe-code-input") || {}).value || "";
      data.language = normalizeCodeLanguage((block.querySelector(".rbe-code-language") || {}).value);
      data.headerColor = normalizeHexColor((block.querySelector(".rbe-code-header-color") || {}).value, "#e5e7eb");
      data.backgroundColor = normalizeHexColor((block.querySelector(".rbe-code-bg-color") || {}).value, "#d7dee8");
    } else if (type === "image") {
      data.items = Array.prototype.slice.call(block.querySelectorAll(".rbe-image-stage")).map(function (stage) {
        var image = stage.querySelector(".rbe-image-preview");
        var stored = stage.__rbeMediaItem || {};
        var item = {
          url: stage.dataset.url || (image ? image.src : ""),
          alt: stage.dataset.alt || (image ? image.alt : ""),
          caption: stage.dataset.caption || "",
          alignment: cleanAlignment(stage.dataset.alignment),
          displayWidth: Number(stage.dataset.displayWidth || 0),
          minWidth: Number(stage.dataset.minWidth || 0),
          naturalWidth: Number(stage.dataset.naturalWidth || 0),
          naturalHeight: Number(stage.dataset.naturalHeight || 0),
          rotation: Number(stage.dataset.rotation || 0),
          crop: stage.dataset.crop === "custom" ? "custom" : "free",
          cropRatio: Number(stage.dataset.cropRatio || 0),
          cropX: imageCropPercent(stage.dataset.cropX),
          cropY: imageCropPercent(stage.dataset.cropY),
          cropZoom: imageCropZoom(stage.dataset.cropZoom)
        };
        if (stored.base64) item.base64 = stored.base64;
        if (stored.upload) item.upload = cloneJSON(stored.upload);
        return item;
      }).filter(function (item) {
        return !!item.url;
      });
      data.url = data.items[0] ? data.items[0].url : "";
      data.alt = data.items[0] ? data.items[0].alt : "";
      data.caption = data.items[0] ? data.items[0].caption : "";
    } else if (type === "table") {
      data.withHeadings = !!(block.querySelector("[data-role='table-heading']") || {}).checked;
      data.align = (block.querySelector(".rbe-table-align") || {}).value || "left";
      data.width = Number((block.querySelector(".rbe-table-width") || {}).value || 100);
      data.borderStyle = cleanTableBorderStyle((block.querySelector(".rbe-table-border-style") || {}).value);
      data.borderWidth = cleanTableBorderWidth((block.querySelector(".rbe-table-border-width") || {}).value);
      data.rows = Array.prototype.slice.call(block.querySelectorAll(".rbe-table tr")).map(function (row) {
        return Array.prototype.slice.call(row.cells).map(function (cell) {
          return {
            text: sanitizeHTML(cell.innerHTML),
            alignment: cleanAlignment(cell.style ? cell.style.textAlign : "left")
          };
        });
      });
    } else if (type === "embed") {
      data.source = (block.querySelector(".rbe-embed-url") || {}).value || "";
      var frame = block.querySelector(".rbe-embed-frame");
      data.embed = frame ? frame.src : "";
      data.service = toEmbed(data.source).service;
      data.caption = (block.querySelector(".rbe-embed-caption") || {}).value || "";
    } else if (type === "callout") {
      var calloutText = block.querySelector(".rbe-callout-text");
      data.text = sanitizeHTML((calloutText || {}).innerHTML || "");
      data.tone = (block.querySelector(".rbe-callout-tone") || {}).value || (block.querySelector(".rbe-callout") || {}).dataset.tone || "info";
      data.alignment = cleanAlignment(calloutText && calloutText.style ? calloutText.style.textAlign : "left");
    } else if (type === "delimiter") {
      data.style = (block.querySelector(".rbe-delimiter-style") || {}).value || (block.querySelector(".rbe-delimiter-wrap") || {}).dataset.style || "line";
    } else if (type === "button") {
      data.text = (block.querySelector(".rbe-button-text") || {}).value || "";
      data.url = (block.querySelector(".rbe-button-url") || {}).value || "";
      data.background = (block.querySelector(".rbe-button-bg") || {}).value || "#2563eb";
      data.color = (block.querySelector(".rbe-button-color") || {}).value || "#ffffff";
      data.alignment = cleanAlignment((block.querySelector(".rbe-button-align") || {}).value || "left");
    } else if (type === "toggle") {
      data.title = (block.querySelector(".rbe-toggle-title") || {}).value || "";
      var nestedCanvas = block.querySelector(".rbe-toggle-nested-canvas");
      data.blocks = nestedCanvas ? Array.prototype.slice.call(nestedCanvas.children).map(this.serializeBlock.bind(this)) : [];
      data.text = data.blocks.map(function (child) {
        return child.data && child.data.text ? child.data.text : "";
      }).join("");
      data.open = !!(block.querySelector(".rbe-toggle") || {}).open;
    } else if (type === "signatureRow") {
      var rowCanvas = block.querySelector(".rbe-signature-row-canvas");
      data.blocks = rowCanvas ? rowContentBlocks(rowCanvas).slice(0, MAX_BLOCKS_PER_ROW).map(this.serializeBlock.bind(this)) : [];
    } else if (type === "linkPreview") {
      data.url = (block.querySelector(".rbe-link-preview-url") || {}).value || "";
      data.title = (block.querySelector(".rbe-link-preview-title") || {}).value || "";
      data.description = (block.querySelector(".rbe-link-preview-description") || {}).value || "";
    } else if (type === "raw") {
      var rawWrap = block.querySelector(".rbe-raw-wrap");
      data.html = (block.querySelector(".rbe-raw-input") || {}).value || (rawWrap && rawWrap.__rbeRawHtml) || "";
    } else if (type === "attaches") {
      var cachedData = normalizeAttachmentItems(block.__rbeBlockData || {});
      var bodyData = normalizeAttachmentItems((block.querySelector(".rbe-block-body") || {}).__rbeBlockData || {});
      var attachmentWrap = block.querySelector(".rbe-attachment");
      var attachmentState = block.querySelector(".rbe-attachment-items-json");
      var stateItems = [];
      if (attachmentState && attachmentState.value) {
        try {
          stateItems = normalizeAttachmentItems({ items: JSON.parse(attachmentState.value) });
        } catch (error) {
          stateItems = [];
        }
      }
      var fallbackItems = normalizeAttachmentItems({ items: attachmentWrap && attachmentWrap.__rbeAttachmentItems ? attachmentWrap.__rbeAttachmentItems : [] });
      data.items = Array.prototype.slice.call(block.querySelectorAll(".rbe-attachment-card")).map(function (card) {
        var stored = card.__rbeMediaItem || {};
        var link = card.querySelector(".rbe-attachment-card-link");
        var upload = stored.upload || null;
        var url = stored.url || card.dataset.url || (link ? link.getAttribute("href") : "") || (upload && upload.url) || stored.base64 || "";
        var item = {
          name: stored.name || card.dataset.name || (link ? link.getAttribute("title") : "") || "Attachment",
          url: url,
          type: stored.type || card.dataset.type || "",
          size: Number(stored.size || card.dataset.size || 0),
          alignment: cleanAlignment(stored.alignment || card.dataset.alignment)
        };
        if (stored.base64) item.base64 = stored.base64;
        if (upload) item.upload = cloneJSON(upload);
        return item;
      }).filter(function (item) {
        return !!(item.url || item.base64 || item.upload);
      });
      if (!data.items.length && cachedData.length) data.items = cachedData;
      if (!data.items.length && bodyData.length) data.items = bodyData;
      if (!data.items.length && stateItems.length) data.items = stateItems;
      if (!data.items.length && fallbackItems.length) data.items = fallbackItems;
      data.name = data.items[0] ? data.items[0].name : "";
      data.url = data.items[0] ? data.items[0].url : "";
      data.type = data.items[0] ? data.items[0].type : "";
      data.size = data.items[0] ? data.items[0].size : 0;
    } else if (PLUGIN_TOOLS[type] && typeof PLUGIN_TOOLS[type].serialize === "function") {
      data = PLUGIN_TOOLS[type].serialize.call(this, block, pluginHelpers()) || {};
    }

    return {
      id: block.dataset.id || uid("block"),
      type: type,
      data: data
    };
  };

  BlockWriteAI.prototype.getData = function () {
    var payload = {
      time: Date.now(),
      version: VERSION,
      blocks: Array.prototype.slice.call(this.canvas.children).map(this.serializeBlock.bind(this))
    };
    Object.keys(this.documentMeta || {}).forEach(function (key) {
      payload[key] = cloneJSON(this.documentMeta[key]);
    }, this);
    return payload;
  };

  BlockWriteAI.prototype.save = function () {
    return Promise.resolve(this.getData());
  };

  BlockWriteAI.prototype.render = function (data, options) {
    var previousRenderingData = this.renderingData;
    var previousSilent = this.silent;
    this.renderingData = true;
    this.silent = !!(options && options.silent);
    try {
      var meta = extractDocumentMeta(data);
      if (Object.keys(meta).length) this.documentMeta = meta;
      this.canvas.innerHTML = "";
      this.claimedBlockIds = {};
      var blocks = normalizeBlocks(data);
      if (!blocks.length) blocks = defaultData().blocks;
      blocks.forEach(
        function (block) {
          var type = BLOCK_TYPES.indexOf(block.type) >= 0 ? block.type : "paragraph";
          this.insertBlock(type, block.data || {}, this.canvas.children.length, block.id || uid("block"));
        }.bind(this)
      );
    } finally {
      this.renderingData = previousRenderingData;
      this.silent = previousSilent;
    }
    this.scheduleSelectEnhance();
    this.updateToolbarButtons();
    if (!(options && options.silent)) this.changed();
  };

  BlockWriteAI.prototype.scheduleSelectEnhance = function () {
    if (this.options.select2 === false || typeof window === "undefined") return;
    clearTimeout(this.selectEnhanceTimer);
    this.selectEnhanceTimer = setTimeout(
      function () {
        this.enhanceSelects();
      }.bind(this),
      0
    );
  };

  BlockWriteAI.prototype.enhanceSelects = function () {
    if (this.options.select2 === false || !this.root) return;
    var root = this.root;
    var self = this;
    ensureSelect2(function () {
      var $ = window.jQuery;
      if (!$ || !$.fn || !$.fn.select2) return;
      $(root)
        .find("select")
        .each(function () {
          var select = this;
          if ($(select).data("select2")) return;
          $(select)
            .select2({
              width: "style",
              minimumResultsForSearch: Infinity,
              dropdownParent: $(document.body)
            })
            .on("select2:opening select2:open", function () {
              if (select.closest(".rbe-inline-toolbar")) self.keepInlineToolbarOpen();
            })
            .on("select2:select", function () {
              if (select.closest(".rbe-inline-toolbar")) self.keepInlineToolbarOpen();
              select.dispatchEvent(new Event("change", { bubbles: true }));
            });
        });
    });
  };

  BlockWriteAI.prototype.clear = function () {
    this.render(defaultData());
  };

  BlockWriteAI.prototype.destroy = function () {
    document.removeEventListener("selectionchange", this.boundSelectionChange);
    document.removeEventListener("mousedown", this.boundDocumentMouseDown);
    (this.externalExportButtons || []).forEach(function (entry) {
      if (entry && entry.button && entry.listener) {
        entry.button.removeEventListener("click", entry.listener);
      }
    });
    this.externalExportButtons = [];
    this.hideCommentPopover();
    if (this.commentPopover && this.boundCommentPopoverClick) this.commentPopover.removeEventListener("click", this.boundCommentPopoverClick);
    if (this.commentPopover && this.commentPopover.parentNode) this.commentPopover.parentNode.removeChild(this.commentPopover);
    this.holder.innerHTML = "";
  };

  BlockWriteAI.prototype.setReadOnly = function (value) {
    this.readOnly = !!value;
    this.root.dataset.readonly = this.readOnly ? "true" : "false";
    Array.prototype.slice.call(this.canvas.children).forEach(
      function (block) {
        this.makeBlockReadonly(block);
      }.bind(this)
    );
    this.updateToolbarButtons();
  };

  BlockWriteAI.prototype.makeBlockReadonly = function (block) {
    Array.prototype.slice.call(block.querySelectorAll("[contenteditable]")).forEach(
      function (item) {
        item.contentEditable = !this.readOnly;
      }.bind(this)
    );
    Array.prototype.slice.call(block.querySelectorAll("input, textarea, select, button")).forEach(
      function (item) {
        if (item.classList.contains("rbe-code-copy")) return;
        item.disabled = this.readOnly;
      }.bind(this)
    );
  };

  BlockWriteAI.prototype.changed = function (immediate) {
    if (this.silent) return;
    this.updateToolbarButtons();
    if (this.hasExplicitSave || this.savedExportData) {
      this.hasExplicitSave = false;
      this.savedExportData = null;
      this.hideExportMenu();
      this.updateToolbarButtons();
    }
    if (immediate) {
      if (this.queueChange && typeof this.queueChange.cancel === "function") this.queueChange.cancel();
      this.emitChange();
      return;
    }
    this.queueChange();
  };

  BlockWriteAI.prototype.emitChange = function () {
    var data = this.getData();
    if (this.options.autosave && this.options.autosave.key) {
      try {
        localStorage.setItem(this.options.autosave.key, JSON.stringify(data));
      } catch (error) {
      }
    }
    if (typeof this.options.onChange === "function") {
      this.options.onChange(data, this);
    }
    this.persistDocument("change", data);
    this.setShellStatus("JSON synced " + new Date(data.time || Date.now()).toLocaleTimeString());
    this.root.dispatchEvent(new CustomEvent("rbe:change", { detail: data }));
  };

  BlockWriteAI.prototype.persistenceDocumentId = function (data) {
    return persistenceDocumentId(this.persistenceOptions, this.options, data || this.documentMeta || {});
  };

  BlockWriteAI.prototype.persistDocument = function (action, data, detail) {
    var persistence = this.persistenceOptions;
    if (!persistence) return Promise.resolve(data);
    data = data || this.getData();
    var payload = {
      type: "document",
      action: action || "change",
      documentId: this.persistenceDocumentId(data),
      data: data,
      detail: detail || null,
      version: VERSION
    };
    var hook = persistence.saveDocument || persistence.save || persistence.onSaveDocument;
    if (persistence.localStorageKey) {
      try {
        localStorage.setItem(persistence.localStorageKey, JSON.stringify(data));
      } catch (error) {
      }
    }
    return callPersistenceHook(hook, payload, this).catch(
      function (error) {
        if (this.root) this.root.dispatchEvent(new CustomEvent("rbe:persistence-error", { detail: { error: error, payload: payload } }));
        return null;
      }.bind(this)
    );
  };

  BlockWriteAI.prototype.persistCommentChange = function (action, commentId, detail) {
    var persistence = this.persistenceOptions;
    if (!persistence || !commentId) return Promise.resolve(null);
    var data = this.getData();
    var comment = this.getCommentPersistencePayload(commentId, action, detail);
    var commentHooks = persistence.comments || persistence.comment || {};
    var hook = commentHooks.save || persistence.saveComment || persistence.onCommentChange;
    var payload = {
      type: "comment",
      action: action || "change",
      documentId: this.persistenceDocumentId(data),
      commentId: commentId,
      comment: comment,
      data: data,
      version: VERSION
    };
    return callPersistenceHook(hook, payload, this).catch(
      function (error) {
        if (this.root) this.root.dispatchEvent(new CustomEvent("rbe:persistence-error", { detail: { error: error, payload: payload } }));
        return null;
      }.bind(this)
    );
  };

  BlockWriteAI.prototype.getCommentPersistencePayload = function (commentId, action, detail) {
    var mark = this.root
      ? Array.prototype.slice.call(this.root.querySelectorAll(".rbe-comment-mark")).filter(function (item) {
          return item.getAttribute("data-rbe-comment-id") === commentId;
        })[0]
      : null;
    var thread = mark ? parseCommentThread(mark.getAttribute("data-rbe-comment-thread") || "", mark.getAttribute("data-rbe-comment-reply") || "", mark.getAttribute("data-rbe-comment-reply-date") || "") : [];
    return Object.assign(
      {
        id: commentId,
        action: action || "change",
        text: mark ? mark.getAttribute("data-rbe-comment") || "" : "",
        status: mark ? normalizeCommentStatus(mark.getAttribute("data-rbe-comment-status")) : "pending",
        reason: mark ? mark.getAttribute("data-rbe-comment-reason") || "" : "",
        createdAt: mark ? mark.getAttribute("data-rbe-comment-date") || "" : "",
        updatedAt: mark ? mark.getAttribute("data-rbe-comment-updated") || "" : "",
        memberName: mark ? mark.getAttribute("data-rbe-comment-member-name") || "" : "",
        memberRole: mark ? mark.getAttribute("data-rbe-comment-member-role") || "" : "",
        updatedMemberName: mark ? mark.getAttribute("data-rbe-comment-updated-member-name") || "" : "",
        updatedMemberRole: mark ? mark.getAttribute("data-rbe-comment-updated-member-role") || "" : "",
        thread: thread
      },
      detail || {}
    );
  };

  BlockWriteAI.prototype.setupAutosave = function () {
    if (!this.options.autosave || !this.options.autosave.key || !this.options.autosave.load) {
      this.render(this.options.data || defaultData(), { silent: true });
      return;
    }
    try {
      var saved = localStorage.getItem(this.options.autosave.key);
      if (saved) this.render(JSON.parse(saved), { silent: true });
      else this.render(this.options.data || defaultData(), { silent: true });
    } catch (error) {
      this.render(this.options.data || defaultData(), { silent: true });
    }
  };

  BlockWriteAI.prototype.resolveExternalButton = function (target) {
    if (!target) return null;
    if (typeof target === "string") return document.querySelector(target);
    if (target && target.nodeType === 1) return target;
    return null;
  };

  BlockWriteAI.prototype.setupExternalExportButtons = function () {
    var editor = this;
    var configs = [
      {
        target: this.options.bwaipdf || this.options.exportPdfButton,
        type: "pdf",
        icon: "file-export",
        label: "Generate PDF",
        className: "rbe-external-export-pdf-btn"
      },
      {
        target: this.options.bwaidoc || this.options.exportWordButton,
        type: "word",
        icon: "file-lines",
        label: "Generate Word",
        className: "rbe-external-export-word-btn"
      }
    ];
    (this.externalExportButtons || []).forEach(function (entry) {
      if (entry && entry.button && entry.listener) {
        entry.button.removeEventListener("click", entry.listener);
      }
    });
    this.externalExportButtons = [];
    configs.forEach(function (config) {
      var button = editor.resolveExternalButton(config.target);
      if (!button) return;
      if (button.tagName && button.tagName.toLowerCase() === "button" && !button.getAttribute("type")) {
        button.setAttribute("type", "button");
      }
      button.classList.add("rbe-toolbar-btn", "rbe-external-export-btn", config.className);
      button.innerHTML = svgIcon(config.icon, config.label);
      button.setAttribute("title", config.label);
      button.setAttribute("aria-label", config.label);
      button.dataset.rbeExternalExport = config.type;
      var listener = function (event) {
        event.preventDefault();
        event.stopPropagation();
        if (button.disabled || editor.readOnly || !editor.hasExplicitSave || !editor.savedExportData) return;
        editor.handleExport(config.type);
      };
      button.addEventListener("click", listener);
      editor.externalExportButtons.push({ button: button, listener: listener, type: config.type });
    });
    this.updateExternalExportButtons();
  };

  BlockWriteAI.prototype.updateExternalExportButtons = function () {
    var disabled = this.readOnly || !this.hasExplicitSave || !this.savedExportData;
    (this.externalExportButtons || []).forEach(function (entry) {
      if (entry && entry.button) entry.button.disabled = disabled;
    });
  };

  BlockWriteAI.prototype.updateToolbarButtons = function () {
    if (this.topToolbar) {
      var clearButton = this.topToolbar.querySelector("[data-rbe-clear='true']");
      var saveButton = this.topToolbar.querySelector("[data-rbe-save='true']");
      var exportButton = this.topToolbar.querySelector("[data-rbe-export-toggle='true']");
      var exportHtmlButton = this.topToolbar.querySelector("[data-rbe-export-html='true']");
      var dashboardButton = this.topToolbar.querySelector("[data-rbe-show-dashboard='true']");
      var blocks = this.canvas ? Array.prototype.slice.call(this.canvas.children).map(this.serializeBlock.bind(this)).filter(function (block) {
        if (!block || !block.type) return false;
        if (block.type !== "paragraph") return true;
        var data = block.data || {};
        return !!String(data.text || "").replace(/<[^>]+>/g, "").trim();
      }) : [];
      var hasContent = blocks.length > 0;
      var hasSheetAnalyzer = blocks.some(function (block) {
        return block && block.type === "sheetAnalyzer";
      });
      var hasNonSheetAnalyzer = blocks.some(function (block) {
        return block && block.type !== "sheetAnalyzer";
      });
      var showDashboard = hasContent && hasSheetAnalyzer;
      var showExportHtml = hasContent && hasNonSheetAnalyzer;
      var disabled = this.readOnly || !this.hasExplicitSave || !this.savedExportData;
      function setToolbarVisibility(button, visible) {
        if (!button) return;
        button.hidden = !visible;
        button.style.display = visible ? "" : "none";
      }
      if (clearButton) clearButton.disabled = this.readOnly;
      if (saveButton) saveButton.disabled = this.readOnly;
      if (exportButton) exportButton.disabled = disabled;
      if (dashboardButton) {
        setToolbarVisibility(dashboardButton, showDashboard);
        dashboardButton.disabled = disabled;
      }
      if (exportHtmlButton) {
        setToolbarVisibility(exportHtmlButton, showExportHtml);
        exportHtmlButton.disabled = disabled;
      }
    }
    this.updateExternalExportButtons();
  };

  BlockWriteAI.prototype.hideExportMenu = function () {
    if (!this.topToolbar) return;
    var menu = this.topToolbar.querySelector(".rbe-export-menu");
    if (menu) menu.hidden = true;
  };

  BlockWriteAI.prototype.toggleExportMenu = function (button) {
    if (!button || button.disabled) return;
    var wrap = button.closest(".rbe-export-menu-wrap");
    var menu = wrap && wrap.querySelector(".rbe-export-menu");
    if (!menu) return;
    menu.hidden = !menu.hidden;
  };

  BlockWriteAI.prototype.handleClearButton = function (button) {
    if (this.readOnly || (button && button.disabled)) return;
    this.clear();
  };

  BlockWriteAI.prototype.handleSaveButton = function (button) {
    if (this.readOnly || (button && button.disabled)) {
      this.showToast("Save is not available right now.", "warning");
      return;
    }
    var editor = this;
    var saveData = null;
    if (button) button.disabled = true;
    Promise.resolve(this.save())
      .then(function (data) {
        saveData = data;
        if (typeof editor.options.onSave === "function") {
          return editor.options.onSave(data, editor);
        }
        if (editor.persistenceOptions) {
          return editor.persistDocument("manual-save", data).then(function (result) {
            return result == null ? data : result;
          });
        }
        return data;
      })
      .then(function (result) {
        var resolved = result == null ? saveData : result;
        editor.savedExportData = BlockWriteAI.normalizePreviewData(resolved);
        editor.hasExplicitSave = true;
        editor.root.dispatchEvent(new CustomEvent("rbe:save", { detail: editor.savedExportData }));
        editor.showToast("Document saved successfully.", "success");
        return editor.savedExportData;
      })
      .catch(function (error) {
        editor.hasExplicitSave = false;
        editor.savedExportData = null;
        editor.hideExportMenu();
        editor.root.dispatchEvent(new CustomEvent("rbe:save-error", { detail: error }));
        editor.showToast(error && error.message ? error.message : "Save failed.", "error");
      })
      .finally(function () {
        if (button) button.disabled = false;
        editor.updateToolbarButtons();
      });
  };

  BlockWriteAI.prototype.getExportFilename = function (extension) {
    var base = String(this.options.exportFilename || "blockwriteai-document")
      .trim()
      .replace(/[\\/:*?"<>|]+/g, "-")
      .replace(/\s+/g, "-")
      .replace(/^-+|-+$/g, "");
    if (!base) base = "blockwriteai-document";
    return base + "." + extension;
  };

  BlockWriteAI.prototype.downloadBlob = function (content, filename, type) {
    var blob = content instanceof Blob ? content : new Blob([content], { type: type || "application/octet-stream" });
    var url = URL.createObjectURL(blob);
    var link = el("a", "", {
      href: url,
      download: filename
    });
    document.body.appendChild(link);
    link.click();
    link.remove();
    setTimeout(function () {
      URL.revokeObjectURL(url);
    }, 1000);
  };

  BlockWriteAI.prototype.collectExportStyles = function () {
    return Array.prototype.slice
      .call(document.querySelectorAll('link[rel="stylesheet"], style'))
      .map(function (node) {
        if (node.tagName && node.tagName.toLowerCase() === "link") {
          var href = String(node.href || "");
          return '<link rel="stylesheet" href="' + escapeHTML(node.href) + '">';
        }
        if (node.matches && node.matches("style[data-blockwriteai-letterhead]")) return "";
        return node.outerHTML || "";
      })
      .filter(Boolean)
      .join("\n");
  };

  BlockWriteAI.prototype.getExportLogoUrl = function () {
    var assetLogo = inlineBlockWriteAILogoDataUrl();
    if (assetLogo) return assetLogo;
    var logo = document.querySelector(".brand-logo[src]");
    return logo ? logo.src : "";
  };

  BlockWriteAI.prototype.getInlineExportLogoDataUrl = function () {
    return inlineBlockWriteAILogoDataUrl();
  };

  BlockWriteAI.prototype.omitUnsignedSignatureFlowsForExport = function (html) {
    if (!html || typeof DOMParser === "undefined") return html;
    var parser = new DOMParser();
    var doc = parser.parseFromString(html, "text/html");
    Array.prototype.slice.call(doc.querySelectorAll(".rbe-output-signature:not([data-signed='true'])")).forEach(function (box) {
      box.remove();
    });
    Array.prototype.slice.call(doc.querySelectorAll(".rbe-output-signature-flow:not([data-complete='true'])")).forEach(function (flow) {
      flow.remove();
    });
    return "<!doctype html>\n" + doc.documentElement.outerHTML;
  };

  BlockWriteAI.prototype.readStoredExportSignature = function (key) {
    if (!key || typeof localStorage === "undefined") return null;
    try {
      return JSON.parse(localStorage.getItem(key) || "null");
    } catch (error) {
      return null;
    }
  };

  BlockWriteAI.prototype.applySingleSignatureForExport = function (box, record) {
    if (!box || !record || !record.name) return false;
    var unsigned = box.querySelector(".rbe-signature-unsigned");
    var signed = box.querySelector(".rbe-signature-signed");
    var input = box.querySelector(".rbe-signature-input");
    var name = box.querySelector(".rbe-signature-name");
    var by = box.querySelector(".rbe-signature-by-name");
    var time = box.querySelector(".rbe-signature-date");
    if (input) {
      input.value = record.name;
      input.readOnly = true;
    }
    if (name) name.textContent = record.name;
    if (by) by.textContent = record.name;
    if (time) {
      time.textContent = record.dateLabel || (record.date ? new Date(record.date).toLocaleString() : "");
      if (record.date) time.setAttribute("datetime", record.date);
    }
    if (unsigned) unsigned.hidden = true;
    if (signed) signed.hidden = false;
    box.dataset.signed = "true";
    box.classList.remove("has-error");
    return true;
  };

  BlockWriteAI.prototype.applyFlowSignatureForExport = function (card, record) {
    if (!card || !record || !record.name || !record.signatureHash) return false;
    var unsigned = card.querySelector(".rbe-flow-signature-unsigned");
    var signed = card.querySelector(".rbe-flow-signature-signed");
    var input = card.querySelector(".rbe-flow-signature-input");
    var status = card.querySelector(".rbe-flow-signature-status");
    var name = card.querySelector(".rbe-flow-signature-name");
    var by = card.querySelector(".rbe-flow-signature-by-name");
    var email = card.querySelector(".rbe-flow-signature-email");
    var ip = card.querySelector(".rbe-flow-signature-ip");
    var time = card.querySelector(".rbe-flow-signature-date");
    card.classList.add("is-signed");
    card.classList.remove("is-locked", "is-active", "is-pending", "has-error");
    card.dataset.signed = "true";
    card.dataset.signatureHash = record.signatureHash || "";
    if (input) {
      input.value = record.name;
      input.readOnly = true;
      input.disabled = true;
    }
    if (status) status.textContent = "Signed";
    if (name) name.textContent = record.name;
    if (by) by.textContent = record.name;
    if (email) email.textContent = record.email || "";
    if (ip) ip.textContent = record.ipAddress || "";
    if (time) {
      time.textContent = record.dateLabel || (record.date ? new Date(record.date).toLocaleString() : "");
      if (record.date) time.setAttribute("datetime", record.date);
    }
    if (unsigned) unsigned.hidden = true;
    if (signed) signed.hidden = false;
    return true;
  };

  BlockWriteAI.prototype.cleanExportSignatureText = function (value) {
    return String(value == null ? "" : value).replace(/\s+/g, " ").trim();
  };

  BlockWriteAI.prototype.signatureExportText = function (node, selector) {
    var target = node && selector ? node.querySelector(selector) : node;
    return this.cleanExportSignatureText((target && target.textContent) || "");
  };

  BlockWriteAI.prototype.appendExportSignatureLine = function (doc, parent, label, value) {
    value = this.cleanExportSignatureText(value);
    if (!value) return;
    var line = doc.createElement("div");
    line.setAttribute("style", "font-size:12px;line-height:1.35;color:#050505;margin:0 0 2px 0");
    var strong = doc.createElement("strong");
    strong.appendChild(doc.createTextNode(label + ": "));
    line.appendChild(strong);
    line.appendChild(doc.createTextNode(value));
    parent.appendChild(line);
  };

  BlockWriteAI.prototype.appendPdfSignatureLine = function (doc, parent, label, value) {
    value = this.cleanExportSignatureText(value);
    if (!value) return;
    var line = doc.createElement("div");
    line.setAttribute("style", "display:block;font-size:11px;line-height:1.35;color:#050505;margin:0 0 3px 0;padding:0");
    var strong = doc.createElement("strong");
    strong.appendChild(doc.createTextNode(label + ": "));
    line.appendChild(strong);
    line.appendChild(doc.createTextNode(value));
    parent.appendChild(line);
  };

  BlockWriteAI.prototype.createPdfSignatureCard = function (doc, label, record) {
    record = record || {};
    var card = doc.createElement("div");
    card.className = "rbe-export-signature-card rbe-export-signature-card-pdf";
    card.setAttribute("style", "display:block;width:100%;box-sizing:border-box;page-break-inside:avoid;background-color:#f0fdf4;border:1px solid #86efac;color:#050505;margin:0 0 10px 0;padding:8px;line-height:1.35");
    var head = doc.createElement("div");
    head.setAttribute("style", "display:block;color:#172033;font-size:13px;font-weight:bold;line-height:1.25;margin:0 0 5px 0;padding:0");
    head.appendChild(doc.createTextNode(this.cleanExportSignatureText(label) || "Signature"));
    var seal = doc.createElement("span");
    seal.setAttribute("style", "color:#d40000;font-size:10px;font-weight:bold");
    seal.appendChild(doc.createTextNode(" PDF"));
    head.appendChild(seal);
    card.appendChild(head);

    var status = doc.createElement("div");
    status.setAttribute("style", "display:block;color:#07845f;font-size:10px;font-weight:bold;line-height:1.1;margin:0 0 5px 0;padding:0");
    status.appendChild(doc.createTextNode("Signed"));
    card.appendChild(status);

    var signature = doc.createElement("div");
    signature.setAttribute("style", "display:block;color:#050505;font-family:'Brush Script MT','Segoe Script','Lucida Handwriting',cursive;font-size:14px;line-height:1.2;margin:0 0 6px 0;padding:0");
    signature.appendChild(doc.createTextNode(this.cleanExportSignatureText(record.name)));
    card.appendChild(signature);

    this.appendPdfSignatureLine(doc, card, "Signer Name", record.name);
    this.appendPdfSignatureLine(doc, card, "Signer Email", record.email);
    this.appendPdfSignatureLine(doc, card, "IP Address", record.ipAddress);
    this.appendPdfSignatureLine(doc, card, "Date & Time", record.dateLabel || record.date);
    return card;
  };

  BlockWriteAI.prototype.createStaticExportSignatureCard = function (doc, label, record) {
    record = record || {};
    if (this.options && this.options.exportTarget === "pdf") {
      return this.createPdfSignatureCard(doc, label, record);
    }
    var card = doc.createElement("div");
    card.className = "rbe-export-signature-card";
    card.setAttribute("style", "border:1px solid #86efac;background:#f0fdf4;border-radius:8px;padding:10px;margin:0 0 8px 0;color:#050505;box-sizing:border-box");
    var head = doc.createElement("div");
    head.setAttribute("style", "font-weight:800;font-size:13px;line-height:1.3;color:#172033;margin:0 0 5px 0");
    head.appendChild(doc.createTextNode(this.cleanExportSignatureText(label) || "Signature"));
    card.appendChild(head);
    var status = doc.createElement("div");
    status.setAttribute("style", "display:inline-block;border-radius:999px;background:#dcfce7;color:#07845f;font-weight:800;font-size:10px;line-height:1;padding:4px 7px;margin:0 0 7px 0");
    status.appendChild(doc.createTextNode("Signed"));
    card.appendChild(status);
    var signature = doc.createElement("div");
    signature.setAttribute("style", "font-family:'Brush Script MT','Segoe Script','Lucida Handwriting',cursive;font-size:16px;line-height:1.2;color:#050505;margin:3px 0 7px 0");
    signature.appendChild(doc.createTextNode(this.cleanExportSignatureText(record.name)));
    card.appendChild(signature);
    this.appendExportSignatureLine(doc, card, "Signer Name", record.name);
    this.appendExportSignatureLine(doc, card, "Signer Email", record.email);
    this.appendExportSignatureLine(doc, card, "IP Address", record.ipAddress);
    this.appendExportSignatureLine(doc, card, "Date & Time", record.dateLabel || record.date);
    return card;
  };

  BlockWriteAI.prototype.staticizeSingleSignatureForExport = function (box, record) {
    if (!box || !box.ownerDocument) return;
    var doc = box.ownerDocument;
    var label = this.signatureExportText(box, ".rbe-output-signature-label") || "Signature";
    while (box.firstChild) box.removeChild(box.firstChild);
    box.className = "rbe-output-signature rbe-export-static-signature";
    box.setAttribute("data-signed", "true");
    box.setAttribute("style", "margin:0 0 12px 0");
    box.appendChild(this.createStaticExportSignatureCard(doc, label, record));
  };

  BlockWriteAI.prototype.staticizeFlowSignatureForExport = function (flow, records) {
    if (!flow || !flow.ownerDocument) return;
    var doc = flow.ownerDocument;
    var title = this.signatureExportText(flow, ".rbe-output-signature-flow-title") || "Signature Flow";
    var cards = Array.prototype.slice.call(flow.querySelectorAll(".rbe-flow-signature-card"));
    while (flow.firstChild) flow.removeChild(flow.firstChild);
    flow.className = "rbe-output-signature-flow rbe-export-static-signature-flow";
    flow.setAttribute("data-complete", "true");
    flow.setAttribute("style", "margin:0 0 14px 0");
    var heading = doc.createElement("div");
    heading.setAttribute("style", "font-weight:800;font-size:15px;line-height:1.35;color:#172033;margin:0 0 8px 0");
    heading.appendChild(doc.createTextNode(title));
    flow.appendChild(heading);
    var pdfTarget = this.options && this.options.exportTarget === "pdf";
    if (pdfTarget) {
      var wrap = doc.createElement("div");
      wrap.setAttribute("style", "display:block;width:100%;page-break-inside:avoid;margin:0;padding:0");
      cards.forEach(function (card, index) {
        wrap.appendChild(this.createStaticExportSignatureCard(doc, this.signatureExportText(card, ".rbe-flow-signature-label") || "Signer " + (index + 1), records[index] || {}));
      }, this);
      flow.appendChild(wrap);
      return;
    }
    var table = doc.createElement("table");
    table.setAttribute("width", "100%");
    table.setAttribute("cellpadding", "0");
    table.setAttribute("cellspacing", "0");
    table.setAttribute("border", "0");
    table.setAttribute("style", pdfTarget ? "width:100%;border:0;margin:0;padding:0" : "width:100%;border-collapse:collapse;border:0;margin:0;padding:0");
    var row = doc.createElement("tr");
    cards.forEach(function (card, index) {
      var cell = doc.createElement("td");
      cell.setAttribute("valign", "top");
      cell.setAttribute("style", pdfTarget ? "border:0;padding:0;vertical-align:top;width:" + (100 / Math.max(cards.length, 1)) + "%" : "border:0;padding:4px;vertical-align:top");
      cell.appendChild(this.createStaticExportSignatureCard(doc, this.signatureExportText(card, ".rbe-flow-signature-label") || "Signer " + (index + 1), records[index] || {}));
      row.appendChild(cell);
    }, this);
    table.appendChild(row);
    flow.appendChild(table);
  };

  BlockWriteAI.prototype.appendWordSignatureSummaryForExport = function (doc, records) {
    if (!doc || !Array.isArray(records) || !records.length) return;
    var root = doc.body || doc.documentElement;
    if (!root) return;
    var summary = doc.createElement("div");
    summary.className = "rbe-word-signature-summary";
    summary.setAttribute("hidden", "hidden");
    summary.setAttribute("style", "display:none");
    records.forEach(function (record) {
      record = record || {};
      var name = this.cleanExportSignatureText(record.name);
      if (!name) return;
      var item = doc.createElement("span");
      item.className = "rbe-word-signature-summary-item";
      item.setAttribute("data-name", name);
      item.setAttribute("data-email", this.cleanExportSignatureText(record.email));
      item.setAttribute("data-ip", this.cleanExportSignatureText(record.ipAddress));
      item.setAttribute("data-date", this.cleanExportSignatureText(record.dateLabel || record.date));
      summary.appendChild(item);
    }, this);
    if (summary.childNodes.length) root.appendChild(summary);
  };

  BlockWriteAI.prototype.prepareSignaturesForExport = function (doc) {
    if (!doc) return;
    var editor = this;
    var summaryTarget = this.options && (this.options.exportTarget === "word" || this.options.exportTarget === "pdf");
    var summaryRecords = [];
    var signatureCounts = {};
    if (summaryTarget) {
      Array.prototype.slice.call(doc.querySelectorAll(".rbe-output-signature")).forEach(function (box) {
        var id = box.dataset.signatureId || "signature";
        signatureCounts[id] = (signatureCounts[id] || 0) + 1;
      });
    }
    var seen = {};
    Array.prototype.slice.call(doc.querySelectorAll(".rbe-output-signature")).forEach(function (box) {
      if (summaryTarget) {
        var id = box.dataset.signatureId || "signature";
        seen[id] = (seen[id] || 0) + 1;
        var storageKey = signatureCounts[id] > 1 ? id + ":" + seen[id] : id;
        var record = editor.readStoredExportSignature("blockwriteai-signature:" + storageKey);
        if (record && record.name) summaryRecords.push(record);
      }
      box.remove();
    });
    Array.prototype.slice.call(doc.querySelectorAll(".rbe-output-signature-flow")).forEach(function (flow) {
      if (summaryTarget) {
        var flowId = flow.dataset.flowId || "";
        var cards = Array.prototype.slice.call(flow.querySelectorAll(".rbe-flow-signature-card"));
        var complete = !!flowId && cards.length > 0;
        var records = [];
        cards.forEach(function (card) {
          var stepId = card.dataset.stepId || "";
          var record = editor.readStoredExportSignature("blockwriteai-signature-flow:" + flowId + ":" + stepId);
          if (!record || !record.name || !record.signatureHash) complete = false;
          records.push(record || {});
        });
        if (complete) summaryRecords = summaryRecords.concat(records);
      }
      flow.remove();
    });
    if (summaryTarget) this.appendWordSignatureSummaryForExport(doc, summaryRecords);
  };

  BlockWriteAI.prototype.applyExportSignatureRowsToDocument = function (doc, rows) {
    if (!doc || !Array.isArray(rows) || !rows.length) return;
    var editor = this;
    var singleRecords = {};
    var flowRecords = {};
    rows.forEach(function (row) {
      row = row || {};
      var payload = BlockWriteAI.normalizeExportSignaturePayload ? BlockWriteAI.normalizeExportSignaturePayload(row) : row;
      if (!payload || !payload.name) return;
      var type = row.signature_type || row.signatureType || row.type || "signature";
      var key = row.signature_key || row.signatureKey || row.key || payload.signatureStorageKey || payload.signatureId || "";
      if (type === "signature_flow" || type === "signatureFlow") {
        var flowId = row.flow_id || row.flowId || payload.flowId || "";
        var stepId = row.step_id || row.stepId || payload.stepId || "";
        if ((!flowId || !stepId) && key && key.indexOf(":") !== -1) {
          var parts = key.split(":");
          flowId = flowId || parts[0] || "";
          stepId = stepId || parts.slice(1).join(":") || "";
        }
        if (flowId && stepId) flowRecords[flowId + ":" + stepId] = payload;
        return;
      }
      if (key) singleRecords[key] = payload;
      if (payload.signatureId) singleRecords[payload.signatureId] = payload;
      if (payload.signatureStorageKey) singleRecords[payload.signatureStorageKey] = payload;
    });

    var signatureCounts = {};
    Array.prototype.slice.call(doc.querySelectorAll(".rbe-output-signature")).forEach(function (box) {
      var id = box.dataset.signatureId || "signature";
      signatureCounts[id] = (signatureCounts[id] || 0) + 1;
    });
    var seen = {};
    Array.prototype.slice.call(doc.querySelectorAll(".rbe-output-signature")).forEach(function (box) {
      var id = box.dataset.signatureId || "signature";
      seen[id] = (seen[id] || 0) + 1;
      var storageKey = signatureCounts[id] > 1 ? id + ":" + seen[id] : id;
      box.dataset.signatureStorageKey = box.dataset.signatureStorageKey || storageKey;
      var record = singleRecords[box.dataset.signatureStorageKey] || singleRecords[storageKey] || singleRecords[id];
      if (record) editor.applySingleSignatureForExport(box, record);
    });

    Array.prototype.slice.call(doc.querySelectorAll(".rbe-output-signature-flow")).forEach(function (flow) {
      var flowId = flow.dataset.flowId || "";
      if (!flowId) return;
      Array.prototype.slice.call(flow.querySelectorAll(".rbe-flow-signature-card")).forEach(function (card) {
        var record = flowRecords[flowId + ":" + (card.dataset.stepId || "")];
        if (record) editor.applyFlowSignatureForExport(card, record);
      });
    });
  };

  BlockWriteAI.prototype.buildExportPreviewRuntime = function () {
    var logoDataUrl = this.getInlineExportLogoDataUrl();
    var serverMode = !!(this.premiumOptions && this.premiumOptions.serverMode);
    var runtimeOptions = {
      serverMode: serverMode,
      licenseKey: serverMode ? "" : (this.premiumOptions && this.premiumOptions.licenseKey) || localStorageGet("blockwriteai_license_key") || localStorageGet("bwai_license_key") || (typeof window !== "undefined" && window.BlockWriteAILicenseKey) || "",
      authorizationToken: serverMode ? "" : (this.premiumOptions && this.premiumOptions.authorizationToken) || localStorageGet("bwai_authorization_token") || (typeof window !== "undefined" && window.BlockWriteAIAuthorizationToken) || "",
      sessionToken: (this.premiumOptions && this.premiumOptions.sessionToken) || "",
      verifyEndpoint: (this.premiumOptions && this.premiumOptions.verifyEndpoint) || "",
      signatureEventUrl: (this.premiumOptions && this.premiumOptions.signatureEventEndpoint) || "",
      signatureFlowEntitled: this.isPremiumFeatureEnabled("signatureFlow")
    };
    runtimeOptions.member = this.getLicenseMemberInfo();
    try {
      runtimeOptions.sessionToken = runtimeOptions.sessionToken || localStorage.getItem("bwai_session_token") || localStorage.getItem("blockwriteai_session_token") || "";
    } catch (error) {
      // Runtime falls back to in-frame storage when parent storage is unavailable.
    }
    var script = function (logoSrc, runtimeOptions) {
      "use strict";
      runtimeOptions = runtimeOptions || {};
      var signaturePrefix = "blockwriteai-signature:";
      var flowPrefix = "blockwriteai-signature-flow:";
      var signatureEventUrl = runtimeOptions.signatureEventUrl || window.BlockWriteAISignatureEventUrl || "/blockwriteai-platform/api/signature_event.php";
      var root = document;
      var activeAnnotation = null;

      function clean(value) {
        return String(value == null ? "" : value).trim();
      }

      function runtimeMember() {
        var member = runtimeOptions.member && typeof runtimeOptions.member === "object" ? runtimeOptions.member : {};
        var access = member.feature_access && typeof member.feature_access === "object" ? member.feature_access : {};
        return {
          name: clean(member.name || member.full_name || member.member_name || member.email || ""),
          role: clean(member.role || member.member_role || member.plan_role || member.type || ""),
          feature_access: access
        };
      }

      function memberFeatureEnabled(feature) {
        var access = runtimeMember().feature_access || {};
        var value = access[feature];
        return value === true || value === "true" || value === 1 || value === "1";
      }

      function truthyFeature(value) {
        return value === true || value === "true" || value === 1 || value === "1";
      }

      function applyRuntimeLicense(body) {
        if (!body || typeof body !== "object" || body.ok === false || body.valid === false) return;
        var features = body.features && typeof body.features === "object" ? body.features : {};
        var member = body.member && typeof body.member === "object" ? body.member : null;
        var access = member && member.feature_access && typeof member.feature_access === "object" ? member.feature_access : {};
        if (truthyFeature(features.signature_flow) || truthyFeature(access.signature_flow)) runtimeOptions.signatureFlowEntitled = true;
        if (member) runtimeOptions.member = member;
        initAnnotations();
      }

      function verifyRuntimeLicense() {
        var endpoint = clean(runtimeOptions.verifyEndpoint || "");
        var serverMode = !!runtimeOptions.serverMode;
        var authorizationToken = serverMode ? "" : clean(runtimeOptions.authorizationToken || readSessionValue("bwai_authorization_token") || window.BlockWriteAIAuthorizationToken);
        var licenseKey = serverMode ? "" : clean(runtimeOptions.licenseKey || readSessionValue("blockwriteai_license_key") || readSessionValue("bwai_license_key") || window.BlockWriteAILicenseKey);
        if (!endpoint || (!serverMode && !authorizationToken && !licenseKey) || typeof fetch !== "function") return;
        fetch(endpoint, {
          method: "POST",
          headers: { "Content-Type": "application/json", Accept: "application/json" },
          body: JSON.stringify({
            authorization_token: serverMode ? undefined : authorizationToken || undefined,
            license_key: serverMode || authorizationToken ? undefined : licenseKey,
            session_token: clean(runtimeOptions.sessionToken || readSessionValue("bwai_session_token") || readSessionValue("blockwriteai_session_token")),
            domain: window.location && window.location.origin ? window.location.origin : ""
          })
        })
          .then(function (response) {
            return response.json().catch(function () { return {}; }).then(function (body) {
              return response.ok ? body : null;
            });
          })
          .then(applyRuntimeLicense)
          .catch(function () {
            // Keep signature-card fallback when the license service is unavailable.
          });
      }

      function initTableSorting() {
        var tables = Array.prototype.slice.call(root.querySelectorAll(".rbe-output-filterable-table"));
        if (!tables.length) return;

        function columnText(row, index) {
          var cell = row && row.cells ? row.cells[index] : null;
          return clean(cell ? cell.textContent : "");
        }

        function parseSortableValue(value) {
          var text = clean(value);
          var numeric = text.replace(/[%,$\u20b9\s]/g, "");
          if (/^-?\d+(?:\.\d+)?$/.test(numeric)) {
            return { type: "number", value: Number(numeric) };
          }
          return { type: "text", value: text.toLowerCase() };
        }

        function compareValues(a, b, direction) {
          if (a.type === "number" && b.type === "number") {
            return direction * (a.value - b.value);
          }
          return direction * String(a.value).localeCompare(String(b.value), undefined, { numeric: true, sensitivity: "base" });
        }

        tables.forEach(function (table) {
          if (table.dataset.sortReady === "true") return;
          table.dataset.sortReady = "true";
          var headerRow = table.querySelector("thead tr");
          var tbody = table.tBodies && table.tBodies[0];
          if (!headerRow || !tbody) return;
          Array.prototype.slice.call(headerRow.cells).forEach(function (cell, columnIndex) {
            var label = clean(cell.textContent) || "Column " + (columnIndex + 1);
            var sortButton = document.createElement("button");
            var labelSpan = document.createElement("span");
            var iconSpan = document.createElement("span");
            sortButton.type = "button";
            sortButton.className = "rbe-output-table-sort-button";
            sortButton.dataset.column = String(columnIndex);
            sortButton.setAttribute("aria-label", "Sort by " + label);
            sortButton.title = "Sort by " + label;
            labelSpan.className = "rbe-output-table-sort-label";
            while (cell.firstChild) labelSpan.appendChild(cell.firstChild);
            iconSpan.className = "rbe-output-table-sort-icon";
            iconSpan.setAttribute("aria-hidden", "true");
            sortButton.appendChild(labelSpan);
            sortButton.appendChild(iconSpan);
            cell.appendChild(sortButton);
            sortButton.addEventListener("click", function (event) {
              event.preventDefault();
              var nextDirection = cell.dataset.sortDir === "asc" ? "desc" : "asc";
              var direction = nextDirection === "asc" ? 1 : -1;
              var rows = Array.prototype.slice.call(tbody.rows);
              Array.prototype.slice.call(headerRow.cells).forEach(function (headerCell) {
                if (headerCell !== cell) headerCell.removeAttribute("data-sort-dir");
              });
              rows.sort(function (rowA, rowB) {
                return compareValues(parseSortableValue(columnText(rowA, columnIndex)), parseSortableValue(columnText(rowB, columnIndex)), direction);
              });
              rows.forEach(function (row) { tbody.appendChild(row); });
              cell.dataset.sortDir = nextDirection;
            });
          });
        });
      }

      function initTableFilters() {
        var tables = Array.prototype.slice.call(root.querySelectorAll(".rbe-output-filterable-table"));
        if (!tables.length) return;

        function cellText(row, index) {
          var cell = row && row.cells ? row.cells[index] : null;
          return clean(cell ? cell.textContent : "");
        }

        function uniqueColumnValues(table, columnIndex) {
          var bodyRows = Array.prototype.slice.call(table.querySelectorAll("tbody tr"));
          var values = [];
          var seen = {};
          bodyRows.forEach(function (row) {
            var value = cellText(row, columnIndex);
            var key = value.toLowerCase();
            if (seen[key]) return;
            seen[key] = true;
            values.push(value);
          });
          return values.sort(function (a, b) {
            return a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" });
          });
        }

        function selectedValues(panel) {
          return Array.prototype.slice.call(panel.querySelectorAll(".rbe-output-table-filter-option input"))
            .filter(function (input) { return input.checked; })
            .map(function (input) { return input.value; });
        }

        function applyTableFilter(table) {
          var panels = Array.prototype.slice.call(table.querySelectorAll(".rbe-output-table-filter-panel"));
          var bodyRows = Array.prototype.slice.call(table.querySelectorAll("tbody tr"));
          var filters = panels.map(function (panel) {
            return {
              column: Number(panel.dataset.column || 0),
              values: selectedValues(panel)
            };
          });
          bodyRows.forEach(function (row) {
            var visible = filters.every(function (filter) {
              return filter.values.length && filter.values.indexOf(cellText(row, filter.column)) >= 0;
            });
            row.hidden = !visible;
          });
          panels.forEach(function (panel) {
            var active = selectedValues(panel).length !== panel.querySelectorAll(".rbe-output-table-filter-option input").length;
            var button = table.querySelector(".rbe-output-table-filter-button[data-column='" + panel.dataset.column + "']");
            if (button) button.classList.toggle("is-filtered", active);
          });
        }

        function closePanels(except) {
          Array.prototype.slice.call(root.querySelectorAll(".rbe-output-table-filter-panel.is-open")).forEach(function (panel) {
            if (panel !== except) {
              panel.classList.remove("is-open");
              panel.style.left = "";
              panel.style.top = "";
              panel.style.right = "";
              panel.style.position = "";
            }
          });
        }

        function positionFilterPanel(panel, button) {
          if (!panel || !button) return;
          panel.style.position = "fixed";
          panel.style.right = "auto";
          panel.style.zIndex = "2147482500";
          var rect = button.getBoundingClientRect();
          var width = panel.offsetWidth || 236;
          var height = panel.offsetHeight || 260;
          var margin = 8;
          var viewportWidth = root.documentElement ? root.documentElement.clientWidth : window.innerWidth;
          var viewportHeight = root.documentElement ? root.documentElement.clientHeight : window.innerHeight;
          var left = Math.max(margin, Math.min(rect.right - width, viewportWidth - width - margin));
          var top = rect.bottom + 4;
          if (top + height > viewportHeight - margin) top = Math.max(margin, rect.top - height - 4);
          panel.style.left = left + "px";
          panel.style.top = top + "px";
        }

        tables.forEach(function (table) {
          if (table.dataset.filterReady === "true") return;
          table.dataset.filterReady = "true";
          var headerRow = table.querySelector("thead tr");
          if (!headerRow) return;
          Array.prototype.slice.call(headerRow.cells).forEach(function (cell, columnIndex) {
            var values = uniqueColumnValues(table, columnIndex);
            if (!values.length) return;
            var label = clean(cell.textContent) || "Column " + (columnIndex + 1);
            var safeLabel = label.replace(/"/g, "&quot;");
            var control = document.createElement("span");
            control.className = "rbe-output-table-filter";
            control.innerHTML = '<button type="button" class="rbe-output-table-filter-button" data-column="' + columnIndex + '" aria-label="Filter ' + safeLabel + '" title="Filter ' + safeLabel + '"><span aria-hidden="true">▾</span></button>' +
              '<span class="rbe-output-table-filter-panel" data-column="' + columnIndex + '">' +
              '<input class="rbe-output-table-filter-search" type="search" placeholder="Search" aria-label="Search ' + safeLabel + '">' +
              '<label class="rbe-output-table-filter-all"><input type="checkbox" checked> (Select All)</label>' +
              '<span class="rbe-output-table-filter-list"></span>' +
              '<span class="rbe-output-table-filter-actions"><button type="button" data-action="apply">OK</button><button type="button" data-action="cancel">Cancel</button></span>' +
              '</span>';
            var list = control.querySelector(".rbe-output-table-filter-list");
            values.forEach(function (value) {
              var option = document.createElement("label");
              option.className = "rbe-output-table-filter-option";
              option.innerHTML = '<input type="checkbox" checked> <span></span>';
              option.querySelector("input").value = value;
              option.querySelector("span").textContent = value || "(Blank)";
              list.appendChild(option);
            });
            cell.appendChild(control);
          });
        });

        root.addEventListener("click", function (event) {
          var button = event.target.closest(".rbe-output-table-filter-button");
          if (button) {
            event.preventDefault();
            var panel = button.parentNode && button.parentNode.querySelector(".rbe-output-table-filter-panel");
            if (!panel) return;
            var open = panel.classList.contains("is-open");
            closePanels(panel);
            panel.classList.toggle("is-open", !open);
            if (!open) positionFilterPanel(panel, button);
            var search = panel.querySelector(".rbe-output-table-filter-search");
            if (!open && search) search.focus();
            return;
          }
          var panelAction = event.target.closest(".rbe-output-table-filter-actions button");
          if (panelAction) {
            event.preventDefault();
            var panelForAction = panelAction.closest(".rbe-output-table-filter-panel");
            var table = panelForAction && panelForAction.closest("table");
            if (panelAction.dataset.action === "apply" && table) applyTableFilter(table);
            if (panelForAction) {
              panelForAction.classList.remove("is-open");
              panelForAction.style.left = "";
              panelForAction.style.top = "";
              panelForAction.style.right = "";
              panelForAction.style.position = "";
            }
            return;
          }
          if (!event.target.closest(".rbe-output-table-filter")) closePanels(null);
        });

        root.addEventListener("input", function (event) {
          var search = event.target.closest(".rbe-output-table-filter-search");
          if (!search) return;
          var panel = search.closest(".rbe-output-table-filter-panel");
          var query = clean(search.value).toLowerCase();
          Array.prototype.slice.call(panel.querySelectorAll(".rbe-output-table-filter-option")).forEach(function (option) {
            option.hidden = query && option.textContent.toLowerCase().indexOf(query) < 0;
          });
        });

        root.addEventListener("change", function (event) {
          var all = event.target.closest(".rbe-output-table-filter-all input");
          var optionInput = event.target.closest(".rbe-output-table-filter-option input");
          if (all) {
            var panel = all.closest(".rbe-output-table-filter-panel");
            Array.prototype.slice.call(panel.querySelectorAll(".rbe-output-table-filter-option input")).forEach(function (input) {
              if (!input.closest(".rbe-output-table-filter-option").hidden) input.checked = all.checked;
            });
            return;
          }
          if (optionInput) {
            var optionPanel = optionInput.closest(".rbe-output-table-filter-panel");
            var options = Array.prototype.slice.call(optionPanel.querySelectorAll(".rbe-output-table-filter-option input"));
            var checked = options.filter(function (input) { return input.checked; }).length;
            var allInput = optionPanel.querySelector(".rbe-output-table-filter-all input");
            if (allInput) allInput.checked = checked === options.length;
          }
        });
      }

      function tooltipText(dateLabel, member) {
        member = member || runtimeMember();
        var lines = [];
        if (member.name) lines.push("Name : " + member.name);
        if (member.role) lines.push("Role : " + member.role);
        if (dateLabel) lines.push("Date & Time : " + dateLabel);
        return lines.join("\n");
      }

      function normalizeStatus(value) {
        value = clean(value).toLowerCase();
        return value === "resolved" || value === "declined" ? value : "pending";
      }

      function statusLabel(value) {
        value = normalizeStatus(value);
        return value === "resolved" ? "Accepted" : value === "declined" ? "Rejected" : "Pending";
      }

      function parseThread(value) {
        try {
          var parsed = JSON.parse(value || "[]");
          return Array.isArray(parsed) ? parsed.filter(function (item) { return item && item.text; }) : [];
        } catch (error) {
          return [];
        }
      }

      function serializeThread(list) {
        return JSON.stringify(Array.isArray(list) ? list : []);
      }

        function buildThreadItem(node, canReply) {
          var item = document.createElement("span");
          var head = document.createElement("span");
          var label = document.createElement("span");
          var meta = document.createElement("span");
          var replyToggle = document.createElement("button");
          var dateChip = document.createElement("span");
          var dateIcon = document.createElement("span");
          var dateTooltip = document.createElement("span");
          var text = document.createElement("span");
          item.className = "rbe-output-annotation-thread-item";
          head.className = "rbe-output-annotation-thread-head";
          label.className = "rbe-output-annotation-thread-label";
          label.textContent = "Reply";
          meta.className = "rbe-output-annotation-thread-meta";
          dateChip.className = "rbe-output-annotation-date-chip";
          dateIcon.className = "rbe-output-annotation-date-icon";
          dateIcon.setAttribute("aria-hidden", "true");
          dateIcon.textContent = "\u{1F553}";
          dateTooltip.className = "rbe-output-annotation-date-tooltip";
          dateTooltip.textContent = tooltipText(formatDate(new Date(node.date || Date.now())), { name: clean(node.memberName || runtimeMember().name), role: clean(node.memberRole || runtimeMember().role) });
          text.className = "rbe-output-annotation-thread-text";
          text.textContent = clean(node.text || "");
          replyToggle.type = "button";
          replyToggle.className = "rbe-output-annotation-reply-toggle rbe-output-annotation-reply-toggle--icon";
          replyToggle.title = "Reply";
          replyToggle.innerHTML = svgIcon("reply", "Reply");
          replyToggle.hidden = !canReply;
          dateChip.appendChild(dateIcon);
          dateChip.appendChild(dateTooltip);
          meta.appendChild(replyToggle);
          meta.appendChild(dateChip);
          head.appendChild(label);
          head.appendChild(meta);
          item.appendChild(head);
          item.appendChild(text);
          return item;
        }

      function pad(value) {
        return String(value).padStart(2, "0");
      }

      function formatDate(date) {
        return date.toLocaleString();
      }

      function readStored(key) {
        try {
          return JSON.parse(localStorage.getItem(key) || "null");
        } catch (error) {
          return null;
        }
      }

      function saveStored(key, record) {
        try {
          localStorage.setItem(key, JSON.stringify(record));
        } catch (error) {
          // Exported HTML can still sign in-memory when storage is unavailable.
        }
      }

      function readSessionValue(key) {
        try {
          return localStorage.getItem(key) || sessionStorage.getItem(key) || "";
        } catch (error) {
          return "";
        }
      }

      function normalizeLookupValue(value) {
        return clean(value).toLowerCase().replace(/\s+/g, " ");
      }

      function signatureMatchesLookup(signature, requestedValue) {
        var requested = normalizeLookupValue(requestedValue);
        if (!signature || !requested) return false;
        return normalizeLookupValue(signature.signer_name) === requested || normalizeLookupValue(signature.signer_email) === requested;
      }

      function lookupFlowSignature(name) {
        var serverMode = !!runtimeOptions.serverMode;
        var authorizationToken = serverMode ? "" : clean(runtimeOptions.authorizationToken || readSessionValue("bwai_authorization_token") || window.BlockWriteAIAuthorizationToken);
        var licenseKey = serverMode ? "" : clean(runtimeOptions.licenseKey || readSessionValue("blockwriteai_license_key") || readSessionValue("bwai_license_key") || window.BlockWriteAILicenseKey);
        if ((!serverMode && !authorizationToken && !licenseKey) || !name || typeof fetch !== "function") return Promise.resolve(null);
        var payload = {
          action: "lookup",
          authorization_token: serverMode ? undefined : authorizationToken || undefined,
          license_key: serverMode || authorizationToken ? undefined : licenseKey,
          match_signer_value: name,
          signer_name: name
        };
        var sessionToken = runtimeOptions.sessionToken || readSessionValue("bwai_session_token") || readSessionValue("blockwriteai_session_token");
        if (sessionToken) payload.session_token = sessionToken;
        return fetch(signatureEventUrl, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload)
        })
          .then(function (response) { return response.ok ? response.json() : null; })
          .then(function (data) {
            if (!data || data.ok === false || !data.signature || !signatureMatchesLookup(data.signature, name)) return null;
            return {
              name: data.signature.signer_name || name,
              email: data.signature.signer_email || "",
              role: data.signature.signer_role || "",
              ipAddress: data.signature.ip_address || "",
              signatureHash: data.signature.signature_hash || "",
              date: data.signature.created_at || new Date().toISOString(),
              dateLabel: data.signature.created_at ? formatDate(new Date(data.signature.created_at)) : formatDate(new Date())
            };
          })
          .catch(function () { return null; });
      }

      function showFlowError(card, input) {
        card.classList.add("has-error");
        var status = card.querySelector(".rbe-flow-signature-status");
        if (status) status.textContent = "No verified signature found.";
        if (input) input.focus();
      }

      function flowHasSignatureHash(flow, card, signatureHash) {
        signatureHash = clean(signatureHash).toLowerCase();
        if (!flow || !signatureHash) return false;
        return Array.prototype.slice.call(flow.querySelectorAll(".rbe-flow-signature-card")).some(function (item) {
          return item !== card && item.dataset.signed === "true" && clean(item.dataset.signatureHash).toLowerCase() === signatureHash;
        });
      }

      function hasSignatureFlowApproval() {
        if (runtimeOptions.signatureFlowEntitled === true || memberFeatureEnabled("signature_flow")) return true;
        return Array.prototype.slice.call(root.querySelectorAll(".rbe-output-signature-flow .rbe-flow-signature-card")).some(function (card) {
          return card.dataset.signed === "true" && !!clean(card.dataset.signatureHash || "");
        });
      }

      function showFlowDuplicateError(card, input) {
        card.classList.add("has-error");
        var status = card.querySelector(".rbe-flow-signature-status");
        if (status) status.textContent = "Signature already used in this flow.";
        if (input) input.focus();
      }

      function setLogoFallback() {
        Array.prototype.slice.call(root.querySelectorAll(".rbe-signature-watermark,.rbe-flow-signature-watermark")).forEach(function (image) {
          image.src = logoSrc;
        });
      }

      function applySingleSignature(box, record) {
        if (!box || !record || !record.name) return;
        var unsigned = box.querySelector(".rbe-signature-unsigned");
        var signed = box.querySelector(".rbe-signature-signed");
        var input = box.querySelector(".rbe-signature-input");
        var name = box.querySelector(".rbe-signature-name");
        var by = box.querySelector(".rbe-signature-by-name");
        var time = box.querySelector(".rbe-signature-date");
        if (input) {
          input.value = record.name;
          input.readOnly = true;
        }
        if (name) name.textContent = record.name;
        if (by) by.textContent = record.name;
        if (time) {
          time.textContent = record.dateLabel || formatDate(new Date(record.date || Date.now()));
          if (record.date) time.setAttribute("datetime", record.date);
        }
        if (unsigned) unsigned.hidden = true;
        if (signed) signed.hidden = false;
        box.dataset.signed = "true";
        box.classList.remove("has-error");
      }

      function signSingle(box) {
        var input = box.querySelector(".rbe-signature-input");
        var name = clean(input && input.value);
        if (!name) {
          box.classList.add("has-error");
          if (input) input.focus();
          return;
        }
        var now = new Date();
        var record = { name: name, date: now.toISOString(), dateLabel: formatDate(now) };
        saveStored(signaturePrefix + (box.dataset.signatureStorageKey || box.dataset.signatureId || ""), record);
        applySingleSignature(box, record);
        try {
          box.dispatchEvent(new CustomEvent("blockwriteai:signature-signed", { bubbles: true, detail: record }));
        } catch (error) {
        }
      }

      function applyFlowStep(card, record) {
        if (!card || !record || !record.name) return;
        var unsigned = card.querySelector(".rbe-flow-signature-unsigned");
        var signed = card.querySelector(".rbe-flow-signature-signed");
        var input = card.querySelector(".rbe-flow-signature-input");
        var status = card.querySelector(".rbe-flow-signature-status");
        var name = card.querySelector(".rbe-flow-signature-name");
        var by = card.querySelector(".rbe-flow-signature-by-name");
        var email = card.querySelector(".rbe-flow-signature-email");
        var ip = card.querySelector(".rbe-flow-signature-ip");
        var time = card.querySelector(".rbe-flow-signature-date");
        card.classList.add("is-signed");
        card.classList.remove("is-locked", "has-error");
        card.dataset.signed = "true";
        card.dataset.signatureHash = record.signatureHash || "";
        if (input) {
          input.value = record.name;
          input.readOnly = true;
        }
        if (status) status.textContent = "Signed";
        if (name) name.textContent = record.name;
        if (by) by.textContent = record.name;
        if (email) email.textContent = record.email || "";
        if (ip) ip.textContent = record.ipAddress || "";
        if (time) {
          time.textContent = record.dateLabel || formatDate(new Date(record.date || Date.now()));
          if (record.date) time.setAttribute("datetime", record.date);
        }
        if (unsigned) unsigned.hidden = true;
        if (signed) signed.hidden = false;
      }

      function updateFlow(flow) {
        var cards = Array.prototype.slice.call(flow.querySelectorAll(".rbe-flow-signature-card"));
        var connectors = Array.prototype.slice.call(flow.querySelectorAll(".rbe-flow-signature-connector"));
        var allSigned = cards.length > 0;
        var previousSigned = true;
        cards.forEach(function (card, index) {
          var isSigned = card.dataset.signed === "true";
          if (!isSigned) allSigned = false;
          var unlocked = previousSigned || isSigned;
          var input = card.querySelector(".rbe-flow-signature-input");
          var button = card.querySelector(".rbe-flow-signature-apply");
          var status = card.querySelector(".rbe-flow-signature-status");
          card.classList.toggle("is-locked", !unlocked);
          card.classList.toggle("is-active", unlocked && !isSigned);
          if (!isSigned) {
            if (input) input.disabled = !unlocked;
            if (button) button.disabled = !unlocked;
            if (status) status.textContent = unlocked ? "Ready to sign" : "Pending";
          }
          if (connectors[index]) {
            connectors[index].classList.toggle("is-complete", isSigned);
            connectors[index].classList.toggle("is-pending", !isSigned);
          }
          previousSigned = isSigned;
        });
        flow.dataset.complete = allSigned ? "true" : "false";
      }

      function signFlow(card) {
        if (!card || card.classList.contains("is-locked")) return;
        var input = card.querySelector(".rbe-flow-signature-input");
        var name = clean(input && input.value);
        if (!name) {
          card.classList.add("has-error");
          if (input) input.focus();
          return;
        }
        var flow = card.closest(".rbe-output-signature-flow");
        lookupFlowSignature(name).then(function (record) {
          if (!record) {
            showFlowError(card, input);
            return;
          }
          if (flowHasSignatureHash(flow, card, record.signatureHash)) {
            showFlowDuplicateError(card, input);
            return;
          }
          saveStored(flowPrefix + (flow ? flow.dataset.flowId || "" : "") + ":" + (card.dataset.stepId || ""), record);
          applyFlowStep(card, record);
          if (flow) updateFlow(flow);
          initAnnotations();
          try {
            card.dispatchEvent(new CustomEvent("blockwriteai:signature-flow-signed", { bubbles: true, detail: record }));
          } catch (error) {
          }
        });
      }

      function appendAnnotationHistory(annotation, message) {
        var history = annotation && annotation.querySelector(".rbe-output-annotation-history");
        if (!history || !message) return;
        var item = document.createElement("span");
        item.className = "rbe-output-annotation-history-item";
        item.textContent = message + " - " + formatDate(new Date());
        history.innerHTML = "";
        history.appendChild(item);
      }

      function closeActiveAnnotation(except) {
        if (activeAnnotation && activeAnnotation !== except) activeAnnotation.classList.remove("is-open");
        if (!except || activeAnnotation !== except) activeAnnotation = except || null;
      }

      function positionAnnotationCard(annotation) {
        var card = annotation && annotation.querySelector(".rbe-output-annotation-card");
        if (!annotation || !card) return;
        var annotationRect = annotation.getBoundingClientRect();
        var cardRect = card.getBoundingClientRect();
        var viewportWidth = root.documentElement ? root.documentElement.clientWidth : window.innerWidth;
        var viewportHeight = root.documentElement ? root.documentElement.clientHeight : window.innerHeight;
        var margin = 14;
        var cardWidth = cardRect.width || Math.min(540, viewportWidth - margin * 2);
        var cardHeight = cardRect.height || 0;
        var targetLeft = annotationRect.left + annotationRect.width / 2 - cardWidth / 2;
        var clampedLeft = Math.max(margin, Math.min(targetLeft, viewportWidth - cardWidth - margin));
        var relativeLeft = clampedLeft - annotationRect.left;
        var spaceBelow = viewportHeight - annotationRect.bottom;
        var spaceAbove = annotationRect.top;
        annotation.style.setProperty("--rbe-annotation-card-left", Math.round(relativeLeft) + "px");
        annotation.style.setProperty("--rbe-annotation-card-transform", "none");
        annotation.classList.toggle("is-card-above", cardHeight > 0 && spaceBelow < cardHeight + 22 && spaceAbove > spaceBelow);
      }

      function pinAnnotation(annotation) {
        if (!annotation) return;
        closeActiveAnnotation(annotation);
        annotation.classList.add("is-open");
        activeAnnotation = annotation;
        positionAnnotationCard(annotation);
      }

      function broadcastAnnotationChange(payload) {
        try {
          document.dispatchEvent(new CustomEvent("blockwriteai:preview-comment-change", { detail: payload }));
        } catch (error) {
        }
        if (!window.parent || window.parent === window) return;
        try {
          window.parent.postMessage(payload, "*");
        } catch (error) {
        }
      }

      function emitAnnotationUpdate(annotation) {
        if (!annotation) return;
        broadcastAnnotationChange({
          source: "BlockWriteAIExportPreviewComment",
          action: "state",
          commentId: annotation.dataset.commentId || "",
          status: normalizeStatus(annotation.dataset.commentStatus || "pending"),
          reason: annotation.dataset.commentReason || "",
          updatedAt: annotation.dataset.commentUpdated || new Date().toISOString(),
          memberName: annotation.dataset.commentUpdatedMemberName || "",
          memberRole: annotation.dataset.commentUpdatedMemberRole || ""
        });
      }

      function emitAnnotationReply(annotation, replyText) {
        if (!annotation) return;
        broadcastAnnotationChange({
          source: "BlockWriteAIExportPreviewComment",
          action: "reply",
          commentId: annotation.dataset.commentId || "",
          replyText: clean(replyText || ""),
          replyDate: new Date().toISOString(),
          memberName: runtimeMember().name,
          memberRole: runtimeMember().role
        });
      }

      function isApprovalReply(value) {
        var text = clean(value || "").toLowerCase().replace(/[_-]+/g, " ");
        var words = text.match(/[a-z0-9]+/g) || [];
        var approvedIndex = words.findIndex(function (word) {
          return word === "approved";
        });
        if (approvedIndex < 0) return false;
        return words.slice(0, approvedIndex).indexOf("not") === -1;
      }

      function setReplyToggleHidden(button, hidden) {
        if (!button) return;
        button.hidden = !!hidden;
        button.style.display = hidden ? "none" : "";
      }

      function setActionHidden(button, hidden) {
        if (!button) return;
        button.hidden = !!hidden;
        button.style.display = hidden ? "none" : "";
      }

      function syncAnnotationUI(annotation, editing) {
        if (!annotation) return;
        var status = normalizeStatus(annotation.dataset.commentStatus || "pending");
        var canSignedUserAct = hasSignatureFlowApproval();
        var reason = annotation.dataset.commentReason || "";
        var createdAt = annotation.dataset.commentDate || "";
        var updatedAt = annotation.dataset.commentUpdated || "";
        var card = annotation.querySelector(".rbe-output-annotation-card");
        var badge = annotation.querySelector(".rbe-output-annotation-badge");
        var stateText = annotation.querySelector(".rbe-output-annotation-state-text");
        var stateDate = annotation.querySelector(".rbe-output-annotation-date");
        var rejectBlock = annotation.querySelector(".rbe-output-annotation-reject-block");
        var rejectRow = annotation.querySelector(".rbe-output-annotation-reject-row");
        var rejectText = annotation.querySelector(".rbe-output-annotation-reject-text");
        var rejectDate = annotation.querySelector(".rbe-output-annotation-reject-date");
        var thread = parseThread(annotation.dataset.commentThread || "");
        var rejectReplyToggle = annotation.querySelector(".rbe-output-annotation-reject-meta > .rbe-output-annotation-reply-toggle");
        var threadReplyToggles = Array.prototype.slice.call(annotation.querySelectorAll(".rbe-output-annotation-thread-item .rbe-output-annotation-reply-toggle"));
        var replyForm = annotation.querySelector(".rbe-output-annotation-reply-form");
        var replyInput = annotation.querySelector(".rbe-output-annotation-reply-input");
        var threadBlock = annotation.querySelector(".rbe-output-annotation-thread-block");
        var editButton = annotation.querySelector(".rbe-output-annotation-edit");
        var actions = annotation.querySelector(".rbe-output-annotation-actions");
        var resolveButton = annotation.querySelector(".rbe-output-annotation-resolve");
        var declineButton = annotation.querySelector(".rbe-output-annotation-decline");
        var cancelButton = annotation.querySelector(".rbe-output-annotation-cancel-edit");
        var form = annotation.querySelector(".rbe-output-annotation-decline-form");
        var reply = annotation.querySelector(".rbe-output-annotation-reply");
        annotation.classList.remove("is-resolved", "is-declined");
        if (status === "resolved") annotation.classList.add("is-resolved");
        if (status === "declined") annotation.classList.add("is-declined");
        if (card) {
          card.classList.remove("is-resolved", "is-declined", "is-pending");
          card.classList.add(status === "resolved" ? "is-resolved" : status === "declined" ? "is-declined" : "is-pending");
        }
        annotation.dataset.commentEditing = editing ? "true" : "false";
        if (badge) {
          badge.textContent = statusLabel(status);
          badge.className = "rbe-output-annotation-badge rbe-output-annotation-badge-" + status;
        }
        if (stateText) stateText.textContent = statusLabel(status);
        if (stateDate) stateDate.textContent = formatDate(new Date((status === "pending" ? createdAt : updatedAt) || createdAt || Date.now()));
        var createdMember = { name: clean(annotation.dataset.commentMemberName || runtimeMember().name), role: clean(annotation.dataset.commentMemberRole || runtimeMember().role) };
        var updatedMember = { name: clean(annotation.dataset.commentUpdatedMemberName || createdMember.name), role: clean(annotation.dataset.commentUpdatedMemberRole || createdMember.role) };
        var tooltips = Array.prototype.slice.call(annotation.querySelectorAll(".rbe-output-annotation-date-tooltip"));
        if (tooltips[0]) tooltips[0].textContent = tooltipText(formatDate(new Date((status === "pending" ? createdAt : updatedAt) || createdAt || Date.now())), status === "pending" ? createdMember : updatedMember);
        if (tooltips[1]) tooltips[1].textContent = tooltipText(formatDate(new Date(updatedAt || createdAt || Date.now())), updatedMember);
        setReplyToggleHidden(rejectReplyToggle, !canSignedUserAct || status !== "declined" || thread.length > 0);
        threadReplyToggles.forEach(function (button, index) {
          setReplyToggleHidden(button, !canSignedUserAct || status !== "declined" || index !== 0);
        });
        if (editButton) editButton.hidden = status === "pending" || editing;
        if (actions) actions.hidden = status !== "pending";
        setActionHidden(resolveButton, !canSignedUserAct || (editing && status !== "pending"));
        if (declineButton) declineButton.hidden = editing && status !== "pending";
        if (cancelButton) cancelButton.hidden = !(editing && status !== "pending");
        if (rejectBlock) rejectBlock.hidden = !(editing && status === "pending") && (status !== "declined" || !reason);
        if (rejectRow) rejectRow.hidden = editing && status === "declined";
        if (rejectText) rejectText.textContent = reason;
        if (rejectDate) rejectDate.textContent = formatDate(new Date(updatedAt || createdAt || Date.now()));
        if (reply) reply.value = reason;
        if (form) form.hidden = !(editing && status === "declined");
        if (replyForm && editing) replyForm.hidden = true;
        if (replyInput && editing) replyInput.value = "";
        if (threadBlock) threadBlock.hidden = thread.length === 0;
        if (annotation.classList.contains("is-open")) positionAnnotationCard(annotation);
      }

      function setAnnotationState(annotation, state, reason) {
        if (!annotation) return;
        state = normalizeStatus(state);
        reason = state === "declined" ? clean(reason) : "";
        annotation.dataset.commentStatus = state;
        annotation.dataset.commentReason = reason;
        annotation.dataset.commentUpdated = new Date().toISOString();
        var member = runtimeMember();
        annotation.dataset.commentUpdatedMemberName = member.name;
        annotation.dataset.commentUpdatedMemberRole = member.role;
        annotation.classList.remove("is-resolved", "is-declined", "has-error");
        annotation.classList.add("is-open");
        appendAnnotationHistory(annotation, state === "declined" ? "Rejected" + (reason ? ": " + reason : "") : "Confirmed");
        syncAnnotationUI(annotation, false);
        emitAnnotationUpdate(annotation);
      }

      function toggleAnnotationReply(annotation) {
        if (!annotation) return;
        if (!hasSignatureFlowApproval()) return;
        pinAnnotation(annotation);
        var form = annotation.querySelector(".rbe-output-annotation-reply-form");
        var input = annotation.querySelector(".rbe-output-annotation-reply-input");
        if (!form) return;
        form.hidden = !form.hidden;
        if (!form.hidden && input) input.focus();
      }

      function saveAnnotationReply(annotation) {
        if (!annotation) return;
        if (!hasSignatureFlowApproval()) {
          syncAnnotationUI(annotation, false);
          return;
        }
        var input = annotation.querySelector(".rbe-output-annotation-reply-input");
        var text = clean(input && input.value);
        if (!text) {
          if (input) input.focus();
          return;
        }
        if (isApprovalReply(text)) {
          if (input) input.value = "";
          var approvalForm = annotation.querySelector(".rbe-output-annotation-reply-form");
          if (approvalForm) approvalForm.hidden = true;
          setAnnotationState(annotation, "resolved");
          return;
        }
        var list = annotation.querySelector(".rbe-output-annotation-thread-list");
        var block = annotation.querySelector(".rbe-output-annotation-thread-block");
        var thread = parseThread(annotation.dataset.commentThread || "");
        var member = runtimeMember();
        var node = { text: text, date: new Date().toISOString(), memberName: member.name, memberRole: member.role };
        thread.push(node);
        annotation.dataset.commentThread = serializeThread(thread);
        if (block) block.hidden = false;
        if (list) list.prepend(buildThreadItem(node, normalizeStatus(annotation.dataset.commentStatus || "pending") === "declined"));
        if (input) input.value = "";
        var form = annotation.querySelector(".rbe-output-annotation-reply-form");
        if (form) form.hidden = true;
        syncAnnotationUI(annotation, false);
        emitAnnotationReply(annotation, text);
      }

      function startAnnotationEdit(annotation) {
        if (!annotation) return;
        pinAnnotation(annotation);
        annotation.classList.remove("has-error");
        syncAnnotationUI(annotation, true);
      }

      function cancelAnnotationEdit(annotation) {
        if (!annotation) return;
        annotation.classList.remove("has-error");
        syncAnnotationUI(annotation, false);
      }

      function toggleAnnotationDecline(annotation) {
        if (!annotation) return;
        startAnnotationEdit(annotation);
        var reply = annotation.querySelector(".rbe-output-annotation-reply");
        var form = annotation.querySelector(".rbe-output-annotation-decline-form");
        var actions = annotation.querySelector(".rbe-output-annotation-actions");
        if (form) form.hidden = false;
        if (actions) actions.hidden = false;
        if (reply) reply.focus();
      }

      function saveAnnotationDecline(annotation) {
        if (!annotation) return;
        var reply = annotation.querySelector(".rbe-output-annotation-reply");
        var reason = clean(reply && reply.value);
        if (!reason) {
          annotation.classList.add("has-error", "is-open");
          if (reply) reply.focus();
          return;
        }
        setAnnotationState(annotation, "declined", reason);
      }

      function initAnnotations() {
        Array.prototype.slice.call(root.querySelectorAll(".rbe-output-annotation")).forEach(function (annotation) {
          syncAnnotationUI(annotation, false);
        });
      }

      function initSignatures() {
        var signatureCounts = {};
        Array.prototype.slice.call(root.querySelectorAll(".rbe-output-signature")).forEach(function (box) {
          var id = box.dataset.signatureId || "signature";
          signatureCounts[id] = (signatureCounts[id] || 0) + 1;
        });
        var seen = {};
        Array.prototype.slice.call(root.querySelectorAll(".rbe-output-signature")).forEach(function (box) {
          var id = box.dataset.signatureId || "signature";
          seen[id] = (seen[id] || 0) + 1;
          var key = signatureCounts[id] > 1 ? id + ":" + seen[id] : id;
          box.dataset.signatureStorageKey = key;
          var stored = readStored(signaturePrefix + key);
          if (stored && stored.name) applySingleSignature(box, stored);
        });
        Array.prototype.slice.call(root.querySelectorAll(".rbe-output-signature-flow")).forEach(function (flow) {
          Array.prototype.slice.call(flow.querySelectorAll(".rbe-flow-signature-card")).forEach(function (card) {
            var stored = readStored(flowPrefix + (flow.dataset.flowId || "") + ":" + (card.dataset.stepId || ""));
            if (stored && stored.name && stored.signatureHash) applyFlowStep(card, stored);
          });
          updateFlow(flow);
        });
      }

      root.addEventListener("click", function (event) {
        var annotation = event.target.closest(".rbe-output-annotation");
        if (!annotation) {
          closeActiveAnnotation();
        } else if (!event.target.closest(".rbe-output-annotation-card button, .rbe-output-annotation-reply")) {
          pinAnnotation(annotation);
        }
        var edit = event.target.closest(".rbe-output-annotation-edit");
        if (edit) {
          event.preventDefault();
          startAnnotationEdit(edit.closest(".rbe-output-annotation"));
          return;
        }
        var replyToggle = event.target.closest(".rbe-output-annotation-reply-toggle");
        if (replyToggle) {
          event.preventDefault();
          toggleAnnotationReply(replyToggle.closest(".rbe-output-annotation"));
          return;
        }
        var replySave = event.target.closest(".rbe-output-annotation-reply-save");
        if (replySave) {
          event.preventDefault();
          saveAnnotationReply(replySave.closest(".rbe-output-annotation"));
          return;
        }
        var replyCancel = event.target.closest(".rbe-output-annotation-reply-cancel");
        if (replyCancel) {
          event.preventDefault();
          var annotationForReplyCancel = replyCancel.closest(".rbe-output-annotation");
          var replyForm = annotationForReplyCancel && annotationForReplyCancel.querySelector(".rbe-output-annotation-reply-form");
          var replyInput = annotationForReplyCancel && annotationForReplyCancel.querySelector(".rbe-output-annotation-reply-input");
          if (replyInput) replyInput.value = "";
          if (replyForm) replyForm.hidden = true;
          return;
        }
        var resolve = event.target.closest(".rbe-output-annotation-resolve");
        if (resolve) {
          event.preventDefault();
          if (!hasSignatureFlowApproval()) return;
          setAnnotationState(resolve.closest(".rbe-output-annotation"), "resolved");
          return;
        }
        var decline = event.target.closest(".rbe-output-annotation-decline");
        if (decline) {
          event.preventDefault();
          toggleAnnotationDecline(decline.closest(".rbe-output-annotation"));
          return;
        }
        var cancelEdit = event.target.closest(".rbe-output-annotation-cancel-edit");
        if (cancelEdit) {
          event.preventDefault();
          cancelAnnotationEdit(cancelEdit.closest(".rbe-output-annotation"));
          return;
        }
        var saveDecline = event.target.closest(".rbe-output-annotation-save-decline");
        if (saveDecline) {
          event.preventDefault();
          saveAnnotationDecline(saveDecline.closest(".rbe-output-annotation"));
          return;
        }
        var single = event.target.closest(".rbe-signature-apply");
        if (single) {
          signSingle(single.closest(".rbe-output-signature"));
          return;
        }
        var flow = event.target.closest(".rbe-flow-signature-apply");
        if (flow) signFlow(flow.closest(".rbe-flow-signature-card"));
      });
      root.addEventListener("mouseover", function (event) {
        var annotation = event.target.closest(".rbe-output-annotation");
        if (!annotation) return;
        closeActiveAnnotation(annotation);
        positionAnnotationCard(annotation);
      });
      root.addEventListener("focusin", function (event) {
        var annotation = event.target.closest(".rbe-output-annotation");
        if (annotation) positionAnnotationCard(annotation);
      });
      window.addEventListener("resize", function () {
        Array.prototype.slice.call(root.querySelectorAll(".rbe-output-annotation.is-open")).forEach(positionAnnotationCard);
      });
      root.addEventListener("input", function (event) {
        var annotation = event.target.closest(".rbe-output-annotation");
        if (annotation && event.target.closest(".rbe-output-annotation-reply")) annotation.classList.remove("has-error");
        var errorBox = event.target.closest(".has-error");
        if (errorBox) errorBox.classList.remove("has-error");
      });
      root.addEventListener("keydown", function (event) {
        if (event.key !== "Enter") return;
        var reply = event.target.closest(".rbe-output-annotation-reply");
        if (reply && !event.shiftKey) {
          event.preventDefault();
          saveAnnotationDecline(reply.closest(".rbe-output-annotation"));
          return;
        }
        var singleInput = event.target.closest(".rbe-signature-input");
        if (singleInput) {
          event.preventDefault();
          signSingle(singleInput.closest(".rbe-output-signature"));
          return;
        }
        var flowInput = event.target.closest(".rbe-flow-signature-input");
        if (flowInput) {
          event.preventDefault();
          signFlow(flowInput.closest(".rbe-flow-signature-card"));
        }
      });

      setLogoFallback();
      initTableSorting();
      initTableFilters();
      initSignatures();
      initAnnotations();
    };
    return "<script>(" + script.toString() + ")(" + JSON.stringify(logoDataUrl) + "," + JSON.stringify(runtimeOptions) + ");<\/script>";
  };

  BlockWriteAI.prototype.previewDataForScope = function (scope) {
    var data = cloneJSON(BlockWriteAI.normalizePreviewData(this.savedExportData));

    function cloneBlock(block) {
      return cloneJSON(block || {});
    }

    function filterOutSheetAnalyzer(blocks) {
      return (blocks || []).filter(function (block) {
        return block && block.type !== "sheetAnalyzer";
      }).map(function (block) {
        var copy = cloneBlock(block);
        if (copy.data && Array.isArray(copy.data.blocks)) copy.data.blocks = filterOutSheetAnalyzer(copy.data.blocks);
        if (copy.data && Array.isArray(copy.data.columns)) {
          copy.data.columns = copy.data.columns.map(function (column) {
            var columnCopy = cloneJSON(column || {});
            if (Array.isArray(columnCopy.blocks)) columnCopy.blocks = filterOutSheetAnalyzer(columnCopy.blocks);
            return columnCopy;
          });
        }
        if (Array.isArray(copy.blocks)) copy.blocks = filterOutSheetAnalyzer(copy.blocks);
        if (Array.isArray(copy.children)) copy.children = filterOutSheetAnalyzer(copy.children);
        return copy;
      });
    }

    function collectSheetAnalyzers(value, found) {
      found = found || [];
      if (!value) return found;
      if (Array.isArray(value)) {
        value.forEach(function (item) { collectSheetAnalyzers(item, found); });
        return found;
      }
      if (typeof value === "object") {
        if (value.type === "sheetAnalyzer") found.push(cloneBlock(value));
        var blockData = value.data || {};
        collectSheetAnalyzers(blockData.blocks, found);
        collectSheetAnalyzers(blockData.columns, found);
        collectSheetAnalyzers(value.blocks, found);
        collectSheetAnalyzers(value.children, found);
      }
      return found;
    }

    if (scope === "dashboard") {
      data.blocks = collectSheetAnalyzers(data.blocks || []);
    } else if (scope === "document") {
      data.blocks = filterOutSheetAnalyzer(data.blocks || []);
    }
    return data;
  };

  BlockWriteAI.prototype.buildExportDocument = function (previewData) {
    var data = previewData ? BlockWriteAI.normalizePreviewData(previewData) : BlockWriteAI.normalizePreviewData(this.savedExportData);
    var content = BlockWriteAI.toHTML(data, { select2: false, htmlPreviewMode: this.__htmlPreviewMode === true });
    var styles = this.collectExportStyles();
    var runtime = this.buildExportPreviewRuntime();
    var exportStyles = [
      "@page{size:A4;margin:0;}",
      "body{margin:0;background:#eef3f8;color:#172033;font:16px/1.58 Arial,system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;}",
      ".blockwriteai-export-page{max-width:1040px;margin:24px auto;padding:28px;background:#fff;border:1px solid #d7dee8;border-radius:8px;}",
      ".blockwriteai-export-page .rbe-output-latex{font-style:italic;}",
      ".blockwriteai-export-page .rbe-output-a4-page{position:relative;display:flex;flex-direction:column;width:210mm;height:297mm;min-height:297mm;margin:0 auto 18px;padding:18mm;border:1px solid #d7dee8;background:#fff;color:#172033;box-shadow:0 16px 42px rgba(15,23,42,.08);overflow:hidden;break-after:page;page-break-after:always;box-sizing:border-box;}",
      ".blockwriteai-export-page .rbe-output-a4-page:last-child{break-after:auto;page-break-after:auto;}",
      ".blockwriteai-export-page .rbe-output-a4-body{position:relative;z-index:1;flex:1 1 auto;min-height:0;overflow:hidden;}",
      ".blockwriteai-export-page .rbe-output-a4-body>*{break-inside:avoid;page-break-inside:avoid;}",
      ".blockwriteai-export-page .rbe-output-a4-foot{display:grid;grid-template-columns:minmax(120px,1fr) auto minmax(160px,1fr);gap:16px;width:100%;align-items:center;margin-top:10px;padding-top:9px;border-top:1px solid #cbd5e1;font-size:12px;color:#475467;}",
      ".blockwriteai-export-page .rbe-output-a4-foot>span{display:block;min-width:0;white-space:nowrap;}",
      ".blockwriteai-export-page .rbe-output-a4-foot-center{text-align:center;font-weight:800;color:#172033;justify-self:center;white-space:nowrap;}",
      ".blockwriteai-export-page .rbe-output-a4-foot-right{text-align:right;justify-self:end;white-space:nowrap;}",
      ".blockwriteai-export-page .rbe-output-letterhead-sheet{position:relative;display:flex;flex-direction:column;width:210mm;height:297mm;min-height:297mm;margin:0 auto 18px;padding:16mm 18mm 14mm;border:1px solid #d7dee8;background:#fff;color:#172033;box-shadow:0 16px 42px rgba(15,23,42,.08);overflow:hidden;break-after:page;page-break-after:always;box-sizing:border-box;}",
      ".blockwriteai-export-page .rbe-output-letterhead-sheet:last-child{break-after:auto;page-break-after:auto;}",
      ".blockwriteai-export-page .rbe-output-letterhead-watermark{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none;opacity:.08;z-index:0;}",
      ".blockwriteai-export-page .rbe-output-letterhead-watermark img{max-width:118mm;max-height:118mm;object-fit:contain;}",
      ".blockwriteai-export-page .rbe-output-letterhead-rule{position:relative;z-index:1;border:0;border-top:1px solid #cbd5e1;width:100%;margin:10px 0;}",
      ".blockwriteai-export-page .rbe-output-letterhead-body{position:relative;z-index:1;flex:1 1 auto;min-height:0;overflow:hidden;}",
      ".blockwriteai-export-page .rbe-output-letterhead-body>*{break-inside:avoid;page-break-inside:avoid;}",
      ".blockwriteai-export-page .rbe-output-outline-list{list-style:none!important;padding-left:0!important;margin-left:0!important;}",
      ".blockwriteai-export-page .rbe-output-outline-list li{list-style:none!important;}",
      ".blockwriteai-export-page .rbe-output-list-marker{display:inline-block;min-width:1.8em;margin-right:.35em;text-align:right;}",
      ".blockwriteai-export-page .rbe-output-list-text{display:inline;}",
      ".blockwriteai-export-page .rbe-output-letterhead-head{display:grid;grid-template-columns:minmax(0,1fr) 108px;gap:20px;align-items:start;width:100%;}",
      ".blockwriteai-export-page .rbe-output-letterhead-company{display:block;min-width:0;}",
      ".blockwriteai-export-page .rbe-output-letterhead-logo-cell{display:block;width:108px;text-align:right;}",
      ".blockwriteai-export-page .rbe-output-letterhead-company strong{display:block;margin:0 0 4px;font-size:22px;line-height:1.15;color:#101828;}",
      ".blockwriteai-export-page .rbe-output-letterhead-company span{display:block;font-size:12px;line-height:1.45;color:#475467;}",
      ".blockwriteai-export-page .rbe-output-letterhead-logo{width:96px;height:72px;object-fit:contain;}",
      ".blockwriteai-export-page .rbe-output-letterhead-foot{display:grid;grid-template-columns:minmax(120px,1fr) auto minmax(160px,1fr);gap:16px;width:100%;align-items:center;}",
      ".blockwriteai-export-page .rbe-output-letterhead-foot>span{display:block;min-width:0;white-space:nowrap;}",
      ".blockwriteai-export-page .rbe-output-letterhead-foot-center{text-align:center;justify-self:center;white-space:nowrap;}",
      ".blockwriteai-export-page .rbe-output-letterhead-foot-right{text-align:right;justify-self:end;white-space:nowrap;}",
      ".blockwriteai-export-page .rbe-output-table-wrap{overflow:visible!important;break-inside:auto!important;page-break-inside:auto!important;}",
      ".blockwriteai-export-page .rbe-output-table-wrap table{width:100%!important;max-width:100%!important;margin-left:0!important;margin-right:0!important;table-layout:auto!important;}",
      ".blockwriteai-export-page .rbe-output-table-wrap th,.blockwriteai-export-page .rbe-output-table-wrap td{overflow-wrap:anywhere;word-break:normal;}",
        ".blockwriteai-export-page .rbe-output-annotation-actions{display:flex;gap:10px;align-items:center;justify-content:flex-end;}",
        ".blockwriteai-export-page .rbe-output-annotation-resolve,.blockwriteai-export-page .rbe-output-annotation-decline{min-height:34px;padding:0 14px;border-radius:999px;font-size:12px;font-weight:800;letter-spacing:0;}",
        ".blockwriteai-export-page .rbe-output-annotation-resolve{border-color:#b9ebc8;background:#ecfff1;color:#159947;box-shadow:inset 0 0 0 1px rgba(33,163,74,.06);}",
        ".blockwriteai-export-page .rbe-output-annotation-decline{border-color:#f2c2c2;background:#fff1f1;color:#d53b3b;box-shadow:inset 0 0 0 1px rgba(213,59,59,.06);}",
        ".blockwriteai-export-page .rbe-output-annotation-edit{color:#4f46e5;}",
        ".blockwriteai-preview .rbe-output-annotation-head{grid-template-columns:auto minmax(0,1fr);align-items:center;margin-bottom:5px!important;}",
        ".blockwriteai-preview .rbe-output-annotation-head-meta{grid-column:2;grid-row:1;}",
        ".blockwriteai-preview .rbe-output-annotation-head-side{grid-column:2;grid-row:1;justify-items:end;align-self:center;height:100%;}",
        ".blockwriteai-preview .rbe-output-annotation-heading{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-right:96px;min-height:34px;}",
        ".blockwriteai-preview .rbe-output-annotation-state-line{align-self:center;}",
        ".blockwriteai-preview .rbe-output-annotation-head .rbe-output-annotation-edit{display:none!important;}",
        ".blockwriteai-preview .rbe-output-annotation-reject-meta .rbe-output-annotation-edit{width:20px;min-width:20px;height:20px;min-height:20px;padding:0;border:1px solid rgba(96,165,250,.24);border-radius:999px;background:rgba(37,99,235,.16);color:#bfdbfe;}",
        ".blockwriteai-preview .rbe-output-annotation-reject-meta .rbe-output-annotation-edit{display:inline-flex!important;align-items:center;justify-content:center;}",
        ".blockwriteai-preview .rbe-output-annotation-reject-meta .rbe-output-annotation-edit-glyph{font-size:10px;line-height:1;}",
        ".blockwriteai-preview .rbe-output-annotation-reply-toggle[hidden]{display:none!important;}",
        ".blockwriteai-preview .rbe-output-annotation-reject-row[hidden]{display:none!important;}",
        ".blockwriteai-preview .rbe-output-annotation-thread-list{max-height:292px;overflow-y:auto;overscroll-behavior:contain;padding-right:4px;scrollbar-color:rgba(148,163,184,.55) rgba(15,23,42,.28);scrollbar-width:thin;}",
        ".blockwriteai-preview .rbe-output-annotation-thread-list::-webkit-scrollbar{width:8px;}",
        ".blockwriteai-preview .rbe-output-annotation-thread-list::-webkit-scrollbar-track{border-radius:999px;background:rgba(15,23,42,.28);}",
        ".blockwriteai-preview .rbe-output-annotation-thread-list::-webkit-scrollbar-thumb{border-radius:999px;background:rgba(148,163,184,.55);}",
        "@media print{@page{size:A4;margin:0;}html,body{width:210mm;background:#fff!important;}.blockwriteai-export-page{max-width:none;margin:0!important;padding:0!important;border:0!important;border-radius:0!important;background:#fff!important;}.blockwriteai-export-page .rbe-output-a4-page,.blockwriteai-export-page .rbe-output-letterhead-sheet{width:210mm!important;height:297mm!important;min-height:297mm!important;margin:0!important;border:0!important;box-shadow:none!important;}.blockwriteai-export-page .rbe-output-a4-page:last-child,.blockwriteai-export-page .rbe-output-letterhead-sheet:last-child{break-after:auto!important;page-break-after:auto!important;}}"
      ].join("");
    var previewClass = "blockwriteai-export-page blockwriteai-preview" + (this.themeMode === "dark" ? " blockwriteai-preview-dark" : "");
    var bodyContent = '<main class="' + previewClass + '">' + content + "</main>";
    return [
      "<!doctype html>",
      '<html lang="en">',
      "<head>",
      '<meta charset="utf-8">',
      '<meta name="viewport" content="width=device-width, initial-scale=1">',
      "<title>BlockWriteAI Report</title>",
      styles,
      "<style>" + exportStyles + "</style>",
      "</head>",
      "<body>",
      bodyContent,
      runtime,
      "</body>",
      "</html>"
    ].join("\n");
  };

  BlockWriteAI.prototype.handleExport = function (type) {
    if (this.readOnly || !this.hasExplicitSave || !this.savedExportData) return;
    this.hideExportMenu();
    var data = BlockWriteAI.normalizePreviewData(this.savedExportData);
    if (type === "json") {
      this.downloadBlob(JSON.stringify(data, null, 2), this.getExportFilename("json"), "application/json;charset=utf-8");
      return;
    }
    if (type === "pdf" || type === "word") {
      this.ensureExportSignatureRows().then(function () {
        this.exportPreparedA4Document(type);
      }.bind(this));
      return;
    }
  };

  BlockWriteAI.prototype.handleExportHTML = function () {
    if (this.readOnly || !this.hasExplicitSave || !this.savedExportData) return;
    this.ensureExportSignatureRows().then(function () {
      var documentData = this.previewDataForScope("document");
      if (this.savedExportHasLetterHead()) {
        this.__htmlPreviewMode = true;
        try {
          this.showExportHTMLPreview(this.buildExportDocument(documentData), { a4Paginated: true });
        } finally {
          this.__htmlPreviewMode = false;
        }
        return;
      }
      this.showExportHTMLPreview(this.buildHTMLPreviewDocument(documentData), { a4Paginated: false });
    }.bind(this));
  };

  BlockWriteAI.prototype.handleShowDashboard = function () {
    if (this.readOnly || !this.hasExplicitSave || !this.savedExportData) return;
    this.showExportHTMLPreview(this.buildHTMLPreviewDocument(this.previewDataForScope("dashboard")), { a4Paginated: false });
  };

  BlockWriteAI.prototype.ensureExportSignatureRows = function () {
    if (Array.isArray(this.options.exportSignatureRows) && this.options.exportSignatureRows.length) {
      return Promise.resolve(this.options.exportSignatureRows);
    }
    if (!BlockWriteAI.primeExportSignaturesForDocument) return Promise.resolve([]);
    var options = Object.assign({}, this.options || {});
    return BlockWriteAI.primeExportSignaturesForDocument(BlockWriteAI.normalizePreviewData(this.savedExportData), options).then(function (rows) {
      this.options.exportSignatureRows = Array.isArray(rows) ? rows : [];
      return this.options.exportSignatureRows;
    }.bind(this));
  };

  BlockWriteAI.prototype.exportPreparedA4Document = function (type) {
    if (!this.hasExplicitSave || !this.savedExportData) return;
    var editor = this;
    var previousExportTarget = this.options.exportTarget;
    this.options.exportTarget = type === "word" ? "word" : "pdf";
    var html = this.stripExportRuntimeScripts(this.buildExportDocument());
    var frame = el("iframe", "", {
      title: "BlockWriteAI export preparation",
      sandbox: "allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"
    });
    frame.style.position = "fixed";
    frame.style.left = "-10000px";
    frame.style.top = "0";
    frame.style.width = "210mm";
    frame.style.height = "297mm";
    frame.style.visibility = "hidden";
    frame.setAttribute("aria-hidden", "true");
    document.body.appendChild(frame);
    frame.addEventListener("load", function () {
      setTimeout(function () {
        try {
          editor.prepareA4ExportDocument(frame.contentDocument);
          var preparedHtml = editor.currentA4ExportHTML(frame, html);
          if (type === "word") {
            editor.exportA4Word(preparedHtml);
          } else {
            editor.exportLetterHeadPDF(preparedHtml);
          }
        } finally {
          editor.options.exportTarget = previousExportTarget;
          setTimeout(function () {
            frame.remove();
          }, 250);
        }
      }, 60);
    }, { once: true });
    frame.srcdoc = html;
  };

  BlockWriteAI.prototype.savedExportHasBlockType = function (type) {
    function containsBlockType(value) {
      if (!value) return false;
      if (Array.isArray(value)) {
        return value.some(function (block) {
          return containsBlockType(block);
        });
      }
      if (typeof value === "object") {
        var block = value;
        if (!block || typeof block !== "object") return false;
        if (block.type === type) return true;
        var data = block.data || {};
        return containsBlockType(data.blocks) || containsBlockType(data.columns) || containsBlockType(block.blocks) || containsBlockType(block.children);
      }
      return false;
    }
    var data = BlockWriteAI.normalizePreviewData(this.savedExportData);
    return containsBlockType(data.blocks || []);
  };

  BlockWriteAI.prototype.savedExportHasLetterHead = function () {
    return this.savedExportHasBlockType("letterHead");
  };

  BlockWriteAI.prototype.savedExportHasSheetAnalyzer = function () {
    return this.savedExportHasBlockType("sheetAnalyzer");
  };

  BlockWriteAI.prototype.currentDocumentHasBlockType = function (type) {
    if (!this.canvas) return false;
    if (Array.prototype.slice.call(this.canvas.querySelectorAll(".rbe-block")).some(function (block) {
      return block && block.dataset && block.dataset.type === type;
    })) return true;
    return false;
  };

  BlockWriteAI.prototype.exportLetterHeadWord = function (html) {
    html = this.omitUnsignedSignatureFlowsForExport(html);
    this.downloadBlob(["\ufeff", this.buildA4WordDocument(html)].join(""), this.getExportFilename("doc"), "application/msword;charset=utf-8");
  };

  BlockWriteAI.prototype.buildA4WordDocument = function (html) {
    var parser = new DOMParser();
    var sourceDoc = parser.parseFromString(html || "", "text/html");
    var firstSheet = sourceDoc.querySelector(".rbe-output-letterhead-sheet");
    var firstGenericSheet = sourceDoc.querySelector(".rbe-output-a4-page");
    if (!firstSheet && !firstGenericSheet) return html;

    var templateHead = firstSheet ? firstSheet.querySelector(".rbe-output-letterhead-head") : null;
    var templateFoot = firstSheet ? firstSheet.querySelector(".rbe-output-letterhead-foot") : null;
    var templateWatermark = firstSheet ? firstSheet.querySelector(".rbe-output-letterhead-watermark img") : null;
    var exportMain = sourceDoc.querySelector(".blockwriteai-export-page") || sourceDoc.body;
    var wordContent = exportMain.cloneNode(true);

    Array.prototype.slice.call(wordContent.querySelectorAll(".rbe-output-letterhead-watermark,.rbe-output-letterhead-head,.rbe-output-letterhead-foot,.rbe-output-letterhead-rule")).forEach(function (node) {
      node.remove();
    });
    Array.prototype.slice.call(wordContent.querySelectorAll(".rbe-output-letterhead-sheet")).forEach(function (sheet) {
      sheet.removeAttribute("style");
      sheet.className = "rbe-output-letterhead-sheet";
    });
    Array.prototype.slice.call(wordContent.querySelectorAll(".rbe-output-a4-page")).forEach(function (sheet) {
      sheet.removeAttribute("style");
      sheet.className = "rbe-output-a4-page";
    });

    var headerHtml = templateHead ? templateHead.outerHTML : "";
    var footerHtml = templateFoot ? templateFoot.cloneNode(true) : null;
    if (footerHtml) {
      var pageNumber = footerHtml.querySelector(".rbe-output-letterhead-page-number");
      if (pageNumber) {
        pageNumber.innerHTML = "<!--[if supportFields]><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>PAGE <span style='mso-element:field-separator'></span><![endif]--><span>1</span><!--[if supportFields]><span style='mso-element:field-end'></span><![endif]-->";
      }
    }
    var footerPartHtml = footerHtml ? footerHtml.outerHTML : "";
    var watermarkHtml = "";
    if (templateWatermark && templateWatermark.getAttribute("src")) {
      watermarkHtml = [
        '<v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">',
        '<v:stroke joinstyle="miter"/><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"/><v:f eqn="sum @0 1 0"/><v:f eqn="sum 0 0 @1"/><v:f eqn="prod @2 1 2"/><v:f eqn="prod @3 21600 pixelWidth"/><v:f eqn="prod @3 21600 pixelHeight"/><v:f eqn="sum @0 0 1"/><v:f eqn="prod @6 1 2"/><v:f eqn="prod @7 21600 pixelWidth"/><v:f eqn="sum @8 21600 0"/><v:f eqn="prod @7 21600 pixelHeight"/><v:f eqn="sum @10 21600 0"/></v:formulas><v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"/><o:lock v:ext="edit" aspectratio="t"/></v:shapetype>',
        '<v:shape id="BlockWriteAIWatermark" o:spid="_x0000_s1025" type="#_x0000_t75" filled="t" stroked="f" o:allowincell="f"',
        ' style="position:absolute;width:150pt;height:150pt;z-index:-251654144;',
        'mso-position-horizontal:center;mso-position-horizontal-relative:page;',
        'mso-position-vertical:center;mso-position-vertical-relative:page">',
        '<v:fill src="' + escapeHTML(templateWatermark.getAttribute("src")) + '" type="frame" opacity="8%"/>',
        '</v:shape>'
      ].join("");
    }

    var styles = Array.prototype.slice.call(sourceDoc.head.querySelectorAll("style")).map(function (style) {
      return (style.textContent || "")
        .replace(/@page[^{]*\{[^}]*\}/gi, "")
        .replace(/@media\s+print\s*\{[\s\S]*?\}\s*$/gi, "");
    }).join("\n");
    var wordStyles = [
      "@page Section1{size:210mm 297mm;margin:42mm 18mm 24mm 18mm;mso-header-margin:10mm;mso-footer-margin:8mm;mso-header:h1;mso-footer:f1;}",
      "div.Section1{page:Section1;}",
      "body{margin:0;background:#fff;color:#172033;font:12pt/1.45 Arial,sans-serif;}",
      ".blockwriteai-export-page{max-width:none;margin:0;padding:0;background:#fff;border:0;border-radius:0;}",
      ".rbe-output-letterhead-sheet{width:auto!important;height:auto!important;min-height:0!important;margin:0!important;padding:0!important;border:0!important;box-shadow:none!important;background:#fff!important;color:#172033!important;page-break-after:always;break-after:page;overflow:visible!important;}",
      ".rbe-output-a4-page{width:auto!important;height:auto!important;min-height:0!important;margin:0!important;padding:0!important;border:0!important;box-shadow:none!important;background:#fff!important;color:#172033!important;page-break-after:always;break-after:page;overflow:visible!important;}",
      ".rbe-output-letterhead-sheet:last-child{page-break-after:auto;break-after:auto;}",
      ".rbe-output-a4-page:last-child{page-break-after:auto;break-after:auto;}",
      ".rbe-output-letterhead-body,.rbe-output-a4-body{position:relative;z-index:1;min-height:0!important;overflow:visible!important;}",
      ".rbe-output-letterhead-body>*,.rbe-output-a4-body>*{break-inside:auto;page-break-inside:auto;}",
      ".rbe-output-letterhead-head{display:grid;grid-template-columns:minmax(0,1fr) 96pt;gap:20pt;align-items:start;width:100%;border-bottom:1px solid #cbd5e1;padding-bottom:8pt;}",
      ".rbe-output-letterhead-company{display:block;min-width:0;}",
      ".rbe-output-letterhead-company strong{display:block;margin:0 0 4pt;font-size:18pt;line-height:1.15;color:#101828;font-weight:800;}",
      ".rbe-output-letterhead-company span{display:block;font-size:9pt;line-height:1.35;color:#102044;}",
      ".rbe-output-letterhead-logo-cell{display:block;width:96pt;text-align:right;}",
      ".rbe-output-letterhead-logo{width:72pt;height:54pt;object-fit:contain;}",
      ".rbe-output-letterhead-foot{display:grid;grid-template-columns:minmax(120px,1fr) auto minmax(160px,1fr);gap:16pt;width:100%;align-items:center;border-top:1px solid #cbd5e1;padding-top:8pt;font-size:10pt;color:#0f172a;}",
      ".rbe-output-letterhead-foot>span{display:block;min-width:0;white-space:nowrap;}",
      ".rbe-output-letterhead-foot-center{text-align:center;font-weight:400;color:#0f172a;justify-self:center;white-space:nowrap;}",
      ".rbe-output-letterhead-foot-right{text-align:right;justify-self:end;white-space:nowrap;}",
      ".rbe-output-a4-foot{display:grid;grid-template-columns:minmax(120px,1fr) auto minmax(160px,1fr);gap:16pt;width:100%;align-items:center;border-top:1px solid #cbd5e1;padding-top:8pt;font-size:10pt;color:#0f172a;}",
      ".rbe-output-a4-foot>span{display:block;min-width:0;white-space:nowrap;}",
      ".rbe-output-a4-foot-center{text-align:center;font-weight:400;color:#0f172a;justify-self:center;white-space:nowrap;}",
      ".rbe-output-a4-foot-right{text-align:right;justify-self:end;white-space:nowrap;}"
    ].join("\n");

    return [
      '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:v="urn:schemas-microsoft-com:vml" xmlns="http://www.w3.org/TR/REC-html40">',
      "<head>",
      '<meta charset="utf-8">',
      "<title>BlockWriteAI Report</title>",
      "<!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View><w:Zoom>100</w:Zoom><w:DoNotOptimizeForBrowser/></w:WordDocument></xml><![endif]-->",
      "<style>",
      styles,
      wordStyles,
      "</style>",
      "</head>",
      "<body>",
      headerHtml ? '<div style="mso-element:header" id="h1">' + watermarkHtml + headerHtml + "</div>" : "",
      footerPartHtml ? '<div style="mso-element:footer" id="f1">' + footerPartHtml + "</div>" : "",
      '<div class="Section1">' + wordContent.innerHTML + "</div>",
      "</body>",
      "</html>"
    ].join("\n");
  };

  BlockWriteAI.prototype.buildHTMLPreviewDocument = function (previewData) {
    this.__htmlPreviewMode = true;
    try {
      var data = previewData ? BlockWriteAI.normalizePreviewData(previewData) : BlockWriteAI.normalizePreviewData(this.savedExportData);
      var content = BlockWriteAI.toHTML(data, { select2: false, htmlPreviewMode: true });
      var styles = this.collectExportStyles();
      var runtime = this.buildExportPreviewRuntime();
      var previewStyles = [
        "body{margin:0;background:#eef3f8;color:#172033;font:16px/1.58 Arial,system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;}",
        ".blockwriteai-html-preview-page{max-width:1180px;margin:24px auto;padding:28px;background:#fff;border:1px solid #d7dee8;border-radius:8px;}",
        ".blockwriteai-html-preview-page .rbe-output-a4-page,.blockwriteai-html-preview-page .rbe-output-letterhead-sheet{width:auto!important;height:auto!important;min-height:0!important;margin:0 0 18px!important;padding:0!important;border:0!important;box-shadow:none!important;overflow:visible!important;break-after:auto!important;page-break-after:auto!important;}",
        ".blockwriteai-html-preview-page .rbe-output-a4-body,.blockwriteai-html-preview-page .rbe-output-letterhead-body{overflow:visible!important;}",
        ".blockwriteai-html-preview-page .rbe-output-a4-foot,.blockwriteai-html-preview-page .rbe-output-letterhead-foot,.blockwriteai-html-preview-page .rbe-output-letterhead-watermark,.blockwriteai-html-preview-page .rbe-output-letterhead-head,.blockwriteai-html-preview-page .rbe-output-letterhead-rule{display:none!important;}",
        ".blockwriteai-html-preview-page .rbe-output-chart{display:block;max-width:100%;min-height:240px;overflow-x:auto;overflow-y:visible;margin:0 0 14px;border:1px solid #d7dee8;border-radius:8px;padding:10px 12px;background:#fff;}",
        ".blockwriteai-html-preview-page .rbe-output-chart svg{display:block;width:min(var(--rbe-chart-svg-width,880px),6400px)!important;min-width:0!important;height:auto!important;max-height:none!important;margin:0 auto;overflow:visible;}",
        ".blockwriteai-html-preview-page .rbe-output-chart svg text{font-size:10px!important;font-weight:600!important;stroke-width:0!important;letter-spacing:0!important;}",
        ".blockwriteai-html-preview-page .rbe-output-chart svg rect{stroke-width:0!important;}",
        ".blockwriteai-html-preview-page .rbe-output-chart svg polyline{stroke-width:2.5!important;}",
        ".blockwriteai-html-preview-page .rbe-output-chart svg circle{stroke-width:0!important;}",
        ".blockwriteai-html-preview-page .rbe-output-chart-title{display:block;margin-bottom:6px;font-size:15px;font-weight:700;}",
        ".blockwriteai-html-preview-page .rbe-output-chart-caption{display:block;margin:8px auto 0;color:#475467;font-size:12px;font-weight:600;}",
        ".blockwriteai-html-preview-page .rbe-output-outline-list{list-style:none!important;margin-left:0!important;padding-left:0!important;}",
        ".blockwriteai-html-preview-page .rbe-output-outline-list li{display:grid;grid-template-columns:1.8em minmax(0,1fr);column-gap:.45em;align-items:start;list-style:none!important;margin:0 0 7px!important;padding-left:0!important;}",
        ".blockwriteai-html-preview-page .rbe-output-list-marker{display:block!important;min-width:0!important;margin-right:0!important;text-align:right;color:#667085;}",
        ".blockwriteai-html-preview-page .rbe-output-list-text{display:block!important;min-width:0;overflow-wrap:anywhere;}",
        ".blockwriteai-html-preview-page .rbe-output-outline-list .rbe-output-outline-list{grid-column:2;margin-top:6px!important;}",
        ".blockwriteai-html-preview-page .rbe-output-table-wrap{display:block;max-width:100%;overflow:auto;}",
        ".blockwriteai-html-preview-page .rbe-output-table-wrap table.rbe-output-filterable-table{width:100%!important;min-width:max-content;table-layout:auto!important;}",
        ".blockwriteai-html-preview-page .rbe-output-table-wrap th{min-width:128px;max-width:220px;white-space:normal;line-height:1.35;overflow-wrap:anywhere;word-break:normal;}",
        ".blockwriteai-html-preview-page .rbe-output-table-sort-button{display:grid;grid-template-columns:minmax(0,1fr) 18px;gap:7px;align-items:start;width:100%;min-width:0;margin:0;padding:0;border:0;background:transparent;color:inherit;font:inherit;font-weight:inherit;text-align:left;cursor:pointer;letter-spacing:0;}",
        ".blockwriteai-html-preview-page .rbe-output-table-sort-label{display:block;min-width:0;overflow-wrap:anywhere;word-break:normal;}",
        ".blockwriteai-html-preview-page .rbe-output-table-sort-icon{position:relative;display:inline-block;width:18px;height:18px;border:1px solid #b8c7dc;border-radius:4px;background:#fff;box-sizing:border-box;}",
        ".blockwriteai-html-preview-page .rbe-output-table-sort-icon:before,.blockwriteai-html-preview-page .rbe-output-table-sort-icon:after{content:\"\";position:absolute;left:5px;width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;}",
        ".blockwriteai-html-preview-page .rbe-output-table-sort-icon:before{top:4px;border-bottom:5px solid #64748b;}",
        ".blockwriteai-html-preview-page .rbe-output-table-sort-icon:after{bottom:4px;border-top:5px solid #64748b;}",
        ".blockwriteai-html-preview-page th[data-sort-dir='asc'] .rbe-output-table-sort-icon:before{border-bottom-color:#2563eb;}",
        ".blockwriteai-html-preview-page th[data-sort-dir='asc'] .rbe-output-table-sort-icon:after{opacity:.25;}",
        ".blockwriteai-html-preview-page th[data-sort-dir='desc'] .rbe-output-table-sort-icon:after{border-top-color:#2563eb;}",
        ".blockwriteai-html-preview-page th[data-sort-dir='desc'] .rbe-output-table-sort-icon:before{opacity:.25;}",
        ".blockwriteai-html-preview-page .rbe-output-table-filter{display:inline-flex;margin-left:6px;vertical-align:top;}"
      ].join("");
      return [
        "<!doctype html>",
        '<html lang="en">',
        "<head>",
        '<meta charset="utf-8">',
        '<meta name="viewport" content="width=device-width, initial-scale=1">',
        "<title>BlockWriteAI HTML Preview</title>",
        styles,
        "<style>" + previewStyles + "</style>",
        "</head>",
        "<body>",
        '<main class="blockwriteai-html-preview-page blockwriteai-preview' + (this.themeMode === "dark" ? " blockwriteai-preview-dark" : "") + '">' + content + "</main>",
        runtime,
        "</body>",
        "</html>"
      ].join("\n");
    } finally {
      this.__htmlPreviewMode = false;
    }
  };

  BlockWriteAI.prototype.buildLetterHeadWordDocument = function (html) {
    return this.buildA4WordDocument(html);
  };

  BlockWriteAI.prototype.exportLetterHeadPDF = function (html) {
    html = this.omitUnsignedSignatureFlowsForExport(html);
    html = this.stripExportRuntimeScripts(html);
    var endpoint = this.getA4ExportEndpoint("pdf");
    if (endpoint) {
      this.submitA4Export(endpoint, "pdf", html);
      return;
    }
    var printFrame = el("iframe", "", {
      title: "BlockWriteAI PDF print",
      sandbox: "allow-same-origin allow-modals allow-downloads"
    });
    printFrame.style.position = "fixed";
    printFrame.style.right = "0";
    printFrame.style.bottom = "0";
    printFrame.style.width = "0";
    printFrame.style.height = "0";
    printFrame.style.border = "0";
    printFrame.style.visibility = "hidden";
    document.body.appendChild(printFrame);
    printFrame.contentDocument.open();
    printFrame.contentDocument.write(html);
    printFrame.contentDocument.close();
    var runPrint = function () {
      try {
        printFrame.contentWindow.focus();
        printFrame.contentWindow.print();
      } catch (error) {}
      setTimeout(function () {
        printFrame.remove();
      }, 1000);
    };
    if (printFrame.contentDocument.readyState === "complete") {
      setTimeout(runPrint, 250);
    } else {
      printFrame.addEventListener("load", function () {
        setTimeout(runPrint, 250);
      }, { once: true });
    }
  };

  BlockWriteAI.prototype.exportA4Word = function (html) {
    html = this.omitUnsignedSignatureFlowsForExport(html);
    var endpoint = this.getA4ExportEndpoint("word");
    if (endpoint) {
      this.submitA4Export(endpoint, "word", html);
      return;
    }
    this.downloadBlob(["\ufeff", this.buildA4WordDocument(html)].join(""), this.getExportFilename("doc"), "application/msword;charset=utf-8");
  };

  BlockWriteAI.prototype.getA4ExportEndpoint = function (type) {
    var endpoints = this.options && (this.options.documentExportEndpoints || this.options.exportEndpoints) || {};
    var key = type === "word" ? "word" : "pdf";
    return endpoints[key]
      || this.options && this.options[key === "word" ? "exportWordEndpoint" : "exportPdfEndpoint"]
      || BlockWriteAI[key === "word" ? "exportWordEndpoint" : "exportPdfEndpoint"]
      || BlockWriteAI.documentExportEndpoint
      || "https://blockwriteai.in/api/premium_proxy.php?action=document_export";
  };

  BlockWriteAI.prototype.stripExportRuntimeScripts = function (html) {
    if (!html || typeof DOMParser === "undefined") {
      return String(html || "").replace(/<script\b[\s\S]*?<\/script>/gi, "");
    }
    try {
      var doc = new DOMParser().parseFromString(String(html || ""), "text/html");
      Array.prototype.slice.call(doc.querySelectorAll("script")).forEach(function (script) {
        script.remove();
      });
      return "<!doctype html>\n" + doc.documentElement.outerHTML;
    } catch (error) {
      return String(html || "").replace(/<script\b[\s\S]*?<\/script>/gi, "");
    }
  };

  BlockWriteAI.prototype.submitA4Export = function (endpoint, type, html) {
    if (type === "pdf" && typeof fetch === "function" && typeof FormData !== "undefined") {
      var editor = this;
      var data = new FormData();
      data.append("format", type);
      data.append("filename", this.getExportFilename("pdf"));
      data.append("html", html);
      fetch(endpoint, {
        method: "POST",
        body: data,
        credentials: "same-origin"
      }).then(function (response) {
        if (!response.ok) throw new Error("PDF export failed.");
        return response.blob().then(function (blob) {
          editor.downloadBlob(blob, editor.getExportFilename("pdf"), blob.type || "application/pdf");
        });
      }).catch(function (error) {
        console.warn("PDF export failed:", error);
        editor.downloadBlob(html, editor.getExportFilename("html"), "text/html;charset=utf-8");
      });
      return;
    }
    var form = document.createElement("form");
    form.method = "post";
    form.action = endpoint;
    form.target = "_blank";
    form.style.display = "none";
    [
      ["format", type],
      ["filename", this.getExportFilename(type === "word" ? "docx" : "pdf")],
      ["html", html]
    ].forEach(function (field) {
      var input = document.createElement("input");
      input.type = "hidden";
      input.name = field[0];
      input.value = field[1];
      form.appendChild(input);
    });
    document.body.appendChild(form);
    form.submit();
    setTimeout(function () {
      form.remove();
    }, 1000);
  };

  function appendPaginatedTableRows(doc, node, getTargetBody, setTargetBody, makePage) {
    var sourceTable = /^TABLE$/i.test(node && node.tagName || "") ? node : node && node.querySelector ? node.querySelector("table") : null;
    if (!sourceTable) return false;
    var sourceRows = Array.prototype.slice.call(sourceTable.querySelectorAll("tr"));
    if (sourceRows.length <= 1) return false;
    var headerRow = sourceRows[0] && (sourceRows[0].querySelector("th") || /^THEAD$/i.test(sourceRows[0].parentNode && sourceRows[0].parentNode.tagName || "")) ? sourceRows[0] : null;
    var bodyRows = headerRow ? sourceRows.filter(function (row) { return row !== headerRow; }) : sourceRows;
    if (!bodyRows.length) return false;
    var tableState = null;

    function newTableRoot() {
      var root = sourceTable === node ? null : node.cloneNode(false);
      var table = sourceTable.cloneNode(false);
      var tableBody = doc.createElement("tbody");
      Array.prototype.slice.call(sourceTable.children).forEach(function (child) {
        if (/^(CAPTION|COLGROUP)$/i.test(child.tagName || "")) table.appendChild(child.cloneNode(true));
      });
      if (headerRow) {
        var tableHead = doc.createElement("thead");
        tableHead.appendChild(headerRow.cloneNode(true));
        table.appendChild(tableHead);
      }
      table.appendChild(tableBody);
      if (root) {
        root.appendChild(table);
      } else {
        root = table;
      }
      return { root: root, body: tableBody };
    }

    function placeNewTable() {
      tableState = newTableRoot();
      getTargetBody().appendChild(tableState.root);
      if (getTargetBody().scrollHeight > getTargetBody().clientHeight + 1 && getTargetBody().children.length > 1) {
        getTargetBody().removeChild(tableState.root);
        setTargetBody(makePage());
        getTargetBody().appendChild(tableState.root);
      }
    }

    placeNewTable();
    bodyRows.forEach(function (row) {
      var clonedRow = row.cloneNode(true);
      tableState.body.appendChild(clonedRow);
      if (getTargetBody().scrollHeight <= getTargetBody().clientHeight + 1) return;
      tableState.body.removeChild(clonedRow);
      if (!tableState.body.children.length && tableState.root.parentNode === getTargetBody()) {
        getTargetBody().removeChild(tableState.root);
      }
      setTargetBody(makePage());
      placeNewTable();
      tableState.body.appendChild(clonedRow);
    });
    return true;
  }

  BlockWriteAI.prototype.prepareLetterHeadExportDocument = function (doc) {
    if (!doc || doc.__blockwriteaiLetterHeadPaginated) return;
    doc.__blockwriteaiLetterHeadPaginated = true;
    var sheets = Array.prototype.slice.call(doc.querySelectorAll(".rbe-output-letterhead-sheet"));
    sheets.forEach(function (sheet) {
      if (sheet.dataset.paginated === "true") return;
      var body = sheet.querySelector(".rbe-output-letterhead-body");
      if (!body) return;
      var children = Array.prototype.slice.call(body.children);
      if (!children.length) return;
      var parent = sheet.parentNode;
      if (!parent) return;
      var watermark = sheet.querySelector(".rbe-output-letterhead-watermark");
      var head = sheet.querySelector(".rbe-output-letterhead-head");
      var foot = sheet.querySelector(".rbe-output-letterhead-foot");
      var company = foot ? foot.querySelector(".rbe-output-letterhead-foot-center") : null;
      var timestamp = foot ? foot.querySelector(".rbe-output-letterhead-foot-right") : null;
      var templateWatermark = watermark ? watermark.cloneNode(true) : null;
      var templateHead = head ? head.cloneNode(true) : null;
      var companyText = company ? company.textContent || "" : "";
      var timestampText = timestamp ? timestamp.textContent || "" : "";
      var pages = [];

      function makePage() {
        var page = sheet.cloneNode(false);
        page.dataset.paginated = "true";
        if (templateWatermark) page.appendChild(templateWatermark.cloneNode(true));
        if (templateHead) page.appendChild(templateHead.cloneNode(true));
        page.appendChild(el("hr", "rbe-output-letterhead-rule"));
        var pageBody = el("main", "rbe-output-letterhead-body");
        page.appendChild(pageBody);
        page.appendChild(el("hr", "rbe-output-letterhead-rule"));
        var pageFoot = el("footer", "rbe-output-letterhead-foot", {
          html: '<span>Page <span class="rbe-output-letterhead-page-number"></span> / <span class="rbe-output-letterhead-page-total"></span></span><span class="rbe-output-letterhead-foot-center">' + escapeHTML(companyText) + '</span><span class="rbe-output-letterhead-foot-right">' + escapeHTML(timestampText) + "</span>"
        });
        page.appendChild(pageFoot);
        parent.insertBefore(page, sheet);
        pages.push(page);
        return pageBody;
      }

      function appendNodeToPage(node) {
        if (appendPaginatedTableRows(doc, node, function () { return targetBody; }, function (nextBody) { targetBody = nextBody; }, makePage)) return;
        if (/^(UL|OL)$/i.test(node.tagName || "") && node.children.length > 1) {
          var list = node.cloneNode(false);
          targetBody.appendChild(list);
          Array.prototype.slice.call(node.children).forEach(function (item) {
            list.appendChild(item.cloneNode(true));
            if (targetBody.scrollHeight > targetBody.clientHeight + 1 && list.children.length > 1) {
              list.removeChild(list.lastChild);
              targetBody = makePage();
              list = node.cloneNode(false);
              targetBody.appendChild(list);
              list.appendChild(item.cloneNode(true));
            } else if (targetBody.scrollHeight > targetBody.clientHeight + 1 && list.children.length === 1 && targetBody.children.length > 1) {
              targetBody.removeChild(list);
              targetBody = makePage();
              list = node.cloneNode(false);
              targetBody.appendChild(list);
              list.appendChild(item.cloneNode(true));
            }
          });
          return;
        }
        targetBody.appendChild(node);
        if (targetBody.scrollHeight > targetBody.clientHeight + 1 && targetBody.children.length > 1) {
          targetBody.removeChild(node);
          targetBody = makePage();
          targetBody.appendChild(node);
        }
      }

      var targetBody = makePage();
      children.forEach(function (child) {
        appendNodeToPage(child.cloneNode(true));
      });
      pages.forEach(function (page, index) {
        var number = page.querySelector(".rbe-output-letterhead-page-number");
        if (number) number.textContent = String(index + 1);
        var total = page.querySelector(".rbe-output-letterhead-page-total");
        if (total) total.textContent = String(pages.length);
      });
      sheet.remove();
    });
  };

  BlockWriteAI.prototype.prepareGenericA4ExportDocument = function (doc) {
    if (!doc || doc.__blockwriteaiGenericA4Paginated) return;
    doc.__blockwriteaiGenericA4Paginated = true;
    var main = doc.querySelector(".blockwriteai-export-page");
    if (!main) return;
    var children = Array.prototype.slice.call(main.children);
    var run = [];

    function isLooseContent(node) {
      if (!node || node.nodeType !== 1) return false;
      if (node.classList.contains("rbe-output-letterhead-sheet") || node.classList.contains("rbe-output-a4-page")) return false;
      if (/^(SCRIPT|STYLE|LINK|META)$/i.test(node.tagName || "")) return false;
      return true;
    }

    function paginateRun(nodes, beforeNode) {
      if (!nodes.length) return;
      var pages = [];
      var targetBody = null;

      function makePage() {
        var page = el("section", "rbe-output-a4-page");
        page.dataset.paginated = "true";
        var pageBody = el("main", "rbe-output-a4-body");
        var pageFoot = el("footer", "rbe-output-a4-foot", {
          html: '<span>Page <span class="rbe-output-a4-page-number"></span> / <span class="rbe-output-a4-page-total"></span></span><span class="rbe-output-a4-foot-center">BlockWriteAI</span><span class="rbe-output-a4-foot-right"></span>'
        });
        page.appendChild(pageBody);
        page.appendChild(pageFoot);
        main.insertBefore(page, beforeNode || null);
        pages.push(page);
        return pageBody;
      }

      function appendNodeToPage(node) {
        if (appendPaginatedTableRows(doc, node, function () { return targetBody; }, function (nextBody) { targetBody = nextBody; }, makePage)) return;
        if (/^(UL|OL)$/i.test(node.tagName || "") && node.children.length > 1) {
          var list = node.cloneNode(false);
          targetBody.appendChild(list);
          Array.prototype.slice.call(node.children).forEach(function (item) {
            list.appendChild(item.cloneNode(true));
            if (targetBody.scrollHeight > targetBody.clientHeight + 1 && list.children.length > 1) {
              list.removeChild(list.lastChild);
              targetBody = makePage();
              list = node.cloneNode(false);
              targetBody.appendChild(list);
              list.appendChild(item.cloneNode(true));
            } else if (targetBody.scrollHeight > targetBody.clientHeight + 1 && list.children.length === 1 && targetBody.children.length > 1) {
              targetBody.removeChild(list);
              targetBody = makePage();
              list = node.cloneNode(false);
              targetBody.appendChild(list);
              list.appendChild(item.cloneNode(true));
            }
          });
          return;
        }
        targetBody.appendChild(node);
        if (targetBody.scrollHeight > targetBody.clientHeight + 1 && targetBody.children.length > 1) {
          targetBody.removeChild(node);
          targetBody = makePage();
          targetBody.appendChild(node);
        }
      }

      targetBody = makePage();
      nodes.forEach(function (node) {
        appendNodeToPage(node.cloneNode(true));
        node.remove();
      });
      pages.forEach(function (page, index) {
        var number = page.querySelector(".rbe-output-a4-page-number");
        if (number) number.textContent = String(index + 1);
        var total = page.querySelector(".rbe-output-a4-page-total");
        if (total) total.textContent = String(pages.length);
      });
    }

    children.forEach(function (child) {
      if (isLooseContent(child)) {
        run.push(child);
        return;
      }
      paginateRun(run, child);
      run = [];
    });
    paginateRun(run, null);
  };

  BlockWriteAI.prototype.prepareA4ExportDocument = function (doc) {
    if (!doc) return;
    if (doc.documentElement && doc.documentElement.dataset.blockwriteaiA4ExportPrepared === "true") return;
    if (doc.documentElement) doc.documentElement.dataset.blockwriteaiA4ExportPrepared = "true";
    this.prepareLetterHeadExportDocument(doc);
    this.prepareGenericA4ExportDocument(doc);
    this.applyExportSignatureRowsToDocument(doc, this.options && this.options.exportSignatureRows);
    this.prepareSignaturesForExport(doc);
  };

  BlockWriteAI.prototype.prepareA4PreviewDocument = function (doc) {
    if (!doc) return;
    if (doc.documentElement && doc.documentElement.dataset.blockwriteaiA4PreviewPrepared === "true") return;
    if (doc.documentElement) doc.documentElement.dataset.blockwriteaiA4PreviewPrepared = "true";
    this.prepareLetterHeadExportDocument(doc);
    this.prepareGenericA4ExportDocument(doc);
    this.applyExportSignatureRowsToDocument(doc, this.options && this.options.exportSignatureRows);
  };

  BlockWriteAI.prototype.currentLetterHeadExportHTML = function (frame, fallbackHtml) {
    try {
      if (frame && frame.contentDocument) {
        this.prepareA4ExportDocument(frame.contentDocument);
        return "<!doctype html>\n" + frame.contentDocument.documentElement.outerHTML;
      }
    } catch (error) {}
    return fallbackHtml;
  };

  BlockWriteAI.prototype.currentA4ExportHTML = function (frame, fallbackHtml) {
    return this.currentLetterHeadExportHTML(frame, fallbackHtml);
  };

  BlockWriteAI.prototype.currentA4PreviewHTML = function (frame, fallbackHtml) {
    try {
      if (frame && frame.contentDocument) {
        this.prepareA4PreviewDocument(frame.contentDocument);
        return "<!doctype html>\n" + frame.contentDocument.documentElement.outerHTML;
      }
    } catch (error) {}
    return fallbackHtml;
  };

  BlockWriteAI.prototype.prepareHTMLPreviewDocument = function (doc) {
    if (!doc) return;
    if (doc.getElementById("blockwriteai-html-preview-chart-styles")) return;
    var style = doc.createElement("style");
    style.id = "blockwriteai-html-preview-chart-styles";
    style.textContent = [
      ".blockwriteai-preview .rbe-output-chart{display:block;max-width:100%;min-height:240px;overflow-x:auto;overflow-y:visible;}",
      ".blockwriteai-preview .rbe-output-chart svg{display:block;width:min(var(--rbe-chart-svg-width,880px),6400px)!important;min-width:0!important;height:auto!important;max-height:none!important;margin:0 auto;overflow:visible;}",
      ".blockwriteai-preview .rbe-output-chart svg text{font-size:10px!important;font-weight:600!important;stroke-width:0!important;letter-spacing:0!important;}",
      ".blockwriteai-preview .rbe-output-chart svg rect{stroke-width:0!important;}",
      ".blockwriteai-preview .rbe-output-chart svg polyline{stroke-width:2.5!important;}",
      ".blockwriteai-preview .rbe-output-chart svg circle{stroke-width:0!important;}",
      ".blockwriteai-preview .rbe-output-chart-title{display:block;font-size:15px;font-weight:700;}",
      ".blockwriteai-preview .rbe-output-chart-caption{display:block;font-size:12px;font-weight:600;}",
      ".blockwriteai-preview .rbe-output-sheet-analyzer .rbe-output-chart{margin-top:10px;}",
      ".blockwriteai-preview .rbe-output-outline-list{list-style:none!important;margin-left:0!important;padding-left:0!important;}",
      ".blockwriteai-preview .rbe-output-outline-list li{display:grid;grid-template-columns:1.8em minmax(0,1fr);column-gap:.45em;align-items:start;list-style:none!important;margin:0 0 7px!important;padding-left:0!important;}",
      ".blockwriteai-preview .rbe-output-list-marker{display:block!important;min-width:0!important;margin-right:0!important;text-align:right;color:#667085;}",
      ".blockwriteai-preview .rbe-output-list-text{display:block!important;min-width:0;overflow-wrap:anywhere;}",
      ".blockwriteai-preview .rbe-output-outline-list .rbe-output-outline-list{grid-column:2;margin-top:6px!important;}",
      ".blockwriteai-preview .rbe-output-table-wrap{display:block;max-width:100%;overflow:auto;}",
      ".blockwriteai-preview .rbe-output-table-wrap table.rbe-output-filterable-table{width:100%!important;min-width:max-content;table-layout:auto!important;}",
      ".blockwriteai-preview .rbe-output-table-wrap th{min-width:128px;max-width:220px;white-space:normal;line-height:1.35;overflow-wrap:anywhere;word-break:normal;}",
      ".blockwriteai-preview .rbe-output-table-sort-button{display:grid;grid-template-columns:minmax(0,1fr) 18px;gap:7px;align-items:start;width:100%;min-width:0;margin:0;padding:0;border:0;background:transparent;color:inherit;font:inherit;font-weight:inherit;text-align:left;cursor:pointer;letter-spacing:0;}",
      ".blockwriteai-preview .rbe-output-table-sort-label{display:block;min-width:0;overflow-wrap:anywhere;word-break:normal;}",
      ".blockwriteai-preview .rbe-output-table-sort-icon{position:relative;display:inline-block;width:18px;height:18px;border:1px solid #b8c7dc;border-radius:4px;background:#fff;box-sizing:border-box;}",
      ".blockwriteai-preview .rbe-output-table-sort-icon:before,.blockwriteai-preview .rbe-output-table-sort-icon:after{content:\"\";position:absolute;left:5px;width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;}",
      ".blockwriteai-preview .rbe-output-table-sort-icon:before{top:4px;border-bottom:5px solid #64748b;}",
      ".blockwriteai-preview .rbe-output-table-sort-icon:after{bottom:4px;border-top:5px solid #64748b;}",
      ".blockwriteai-preview th[data-sort-dir='asc'] .rbe-output-table-sort-icon:before{border-bottom-color:#2563eb;}",
      ".blockwriteai-preview th[data-sort-dir='asc'] .rbe-output-table-sort-icon:after{opacity:.25;}",
      ".blockwriteai-preview th[data-sort-dir='desc'] .rbe-output-table-sort-icon:after{border-top-color:#2563eb;}",
      ".blockwriteai-preview th[data-sort-dir='desc'] .rbe-output-table-sort-icon:before{opacity:.25;}",
      ".blockwriteai-preview .rbe-output-table-filter{display:inline-flex;margin-left:6px;vertical-align:top;}"
    ].join("");
    (doc.head || doc.documentElement).appendChild(style);
  };

  BlockWriteAI.prototype.showExportHTMLPreview = function (html, options) {
    var editor = this;
    options = options || {};
    var existing = document.querySelector(".rbe-html-preview-modal");
    if (existing) existing.remove();
    var frame = el("iframe", "rbe-html-preview-frame", {
      title: "BlockWriteAI HTML preview",
      sandbox: "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"
    });
    var modal = el("div", "rbe-html-preview-modal", {
      role: "dialog",
        "aria-modal": "true",
        "aria-label": "BlockWriteAI HTML preview",
        html:
        '<div class="rbe-html-preview-shell">' +
        '<header class="rbe-html-preview-header"><strong>HTML Preview</strong>' +
        '<button type="button" class="rbe-html-preview-close" aria-label="Close preview">' +
        svgIcon("xmark", "Close preview") +
        "</button></header>" +
        '<div class="rbe-html-preview-body"></div>' +
        "</div>"
    });
    modal.querySelector(".rbe-html-preview-body").appendChild(frame);
    var onMessage = function (event) {
      var data = event && event.data;
      if (!data || data.source !== "BlockWriteAIExportPreviewComment") return;
      if (data.action === "reply") {
        editor.updateCommentReply(data.commentId, data.replyText || "", { name: data.memberName || "", role: data.memberRole || "" });
        return;
      }
      editor.updateCommentState(data.commentId, data.status, data.reason, { name: data.memberName || "", role: data.memberRole || "" });
    };
    var closeModal = function () {
      window.removeEventListener("message", onMessage);
      modal.remove();
    };
    modal.addEventListener("click", function (event) {
      if (event.target === modal || event.target.closest(".rbe-html-preview-close")) {
        closeModal();
      }
    });
    window.addEventListener("message", onMessage);
    document.body.appendChild(modal);
    frame.addEventListener("load", function () {
      if (options.a4Paginated) {
        editor.prepareA4PreviewDocument(frame.contentDocument);
      } else {
        editor.prepareHTMLPreviewDocument(frame.contentDocument);
      }
    }, { once: true });
    frame.srcdoc = html;
  };

  BlockWriteAI.prototype.exportHTML = function () {
    var editor = this;
    var alignAttr = function (align) {
      align = cleanAlignment(align);
      return align === "left" ? "" : ' style="text-align:' + align + '"';
    };
    var renderEmbedOutput = function (data) {
      data = data || {};
      var embed = toEmbed(data.source || data.embed);
      var embedUrl = embed.embed || data.embed;
      if (!embedUrl) {
        return '<p class="rbe-output-embed-link"><a href="' + escapeHTML(data.source || "") + '">' + escapeHTML(data.source || "") + "</a></p>";
      }
      return '<figure class="rbe-output-embed-figure"><iframe class="rbe-output-embed" src="' + escapeHTML(embedUrl) + '" title="' + escapeHTML(embed.service || data.service || "Embedded content") + ' embed" frameborder="0" referrerpolicy="strict-origin-when-cross-origin" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>' + (data.caption ? '<figcaption>' + escapeHTML(data.caption) + "</figcaption>" : "") + "</figure>";
    };
    var renderHTMLPreviewChart = function (data) {
      data = data || {};
      var chartTool = PLUGIN_TOOLS.chart;
      if (chartTool && typeof chartTool.toHTML === "function") {
        try {
          return chartTool.toHTML.call(editor, data, pluginHelpers());
        } catch (error) {
          if (window.console && typeof window.console.warn === "function") {
            window.console.warn("BlockWriteAI HTML preview chart renderer failed, using fallback chart.", error);
          }
        }
      }
      var labels = String(data.labels || "Q1,Q2,Q3,Q4").split(",").map(function (item) { return item.trim(); }).filter(Boolean);
      var values = String(data.values || "12,19,7,14").split(/[,\s;]+/).map(function (item) { return Number(item); }).filter(function (item) { return Number.isFinite(item); });
      var count = Math.max(labels.length, values.length, 1);
      for (var fillIndex = 0; fillIndex < count; fillIndex += 1) {
        if (!labels[fillIndex]) labels[fillIndex] = "Item " + (fillIndex + 1);
        if (!Number.isFinite(values[fillIndex])) values[fillIndex] = 0;
      }
      var width = Math.max(640, Math.min(2400, count * 76 + 140));
      var height = 300;
      var padLeft = 58;
      var padRight = 28;
      var padTop = 30;
      var padBottom = 48;
      var axisBottom = height - padBottom;
      var plotWidth = width - padLeft - padRight;
      var plotHeight = height - padTop - padBottom;
      var step = plotWidth / count;
      var max = Math.max.apply(null, values.map(function (value) { return Math.max(0, value); }).concat([1]));
      var color = /^#[0-9a-f]{3}([0-9a-f]{3})?$/i.test(String(data.color || "")) ? data.color : "#2563eb";
      var valueLabel = function (value) { return String(Math.round((Number(value) || 0) * 100) / 100); };
      var yFor = function (value) { return axisBottom - (Math.max(0, value) / max) * plotHeight; };
      var ticks = [0, max * 0.25, max * 0.5, max * 0.75, max];
      var axis = '<path d="M' + padLeft + " " + axisBottom + "H" + (width - padRight) + "M" + padLeft + " " + padTop + "V" + axisBottom + '" stroke="#cbd5e1" fill="none"/>' +
        ticks.map(function (tick) {
          var y = yFor(tick);
          return '<g><path d="M' + padLeft + " " + y + "H" + (width - padRight) + '" stroke="#e8eef6" fill="none"/><text x="' + (padLeft - 10) + '" y="' + (y + 4) + '" text-anchor="end" font-size="12" fill="#64748b">' + escapeHTML(valueLabel(tick)) + "</text></g>";
        }).join("") +
        labels.map(function (label, index) {
          var x = padLeft + step * index + step / 2;
          return '<text x="' + x + '" y="' + (height - 18) + '" text-anchor="middle" font-size="12" fill="#64748b">' + escapeHTML(label) + "</text>";
        }).join("");
      var bars = values.map(function (value, index) {
        var barHeight = (Math.max(0, value) / max) * plotHeight;
        var x = padLeft + step * index + step * 0.18;
        var y = axisBottom - barHeight;
        var barWidth = Math.max(8, step * 0.64);
        return '<g><rect x="' + x + '" y="' + y + '" width="' + barWidth + '" height="' + barHeight + '" rx="5" fill="' + escapeHTML(color) + '"><title>' + escapeHTML((labels[index] || "") + ": " + valueLabel(value)) + '</title></rect><text x="' + (x + barWidth / 2) + '" y="' + Math.max(padTop + 12, y - 6) + '" text-anchor="middle" font-size="12" fill="#344054" font-weight="700">' + escapeHTML(valueLabel(value)) + "</text></g>";
      }).join("");
      return '<figure class="rbe-output-chart">' + (data.title ? '<figcaption class="rbe-output-chart-title">' + escapeHTML(data.title) + "</figcaption>" : "") + '<svg style="--rbe-chart-svg-width:' + width + 'px" viewBox="0 0 ' + width + ' 300" role="img" aria-label="' + escapeHTML(data.title || "Chart") + '">' + axis + bars + "</svg>" + (data.caption ? '<figcaption class="rbe-output-chart-caption">' + renderOutputInline(data.caption) + "</figcaption>" : "") + "</figure>";
    };
    var renderTableOutput = function (data) {
      data = data || {};
      var tableAlign = data.align === "center" || data.align === "right" ? data.align : "left";
      var tableWidth = Math.min(100, Math.max(40, Number(data.width || 100)));
      var margin = tableAlign === "center" ? "margin-left:auto;margin-right:auto;" : tableAlign === "right" ? "margin-left:auto;margin-right:0;" : "margin-left:0;margin-right:auto;";
      var border = tableBorderValue(data);
      var rows = Array.isArray(data.rows) ? data.rows : [];
      var bodyRows = data.withHeadings ? rows.slice(1) : rows;
      var maxColumns = rows.reduce(function (count, row) {
        return Math.max(count, Array.isArray(row) ? row.length : 0);
      }, 0);
      var shouldFilter = !!(editor.options && editor.options.htmlPreviewMode && bodyRows.length > 5 && maxColumns);
      var tableClass = shouldFilter ? ' class="rbe-output-filterable-table"' : "";
      var headerCells = shouldFilter ? (data.withHeadings ? rows[0] || [] : []) : null;
      var headingHtml = shouldFilter ? "<thead><tr>" + Array.from({ length: maxColumns }).map(function (_, cellIndex) {
        var cell = headerCells && cellIndex < headerCells.length ? headerCells[cellIndex] : "Column " + (cellIndex + 1);
        var align = cellAlignment(cell);
        var cellStyle = "border:" + border + ";padding:8px;vertical-align:top;text-align:" + align + ";background:#eef5ff;font-weight:720;";
        return '<th style="' + cellStyle + '">' + renderOutputInline(cellText(cell)) + "</th>";
      }).join("") + "</tr></thead>" : "";
      var plainRows = shouldFilter && data.withHeadings ? rows.slice(1) : rows;
      var bodyHtml = plainRows.map(function (row, rowIndex) {
        row = Array.isArray(row) ? row : [];
        return "<tr>" + row.map(function (cell) {
          var tag = !shouldFilter && data.withHeadings && rowIndex === 0 ? "th" : "td";
          var align = cellAlignment(cell);
          var cellStyle = "border:" + border + ";padding:8px;vertical-align:top;text-align:" + align + ";";
          if (tag === "th") cellStyle += "background:#eef5ff;font-weight:720;";
          return '<' + tag + ' style="' + cellStyle + '">' + renderOutputInline(cellText(cell)) + "</" + tag + ">";
        }).join("") + "</tr>";
      }).join("");
      if (shouldFilter) bodyHtml = "<tbody>" + bodyHtml + "</tbody>";
      return '<div class="rbe-output-table-wrap' + (shouldFilter ? ' rbe-output-table-filter-wrap' : '') + '"><table' + tableClass + ' style="width:' + tableWidth + "%;" + margin + 'border-collapse:collapse;table-layout:fixed;">' + headingHtml + bodyHtml + "</table></div>";
    };
    var renderChildBlock = function (child) {
      var data = (child && child.data) || {};
      if (!child) return "";
      if (child.type === "paragraph") return "<p" + alignAttr(data.alignment) + ">" + renderOutputInline(data.text || "") + "</p>";
      if (child.type === "heading") return "<h" + (data.level || 2) + alignAttr(data.alignment) + ">" + renderOutputInline(data.text || "") + "</h" + (data.level || 2) + ">";
      if (child.type === "quote") return '<blockquote class="rbe-output-quote"' + alignAttr(data.alignment) + ">" + renderOutputInline(data.text || "") + (data.caption ? "<footer>" + escapeHTML(data.caption) + "</footer>" : "") + "</blockquote>";
      if (child.type === "list") return renderNestedList(data.items || [], data.style, data.alignment);
      if (child.type === "checklist") return renderNestedChecklist(data.items || []);
      if (child.type === "code") return renderCodeOutput(data);
      if (child.type === "raw") return rawOutputFrame(data.html || "");
      if (child.type === "image") {
        return '<figure class="rbe-output-image-figure"><div class="rbe-image-gallery">' + normalizeImageItems(data).map(function (item) {
          return imageOutputStageHTML(item);
        }).join("") + "</div></figure>";
      }
      if (child.type === "attaches") {
        return '<div class="rbe-attachment-grid">' + normalizeAttachmentItems(data).map(function (item) {
          var kind = attachmentKindKey(item.type, item.name);
          return '<a class="rbe-attachment-card" data-kind="' + escapeHTML(kind) + '" href="' + escapeHTML(item.url) + '" target="_blank" rel="noopener noreferrer"><span class="rbe-attachment-card-icon">' + svgIcon(attachmentIconName(item.type, item.name), "Attachment") + '</span><strong class="rbe-attachment-card-type">' + escapeHTML(attachmentKindLabel(item.type, item.name)) + '</strong><span class="rbe-attachment-card-name">' + escapeHTML(item.name || "Attachment") + "</span></a>";
        }).join("") + "</div>";
      }
      if (child.type === "table") {
        return renderTableOutput(data);
      }
      if (child.type === "callout") {
        var tone = calloutTone(data.tone);
        return '<aside class="rbe-output-callout rbe-output-callout-' + escapeHTML(tone) + '" style="' + calloutExportStyle(tone, data.alignment) + '">' + renderOutputInline(data.text || "") + "</aside>";
      }
      if (child.type === "embed") return renderEmbedOutput(data);
      if (child.type === "signatureRow") {
        var childRowBody = (Array.isArray(data.blocks) ? data.blocks : []).slice(0, MAX_BLOCKS_PER_ROW).map(renderChildBlock).filter(Boolean).map(function (html) {
          return '<div class="rbe-output-signature-row-item">' + html + "</div>";
        }).join("");
        return '<section class="rbe-output-signature-row rbe-output-multi-row">' + childRowBody + "</section>";
      }
      if (child.type === "toggle") {
        var toggleBody = Array.isArray(data.blocks) && data.blocks.length ? data.blocks.map(renderChildBlock).join("\n") : renderOutputInline(data.text || "");
        return '<details class="rbe-output-toggle"' + (data.open === false ? "" : " open") + '><summary>' + escapeHTML(data.title || "Details") + "</summary><div>" + toggleBody + "</div></details>";
      }
      if (child.type === "chart") return renderHTMLPreviewChart(data);
      if (PLUGIN_TOOLS[child.type] && typeof PLUGIN_TOOLS[child.type].toHTML === "function") {
        return PLUGIN_TOOLS[child.type].toHTML.call(editor, data, Object.assign(pluginHelpers(), { renderBlock: renderChildBlock }));
      }
      return "";
    };
    return this.getData()
      .blocks.map(function (block) {
        var data = block.data || {};
        if (block.type === "paragraph") return "<p" + alignAttr(data.alignment) + ">" + renderOutputInline(data.text || "") + "</p>";
        if (block.type === "heading") return "<h" + (data.level || 2) + alignAttr(data.alignment) + ">" + renderOutputInline(data.text || "") + "</h" + (data.level || 2) + ">";
        if (block.type === "quote") return '<blockquote class="rbe-output-quote"' + alignAttr(data.alignment) + ">" + renderOutputInline(data.text || "") + (data.caption ? "<footer>" + escapeHTML(data.caption) + "</footer>" : "") + "</blockquote>";
        if (block.type === "list") {
          return renderNestedList(data.items || [], data.style, data.alignment);
        }
        if (block.type === "checklist") {
          return renderNestedChecklist(data.items || []);
        }
        if (block.type === "code") return renderCodeOutput(data);
        if (block.type === "image") {
          var images = normalizeImageItems(data);
          return '<figure class="rbe-output-image-figure"><div class="rbe-image-gallery">' + images.map(function (item) {
            return imageOutputStageHTML(item);
          }).join("") + "</div></figure>";
        }
        if (block.type === "table") {
          return renderTableOutput(data);
        }
        if (block.type === "embed") {
          return renderEmbedOutput(data);
        }
        if (block.type === "callout") {
          var tone = calloutTone(data.tone);
          return '<aside class="rbe-output-callout rbe-output-callout-' + escapeHTML(tone) + '" style="' + calloutExportStyle(tone, data.alignment) + '">' + renderOutputInline(data.text || "") + "</aside>";
        }
        if (block.type === "delimiter") {
          var delimiterStyle = data.style === "dashed" || data.style === "dotted" || data.style === "thick" ? data.style : "line";
          return '<hr class="rbe-output-delimiter rbe-output-delimiter-' + escapeHTML(delimiterStyle) + '">';
        }
        if (block.type === "button") {
          var buttonAlign = cleanAlignment(data.alignment);
          var buttonUrl = isSafeUrl(data.url || "") ? data.url : "#";
          var buttonBg = data.background || "#2563eb";
          var buttonColor = data.color || "#ffffff";
          return '<p class="rbe-output-button-wrap" style="text-align:' + buttonAlign + '"><a class="rbe-button-link" style="background-color:' + escapeHTML(buttonBg) + ";color:" + escapeHTML(buttonColor) + '" href="' + escapeHTML(buttonUrl) + '" target="_blank" rel="noopener noreferrer">' + escapeHTML(data.text || "Button") + "</a></p>";
        }
        if (block.type === "toggle") {
          var toggleBody = Array.isArray(data.blocks) && data.blocks.length ? data.blocks.map(renderChildBlock).join("\n") : renderOutputInline(data.text || "");
          return '<details class="rbe-output-toggle"' + (data.open === false ? "" : " open") + '><summary>' + escapeHTML(data.title || "Details") + "</summary><div>" + toggleBody + "</div></details>";
        }
        if (block.type === "signatureRow") {
          var rowBody = (Array.isArray(data.blocks) ? data.blocks : []).slice(0, MAX_BLOCKS_PER_ROW).map(renderChildBlock).filter(Boolean).map(function (html) {
            return '<div class="rbe-output-signature-row-item">' + html + "</div>";
          }).join("");
          return '<section class="rbe-output-signature-row rbe-output-multi-row">' + rowBody + "</section>";
        }
        if (block.type === "linkPreview") {
          var linkUrl = isSafeUrl(data.url || "") ? data.url : "#";
          return '<a class="rbe-output-link-preview" href="' + escapeHTML(linkUrl) + '" target="_blank" rel="noopener noreferrer"><span class="rbe-link-preview-icon">' + svgIcon("arrow-up-right-from-square", "Open link") + '</span><strong>' + escapeHTML(data.title || data.url || "Link preview") + "</strong><span>" + escapeHTML(data.description || "") + "</span></a>";
        }
        if (block.type === "raw") return rawOutputFrame(data.html || "");
        if (block.type === "attaches") {
          var attachments = normalizeAttachmentItems(data);
          return '<div class="rbe-attachment-grid">' + attachments.map(function (item) {
            var kind = attachmentKindKey(item.type, item.name);
            return '<a class="rbe-attachment-card" data-kind="' + escapeHTML(kind) + '" href="' + escapeHTML(item.url) + '" target="_blank" rel="noopener noreferrer"><span class="rbe-attachment-card-icon">' + svgIcon(attachmentIconName(item.type, item.name), "Attachment") + '</span><strong class="rbe-attachment-card-type">' + escapeHTML(attachmentKindLabel(item.type, item.name)) + '</strong><span class="rbe-attachment-card-name">' + escapeHTML(item.name || "Attachment") + "</span></a>";
          }).join("") + "</div>";
        }
        if (block.type === "chart") return renderHTMLPreviewChart(data);
        if (PLUGIN_TOOLS[block.type] && typeof PLUGIN_TOOLS[block.type].toHTML === "function") {
          return PLUGIN_TOOLS[block.type].toHTML.call(this, data, Object.assign(pluginHelpers(), { renderBlock: renderChildBlock }));
        }
        return "";
      }, this)
      .join("\n");
  };

  BlockWriteAI.normalizePreviewData = function (payload) {
    if (!payload) return { time: Date.now(), blocks: [] };
    if (payload.data && Array.isArray(payload.data.blocks)) return payload.data;
    if (Array.isArray(payload.blocks)) return payload;
    return { time: Date.now(), blocks: [] };
  };

  BlockWriteAI.toHTML = function (data, options) {
    if (typeof document === "undefined" || !document.body) return "";
    var holder = el("div", "rbe-preview-renderer", { hidden: true });
    document.body.appendChild(holder);
    var editor = new BlockWriteAI(
      Object.assign(
        {
          holder: holder,
          data: BlockWriteAI.normalizePreviewData(data),
          readOnly: true,
          minHeight: "0px",
          autosave: null,
          saveButton: false,
          exportButton: false,
          shell: false,
          select2: false
        },
        options || {}
      )
    );
    var html = editor.exportHTML();
    editor.destroy();
    holder.remove();
    return html;
  };

  BlockWriteAI.exportDocument = function (data, type, options) {
    if (typeof document === "undefined" || !document.body) return null;
    type = type === "word" ? "word" : "pdf";
    options = options || {};
    var holder = el("div", "rbe-export-renderer", { hidden: true });
    holder.style.position = "fixed";
    holder.style.left = "-10000px";
    holder.style.top = "0";
    document.body.appendChild(holder);
    var editor = new BlockWriteAI(
      Object.assign(
        {
          holder: holder,
          data: BlockWriteAI.normalizePreviewData(data),
          readOnly: false,
          minHeight: "0px",
          autosave: null,
          saveButton: false,
          exportButton: false,
          exportHtmlButton: false,
          shell: false,
          select2: false
        },
        options.editor || options || {},
        {
          holder: holder
        }
      )
    );
    editor.savedExportData = BlockWriteAI.normalizePreviewData(data);
    editor.hasExplicitSave = true;
    editor.options.exportFilename = options.filename || options.exportFilename || editor.options.exportFilename;
    editor.options.exportSignatureRows = options.exportSignatureRows || [];
    editor.exportPreparedA4Document(type);
    setTimeout(function () {
      editor.destroy();
      holder.remove();
    }, 5000);
    return editor;
  };

  BlockWriteAI.buildA4PreviewDocument = function (data, options) {
    if (typeof document === "undefined" || !document.body) return Promise.resolve(null);
    options = options || {};
    var normalizedData = BlockWriteAI.normalizePreviewData(data);
    var holder = el("div", "rbe-export-renderer", { hidden: true });
    holder.style.position = "fixed";
    holder.style.left = "-10000px";
    holder.style.top = "0";
    document.body.appendChild(holder);
    var editor = new BlockWriteAI(
      Object.assign(
        {
          holder: holder,
          data: normalizedData,
          readOnly: false,
          minHeight: "0px",
          autosave: null,
          saveButton: false,
          exportButton: false,
          exportHtmlButton: false,
          shell: false,
          select2: false
        },
        options.editor || options || {},
        {
          holder: holder,
          persistence: null,
          previewRuntime: false
        }
      )
    );
    editor.savedExportData = normalizedData;
    editor.hasExplicitSave = true;
    editor.options.exportSignatureRows = options.exportSignatureRows || [];
    var html = editor.stripExportRuntimeScripts(editor.buildExportDocument());
    var frame = el("iframe", "", {
      title: "BlockWriteAI A4 preview preparation",
      sandbox: "allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"
    });
    frame.style.position = "fixed";
    frame.style.left = "-10000px";
    frame.style.top = "0";
    frame.style.width = "210mm";
    frame.style.height = "297mm";
    frame.style.visibility = "hidden";
    frame.setAttribute("aria-hidden", "true");
    document.body.appendChild(frame);
    return new Promise(function (resolve) {
      var cleanup = function () {
        try { editor.destroy(); } catch (error) {}
        if (holder && holder.parentNode) holder.remove();
        if (frame && frame.parentNode) frame.remove();
      };
      frame.addEventListener("load", function () {
        setTimeout(function () {
          try {
            editor.prepareA4PreviewDocument(frame.contentDocument);
            var preparedHtml = editor.currentA4PreviewHTML(frame, html);
            var parser = new DOMParser();
            var preparedDoc = parser.parseFromString(preparedHtml || "", "text/html");
            var main = preparedDoc.querySelector(".blockwriteai-export-page");
            var styles = Array.prototype.slice.call(preparedDoc.head.querySelectorAll("style")).map(function (style) {
              return style.textContent || "";
            }).join("\n");
            resolve({
              html: main ? main.innerHTML : "",
              styles: styles,
              className: main ? main.className : "blockwriteai-export-page blockwriteai-preview"
            });
          } catch (error) {
            resolve(null);
          } finally {
            cleanup();
          }
        }, 60);
      }, { once: true });
      frame.srcdoc = html;
    });
  };

  BlockWriteAI.normalizeLetterheadSettings = function (settings) {
    settings = Object.assign({}, settings || {});
    function assetUrl(value) {
      value = String(value || "").trim();
      if (!value) return "";
      return value
        .replace(/^https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d+)?\/blockwriteai-platform\/uploads\//i, "https://blockwriteai.in/uploads/")
        .replace(/^https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d+)?\/uploads\//i, "https://blockwriteai.in/uploads/");
    }
    settings.asset_base_url = settings.asset_base_url || settings.assetBaseUrl || "https://blockwriteai.in/";
    settings.assetBaseUrl = settings.asset_base_url;
    settings.logo_url = assetUrl(settings.logo_url || settings.logoUrl);
    settings.logoUrl = settings.logo_url;
    settings.logo_url_absolute = assetUrl(settings.logo_url_absolute || settings.logoUrlAbsolute || settings.logo_url);
    settings.logoUrlAbsolute = settings.logo_url_absolute;
    settings.watermark_url = assetUrl(settings.watermark_url || settings.watermarkUrl);
    settings.watermarkUrl = settings.watermark_url;
    settings.watermark_url_absolute = assetUrl(settings.watermark_url_absolute || settings.watermarkUrlAbsolute || settings.watermark_url);
    settings.watermarkUrlAbsolute = settings.watermark_url_absolute;
    return settings;
  };

  BlockWriteAI.loadLetterheadSettings = function (options) {
    options = options || {};
    var licenseKey = options.licenseKey || options.license_key || GLOBAL_SCOPE.BlockWriteAILicenseKey || "";
    var endpoint = options.letterheadSettingsEndpoint || options.letterheadEndpoint || PLATFORM_ENDPOINTS.letterheadSettings;
    if (!endpoint || !licenseKey || typeof fetch !== "function") return Promise.resolve(false);
    if (BlockWriteAI.__letterheadSettingsPromise) return BlockWriteAI.__letterheadSettingsPromise;
    BlockWriteAI.__letterheadSettingsPromise = fetch(endpoint, {
      method: "POST",
      headers: { "Content-Type": "application/json", Accept: "application/json" },
      body: JSON.stringify({ license_key: licenseKey, domain: GLOBAL_SCOPE.location && GLOBAL_SCOPE.location.origin || "" })
    })
      .then(function (response) {
        return response.json().catch(function () { return {}; }).then(function (payload) {
          if (!response.ok || !payload.ok || !payload.settings) return false;
          if (payload.settings && payload.settings.enabled === false) return false;
          var settings = Object.freeze(BlockWriteAI.normalizeLetterheadSettings(payload.settings));
          try {
            Object.defineProperty(GLOBAL_SCOPE, "BlockWriteAILetterHeadSettings", {
              value: settings,
              writable: false,
              configurable: false
            });
          } catch (error) {
            GLOBAL_SCOPE.BlockWriteAILetterHeadSettings = settings;
          }
          return true;
        });
      })
      .catch(function () { return false; });
    return BlockWriteAI.__letterheadSettingsPromise;
  };

  BlockWriteAI.prepareExternalExportRuntime = function (options) {
    options = options || {};
    if (BlockWriteAI.__externalExportRuntimePromise) return BlockWriteAI.__externalExportRuntimePromise;
    var licenseKey = options.licenseKey || options.license_key || GLOBAL_SCOPE.BlockWriteAILicenseKey || "";
    var pluginEndpoint = options.pluginEndpoint || options.premiumPluginEndpoint || PLATFORM_ENDPOINTS.premiumPlugins;
    BlockWriteAI.__externalExportRuntimePromise = Promise.resolve()
      .then(function () { return BlockWriteAI.loadLetterheadSettings(Object.assign({}, options, { licenseKey: licenseKey })); })
      .then(function () {
        if (!licenseKey || typeof BlockWriteAI.loadPremiumPlugins !== "function") return true;
        return BlockWriteAI.loadPremiumPlugins({
          endpoint: pluginEndpoint,
          licenseKey: licenseKey,
          features: options.features || ["drawing", "mermaid", "signature", "signature_flow", "letterhead", "ai"]
        }).catch(function () { return false; });
      });
    return BlockWriteAI.__externalExportRuntimePromise;
  };

  BlockWriteAI.documentFromExportResponse = function (payload) {
    payload = payload || {};
    var candidates = [
      payload.data,
      payload.document && payload.document.data,
      payload.document && payload.document.content_json,
      payload.content_json,
      payload.document
    ];
    for (var i = 0; i < candidates.length; i += 1) {
      var value = candidates[i];
      if (!value) continue;
      if (typeof value === "string") {
        try {
          value = JSON.parse(value);
        } catch (error) {
          value = null;
        }
      }
      if (value && Array.isArray(value.blocks)) return value;
      if (value && value.data && Array.isArray(value.data.blocks)) return value.data;
    }
    return null;
  };

  BlockWriteAI.fetchExportDocument = function (id, options) {
    options = options || {};
    if (!id) return Promise.resolve(null);
    if (typeof options.getDocument === "function") {
      return Promise.resolve(options.getDocument(id)).then(function (payload) {
        return BlockWriteAI.documentFromExportResponse(payload) || BlockWriteAI.normalizePreviewData(payload);
      });
    }
    var endpoint = options.documentEndpoint || options.endpoint || "api.php";
    if (!endpoint || typeof fetch !== "function") return Promise.resolve(null);
    return fetch(endpoint, {
      method: "POST",
      headers: Object.assign({ "Content-Type": "application/json", Accept: "application/json" }, options.headers || {}),
      body: JSON.stringify(Object.assign({ action: "document.get", id: id, document_id: id }, options.payload || {}))
    })
      .then(function (response) {
        return response.json().catch(function () { return {}; }).then(function (payload) {
          if (!response.ok || payload.ok === false) return null;
          return BlockWriteAI.documentFromExportResponse(payload);
        });
      })
      .catch(function () { return null; });
  };

  BlockWriteAI.normalizeExportSignaturePayload = function (row) {
    row = row || {};
    var payload = row.payload || row.payload_json || row.payloadJson || row.signature || row.data || {};
    if (typeof payload === "string") {
      try {
        payload = JSON.parse(payload);
      } catch (error) {
        payload = {};
      }
    }
    payload = Object.assign({}, payload || {});
    payload.name = payload.name || row.signer_name || row.name || "";
    payload.email = payload.email || row.signer_email || row.email || "";
    payload.role = payload.role || row.signer_role || row.role || "";
    payload.ipAddress = payload.ipAddress || payload.ip_address || row.ip_address || row.ipAddress || "";
    payload.signatureHash = payload.signatureHash || payload.signature_hash || row.signature_hash || row.signatureHash || "";
    payload.date = payload.date || payload.signed_at || row.signed_at || row.date || "";
    payload.dateLabel = payload.dateLabel || payload.date_label || row.date_label || payload.date || "";
    payload.signatureId = payload.signatureId || row.signature_id || row.signatureId || "";
    payload.signatureStorageKey = payload.signatureStorageKey || row.signature_key || row.signatureKey || "";
    return payload;
  };

  BlockWriteAI.primeExportSignatureRows = function (rows) {
    if (!Array.isArray(rows) || typeof localStorage === "undefined") return rows || [];
    rows.forEach(function (row) {
      row = row || {};
      var type = row.signature_type || row.signatureType || row.type || "signature";
      var key = row.signature_key || row.signatureKey || row.key || "";
      var payload = BlockWriteAI.normalizeExportSignaturePayload(row);
      if (!key) return;
      if (type === "signature_flow" || type === "signatureFlow") {
        var flowId = row.flow_id || row.flowId || payload.flowId || "";
        var stepId = row.step_id || row.stepId || payload.stepId || "";
        if ((!flowId || !stepId) && key.indexOf(":") !== -1) {
          var parts = key.split(":");
          flowId = flowId || parts[0] || "";
          stepId = stepId || parts.slice(1).join(":") || "";
        }
        if (flowId && stepId) {
          localStorage.setItem("blockwriteai-signature-flow:" + flowId + ":" + stepId, JSON.stringify(payload));
        }
      } else {
        localStorage.setItem("blockwriteai-signature:" + key, JSON.stringify(payload));
      }
    });
    return rows;
  };

  BlockWriteAI.documentIdFromExportOptions = function (data, options) {
    options = options || {};
    var persistence = options.persistence || options.Persistence || {};
    return options.documentId ||
      options.document_id ||
      options.id ||
      (persistence && (persistence.documentId || persistence.document_id || persistence.id)) ||
      (data && (data.documentId || data.document_id || data.id)) ||
      "";
  };

  BlockWriteAI.fetchExportSignatures = function (id, options) {
    options = options || {};
    if (!id) return Promise.resolve([]);
    if (Array.isArray(options.signatures)) return Promise.resolve(options.signatures);
    if (typeof options.getSignatures === "function") {
      return Promise.resolve(options.getSignatures(id)).then(function (payload) {
        return Array.isArray(payload) ? payload : (payload && payload.signatures) || [];
      });
    }
    var endpoint = options.signatureEndpoint || options.documentEndpoint || options.endpoint || "api.php";
    if (!endpoint || typeof fetch !== "function") return Promise.resolve([]);
    return fetch(endpoint, {
      method: "POST",
      headers: Object.assign({ "Content-Type": "application/json", Accept: "application/json" }, options.headers || {}),
      body: JSON.stringify(Object.assign({ action: "signature.list", id: id, document_id: id }, options.signaturePayload || {}))
    })
      .then(function (response) {
        return response.json().catch(function () { return {}; }).then(function (payload) {
          if (!response.ok || payload.ok === false) return [];
          return Array.isArray(payload) ? payload : (payload.signatures || payload.data || []);
        });
      })
      .catch(function () { return []; });
  };

  BlockWriteAI.primeExportSignaturesForDocument = function (data, options) {
    options = options || {};
    var rows = options.signatures || options.signatureRows || (data && (data.signatures || data.signatureRows));
    if (Array.isArray(rows)) {
      BlockWriteAI.primeExportSignatureRows(rows);
      return Promise.resolve(rows);
    }
    if (rows) {
      BlockWriteAI.primeSignatureData(rows);
      return Promise.resolve(rows);
    }
    var id = BlockWriteAI.documentIdFromExportOptions(data, options);
    if (!id) return Promise.resolve([]);
    return BlockWriteAI.fetchExportSignatures(id, options).then(function (fetchedRows) {
      BlockWriteAI.primeExportSignatureRows(fetchedRows);
      return fetchedRows;
    });
  };

  BlockWriteAI.bindExportButtons = function (options) {
    options = options || {};
    var explicitBindings = options.pdf || options.doc || options.word || options.bwaipdf || options.bwaidoc || options.exportPdfButton || options.exportWordButton;
    var bindings = explicitBindings ? [
      { target: options.pdf || options.bwaipdf || options.exportPdfButton, type: "pdf", icon: "file-export", label: "Generate PDF" },
      { target: options.doc || options.word || options.bwaidoc || options.exportWordButton, type: "word", icon: "file-lines", label: "Generate Word" }
    ] : Array.prototype.slice.call(document.querySelectorAll(options.selector || "[data-bwai-export],button[id^='pdf-'],button[id^='doc-']"))
      .map(function (button) {
        var id = button.getAttribute("id") || "";
        var type = button.getAttribute("data-bwai-export") || (/^doc-/i.test(id) ? "word" : "pdf");
        return { target: button, type: type === "word" || type === "doc" ? "word" : "pdf", icon: type === "word" || type === "doc" ? "file-lines" : "file-export", label: type === "word" || type === "doc" ? "Generate Word" : "Generate PDF" };
      });
    var buttons = [];
    bindings.forEach(function (binding) {
      var button = typeof binding.target === "string" ? document.querySelector(binding.target) : binding.target;
      if (!button) return;
      if (button.tagName && button.tagName.toLowerCase() === "button" && !button.getAttribute("type")) {
        button.setAttribute("type", "button");
      }
      button.classList.add("rbe-toolbar-btn", "rbe-external-export-btn");
      button.innerHTML = svgIcon(binding.icon, binding.label);
      button.setAttribute("title", binding.label);
      button.setAttribute("aria-label", binding.label);
      var listener = function (event) {
        event.preventDefault();
        event.stopPropagation();
        if (button.disabled) return;
        var originalDisabled = button.disabled;
        button.disabled = true;
        var id = button.getAttribute("data-bwai-document-id") || button.getAttribute("data-document-id") || (button.getAttribute("id") || "").replace(/^(?:pdf|doc)-/i, "");
        var data = typeof options.data === "function" ? options.data(button, binding.type) : options.data;
        var filename = typeof options.filename === "function" ? options.filename(button, binding.type) : options.filename || button.getAttribute("data-bwai-filename") || button.getAttribute("data-filename") || "blockwriteai-document";
        Promise.resolve(data || BlockWriteAI.fetchExportDocument(id, options))
          .then(function (resolved) {
            var documentData = BlockWriteAI.documentFromExportResponse(resolved) || (resolved && Array.isArray(resolved.blocks) ? resolved : null);
            if (!documentData) return null;
            var signatureRows = resolved && (resolved.signatures || resolved.signatureRows);
            var signatureOptions = Object.assign({}, options, { documentId: id });
            if (signatureRows) signatureOptions.signatures = signatureRows;
            return BlockWriteAI.primeExportSignaturesForDocument(documentData, signatureOptions).then(function (rows) {
              signatureOptions.exportSignatureRows = Array.isArray(rows) ? rows : [];
              return BlockWriteAI.prepareExternalExportRuntime(options);
            }).then(function () {
              return BlockWriteAI.exportDocument(documentData, binding.type, Object.assign({}, options, { filename: filename, exportSignatureRows: signatureOptions.exportSignatureRows }));
            });
          })
          .finally(function () {
            button.disabled = originalDisabled;
          });
      };
      button.addEventListener("click", listener);
      buttons.push({ button: button, listener: listener, type: binding.type });
    });
    return buttons;
  };

  BlockWriteAI.initPreviewPlugins = function (root) {
    var scope = typeof window !== "undefined" ? window : null;
    if (!scope) return root;

    if (scope.BlockWriteAISignature && typeof scope.BlockWriteAISignature.init === "function") {
      scope.BlockWriteAISignature.init(root || document);
    }

    if (scope.BlockWriteAISignatureFlow && typeof scope.BlockWriteAISignatureFlow.init === "function") {
      scope.BlockWriteAISignatureFlow.init(root || document);
    }

    return root;
  };

  BlockWriteAI.primeSignatureData = function (signatures) {
    if (!signatures || typeof localStorage === "undefined") return signatures;
    var items = signatures.items || signatures.signatures || {};
    if (Array.isArray(items)) {
      items.forEach(function (record) {
        var key = record && (record.storageKey || record.signatureId || record.id);
        if (key) localStorage.setItem("blockwriteai-signature:" + key, JSON.stringify(record));
      });
    } else {
      Object.keys(items).forEach(function (key) {
        if (items[key]) localStorage.setItem("blockwriteai-signature:" + key, JSON.stringify(items[key]));
      });
    }
    var flows = signatures.flows || {};
    Object.keys(flows).forEach(function (flowId) {
      var steps = (flows[flowId] && (flows[flowId].steps || flows[flowId])) || {};
      Object.keys(steps).forEach(function (stepId) {
        if (steps[stepId]) localStorage.setItem("blockwriteai-signature-flow:" + flowId + ":" + stepId, JSON.stringify(steps[stepId]));
      });
    });
    return signatures;
  };

  BlockWriteAI.patchPreviewCommentHTML = function (html, payload) {
    if (!html || typeof html !== "string" || html.indexOf("data-rbe-comment-id") === -1 || typeof document === "undefined") return html;
    payload = payload || {};
    var template = document.createElement("template");
    template.innerHTML = html;
    Array.prototype.slice.call(template.content.querySelectorAll(".rbe-comment-mark")).forEach(function (mark) {
      if (mark.getAttribute("data-rbe-comment-id") !== payload.commentId) return;
      if (payload.action === "reply") {
        var thread = parseCommentThread(mark.getAttribute("data-rbe-comment-thread") || "", mark.getAttribute("data-rbe-comment-reply") || "", mark.getAttribute("data-rbe-comment-reply-date") || "");
        var replyDate = payload.replyDate || new Date().toISOString();
        var replyText = String(payload.replyText || "");
        thread.push({ text: replyText, date: replyDate, memberName: payload.memberName || "", memberRole: payload.memberRole || "" });
        mark.setAttribute("data-rbe-comment-thread", serializeCommentThread(thread));
        mark.setAttribute("data-rbe-comment-reply", replyText);
        mark.setAttribute("data-rbe-comment-reply-date", replyDate);
        return;
      }
      var updatedAt = payload.updatedAt || new Date().toISOString();
      var status = normalizeCommentStatus(payload.status || "pending");
      mark.setAttribute("data-rbe-comment-status", status);
      mark.setAttribute("data-rbe-comment-updated", updatedAt);
      mark.setAttribute("data-rbe-comment-updated-member-name", payload.memberName || "");
      mark.setAttribute("data-rbe-comment-updated-member-role", payload.memberRole || "");
      if (payload.reason) mark.setAttribute("data-rbe-comment-reason", payload.reason);
      else mark.removeAttribute("data-rbe-comment-reason");
    });
    return template.innerHTML;
  };

  BlockWriteAI.patchPreviewCommentData = function (value, payload) {
    if (typeof value === "string") return BlockWriteAI.patchPreviewCommentHTML(value, payload);
    if (Array.isArray(value)) {
      value.forEach(function (item, index) {
        value[index] = BlockWriteAI.patchPreviewCommentData(item, payload);
      });
      return value;
    }
    if (value && typeof value === "object") {
      Object.keys(value).forEach(function (key) {
        value[key] = BlockWriteAI.patchPreviewCommentData(value[key], payload);
      });
    }
    return value;
  };

  BlockWriteAI.bindPreviewPersistence = function (holder, data, options) {
    var target = typeof holder === "string" ? document.querySelector(holder) : holder;
    if (!target || typeof document === "undefined") return null;
    options = options || {};
    var persistence = resolvePersistenceOptions(options.persistence || options.Persistence, options);
    if (!persistence) return null;
    if (target.__blockwriteaiPreviewPersistenceHandler) {
      document.removeEventListener("blockwriteai:preview-comment-change", target.__blockwriteaiPreviewPersistenceHandler);
    }
    var previewData = data || { blocks: [] };
    var saveDocument = persistence.saveDocument || persistence.save || persistence.onSaveDocument;
    var commentHooks = persistence.comments || persistence.comment || {};
    var saveComment = commentHooks.save || persistence.saveComment || persistence.onCommentChange;
    var handler = function (event) {
      var payload = event && event.detail;
      if (!payload || payload.source !== "BlockWriteAIExportPreviewComment" || !payload.commentId) return;
      if (!target.querySelector('.rbe-output-annotation[data-comment-id="' + String(payload.commentId).replace(/["\\]/g, "\\$&") + '"]')) return;
      BlockWriteAI.patchPreviewCommentData(previewData, payload);
      var documentId = persistenceDocumentId(persistence, options, previewData);
      if (persistence.localStorageKey) {
        try {
          localStorage.setItem(persistence.localStorageKey, JSON.stringify(previewData));
        } catch (error) {
        }
      }
      var commentPayload = {
        type: "comment",
        action: payload.action || "change",
        documentId: documentId,
        commentId: payload.commentId,
        comment: payload,
        data: previewData,
        version: VERSION
      };
      callPersistenceHook(saveComment, commentPayload, null).catch(function () {});
      callPersistenceHook(saveDocument, {
        type: "document",
        action: "preview-comment-" + (payload.action || "change"),
        documentId: documentId,
        data: previewData,
        detail: payload,
        version: VERSION
      }, null).catch(function () {});
    };
    target.__blockwriteaiPreviewPersistenceData = previewData;
    target.__blockwriteaiPreviewPersistenceHandler = handler;
    document.addEventListener("blockwriteai:preview-comment-change", handler);
    return handler;
  };

  BlockWriteAI.bindSignaturePersistence = function (holder, options) {
    var target = typeof holder === "string" ? document.querySelector(holder) : holder;
    if (!target || typeof document === "undefined") return null;
    options = options || {};
    var persistence = resolvePersistenceOptions(options.persistence || options.Persistence, options);
    if (!persistence) return null;
    if (target.__blockwriteaiSignaturePersistenceHandlers) {
      target.__blockwriteaiSignaturePersistenceHandlers.forEach(function (entry) {
        document.removeEventListener(entry.name, entry.handler);
      });
    }
    var signatureHooks = persistence.signatures || persistence.signature || {};
    var saveSignature = signatureHooks.save || persistence.saveSignature || persistence.onSignatureSigned || persistence.save;
    var handle = function (event, signatureType) {
      if (!event || !target.contains(event.target)) return;
      var box = signatureType === "signature_flow" ? event.target.closest(".rbe-flow-signature-card") : event.target.closest(".rbe-output-signature");
      if (!box) return;
      var flow = signatureType === "signature_flow" && box.closest ? box.closest(".rbe-output-signature-flow") : null;
      var flowId = flow ? flow.dataset.flowId || "" : "";
      var stepId = signatureType === "signature_flow" ? box.dataset.stepId || "" : "";
      var signatureKey = signatureType === "signature_flow" ? flowId + ":" + stepId : box.dataset.signatureStorageKey || box.dataset.signatureId || "";
      var payload = {
        type: "signature",
        action: "signed",
        signatureType: signatureType,
        signatureKey: signatureKey,
        documentId: persistenceDocumentId(persistence, options, {}),
        flowId: flowId,
        stepId: stepId,
        signatureId: signatureType === "signature" ? box.dataset.signatureId || "" : "",
        signature: event.detail || {},
        version: VERSION
      };
      callPersistenceHook(saveSignature, payload, null).catch(function () {});
    };
    var singleHandler = function (event) {
      handle(event, "signature");
    };
    var flowHandler = function (event) {
      handle(event, "signature_flow");
    };
    document.addEventListener("blockwriteai:signature-signed", singleHandler);
    document.addEventListener("blockwriteai:signature-flow-signed", flowHandler);
    target.__blockwriteaiSignaturePersistenceHandlers = [
      { name: "blockwriteai:signature-signed", handler: singleHandler },
      { name: "blockwriteai:signature-flow-signed", handler: flowHandler }
    ];
    return target.__blockwriteaiSignaturePersistenceHandlers;
  };

  BlockWriteAI.activatePreviewRuntime = function (data, options) {
    if (typeof document === "undefined" || typeof window === "undefined") return false;
    if (window.__blockwriteaiPreviewRuntimeActive) return true;
    var holder = document.createElement("div");
    holder.hidden = true;
    document.body.appendChild(holder);
    var editor = new BlockWriteAI(
      Object.assign({}, options || {}, {
        holder: holder,
        data: data,
        readOnly: true,
        minHeight: "0px",
        autosave: null,
        persistence: null,
        saveButton: false,
        exportButton: false,
        shell: false,
        select2: false
      })
    );
    var runtimeHTML = editor.buildExportPreviewRuntime();
    editor.destroy();
    holder.remove();
    var template = document.createElement("template");
    template.innerHTML = runtimeHTML;
    var runtimeScript = template.content.querySelector("script");
    if (!runtimeScript) return false;
    var script = document.createElement("script");
    script.textContent = runtimeScript.textContent || "";
    document.body.appendChild(script);
    script.remove();
    window.__blockwriteaiPreviewRuntimeActive = true;
    return true;
  };

  BlockWriteAI.renderPreview = function (holder, data, options) {
    var target = typeof holder === "string" ? document.querySelector(holder) : holder;
    if (!target) return null;
    options = options || {};
    if (options.a4Paginated === true) {
      BlockWriteAI.renderA4Preview(target, data, options);
      return target;
    }
    var previewData = BlockWriteAI.normalizePreviewData(data);
    BlockWriteAI.primeSignatureData(previewData && previewData.signatures);
    target.classList.add("blockwriteai-preview");
    target.classList.toggle(
      "blockwriteai-preview-dark",
      options.theme === "dark" || options.themeMode === "dark" || options.dark === true
    );
    target.innerHTML = BlockWriteAI.toHTML(previewData, options);
    target.dataset.blockwriteaiPreviewMounted = "true";
    BlockWriteAI.initPreviewPlugins(target);
    BlockWriteAI.bindPreviewPersistence(target, previewData, options);
    BlockWriteAI.bindSignaturePersistence(target, options);
    if (options && options.previewRuntime === true) BlockWriteAI.activatePreviewRuntime(previewData, options);
    return target;
  };

  BlockWriteAI.renderA4Preview = function (holder, data, options) {
    var target = typeof holder === "string" ? document.querySelector(holder) : holder;
    if (!target) return Promise.resolve(null);
    options = options || {};
    var previewData = BlockWriteAI.normalizePreviewData(data);
    BlockWriteAI.primeSignatureData(previewData && previewData.signatures);
    target.classList.add("blockwriteai-preview", "blockwriteai-export-page");
    target.classList.toggle(
      "blockwriteai-preview-dark",
      options.theme === "dark" || options.themeMode === "dark" || options.dark === true
    );
    target.dataset.blockwriteaiPreviewMounted = "pending";
    return BlockWriteAI.primeExportSignaturesForDocument(previewData, options).then(function (rows) {
      if (Array.isArray(rows)) options.exportSignatureRows = rows;
      return BlockWriteAI.buildA4PreviewDocument(previewData, options);
    }).then(function (prepared) {
      if (!prepared || !prepared.html) {
        target.innerHTML = BlockWriteAI.toHTML(previewData, options);
      } else {
        var styleId = "blockwriteai-a4-preview-styles";
        var style = document.getElementById(styleId);
        if (!style) {
          style = document.createElement("style");
          style.id = styleId;
          document.head.appendChild(style);
        }
        style.textContent = prepared.styles || "";
        target.className = prepared.className || target.className;
        target.classList.add("blockwriteai-preview", "blockwriteai-export-page");
        target.innerHTML = prepared.html;
      }
      target.dataset.blockwriteaiPreviewMounted = "true";
      BlockWriteAI.initPreviewPlugins(target);
      BlockWriteAI.bindPreviewPersistence(target, previewData, options);
      BlockWriteAI.bindSignaturePersistence(target, options);
      if (options && options.previewRuntime === true) BlockWriteAI.activatePreviewRuntime(previewData, options);
      return target;
    });
  };

  BlockWriteAI.mountPreviews = function (options) {
    options = options || {};
    var selector = options.selector || ".blockwriteai-preview, [data-blockwriteai-preview]";
    var targets = Array.prototype.slice.call(document.querySelectorAll(selector));
    return Promise.all(
      targets.map(function (target) {
        if (!options.force && target.dataset.blockwriteaiPreviewMounted === "true") return Promise.resolve(target);
        var inlineData = target.dataset.blockwriteaiJson || "";
        var script = target.querySelector('script[type="application/json"]');
        var source = target.dataset.source || target.dataset.blockwriteaiSource || "";
        var render = function (payload) {
          BlockWriteAI.renderPreview(target, payload, options.editor || {});
          return target;
        };
        if (inlineData) {
          try {
            return Promise.resolve(render(JSON.parse(inlineData)));
          } catch (error) {
            target.innerHTML = "<p>Invalid BlockWriteAI preview JSON.</p>";
            return Promise.resolve(target);
          }
        }
        if (script && script.textContent.trim()) {
          try {
            return Promise.resolve(render(JSON.parse(script.textContent)));
          } catch (error) {
            target.innerHTML = "<p>Invalid BlockWriteAI preview JSON.</p>";
            return Promise.resolve(target);
          }
        }
        if (source) {
          return fetch(source, { headers: { Accept: "application/json" }, cache: "no-store" })
            .then(function (response) {
              if (!response.ok) throw new Error("Unable to load BlockWriteAI preview JSON");
              return response.json();
            })
            .then(render)
            .catch(function (error) {
              target.innerHTML = "<p>" + escapeHTML(error.message || "Unable to load BlockWriteAI preview.") + "</p>";
              return target;
            });
        }
        if (options.data) return Promise.resolve(render(options.data));
        if (window.BlockWriteAIPreviewData) return Promise.resolve(render(window.BlockWriteAIPreviewData));
        if (window.BlockWriteAIPreviewData) return Promise.resolve(render(window.BlockWriteAIPreviewData));
        target.innerHTML = "<p>No preview content available.</p>";
        target.dataset.blockwriteaiPreviewMounted = "true";
        return Promise.resolve(target);
      })
    );
  };

  BlockWriteAI.prototype.exportMarkdown = function () {
    return this.getData()
      .blocks.map(function (block) {
        var data = block.data || {};
        var text = function (html) {
          var div = document.createElement("div");
          div.innerHTML = sanitizeHTML(html || "");
          return div.textContent || "";
        };
        if (block.type === "paragraph") return text(data.text);
        if (block.type === "heading") return Array(Number(data.level || 2) + 1).join("#") + " " + text(data.text);
        if (block.type === "quote") return "> " + text(data.text) + (data.caption ? "\n> -- " + data.caption : "");
        if (block.type === "list") {
          var counters = [0, 0, 0, 0, 0];
          return (data.items || []).map(normalizeListItem).filter(function (item) { return !isEmptyListItem(item); }).map(function (item) {
            var level = listItemLevel(item);
            counters[level] += 1;
            for (var i = level + 1; i < counters.length; i += 1) counters[i] = 0;
            return Array(level + 1).join("  ") + (data.style === "ordered" ? counters[level] + ". " : "- ") + text(item.text);
          }).join("\n");
        }
        if (block.type === "checklist") {
          return (data.items || []).map(normalizeChecklistItem).filter(function (item) { return !isEmptyListItem(item); }).map(function (item) {
            var level = listItemLevel(item);
            return Array(level + 1).join("  ") + "- [" + (item.checked ? "x" : " ") + "] " + text(item.text);
          }).join("\n");
        }
        if (block.type === "code") return "```" + (data.language || "") + "\n" + (data.code || "") + "\n```";
        if (block.type === "image") return normalizeImageItems(data).map(function (item) { return "![" + (item.alt || item.caption || "") + "](" + item.url + ")"; }).join("\n");
        if (block.type === "delimiter") return "---";
        if (block.type === "embed") return data.source || data.embed || "";
        if (block.type === "callout") return "> " + text(data.text);
        if (block.type === "button") return "[" + (data.text || "Button") + "](" + (data.url || "#") + ")";
        if (block.type === "toggle") return "<details>\n<summary>" + (data.title || "Details") + "</summary>\n\n" + text(data.text) + "\n</details>";
        if (block.type === "signatureRow") {
          return (Array.isArray(data.blocks) ? data.blocks : []).slice(0, MAX_BLOCKS_PER_ROW).map(function (child) {
            return BlockWriteAI.prototype.exportMarkdown.call({ getData: function () { return { blocks: [child] }; } });
          }).filter(Boolean).join("\n\n");
        }
        if (block.type === "linkPreview") return "[" + (data.title || data.url || "Link preview") + "](" + (data.url || "#") + ")" + (data.description ? "\n" + data.description : "");
        if (block.type === "raw") return data.html || "";
        if (block.type === "attaches") return normalizeAttachmentItems(data).map(function (item) { return "[" + (item.name || "Attachment") + "](" + item.url + ")"; }).join("\n");
        if (PLUGIN_TOOLS[block.type] && typeof PLUGIN_TOOLS[block.type].toMarkdown === "function") return PLUGIN_TOOLS[block.type].toMarkdown.call(this, data, Object.assign(pluginHelpers(), { text: text }));
        return "";
      }, this)
      .join("\n\n");
  };

  BlockWriteAI.prototype.importHTML = function (html) {
    var template = document.createElement("template");
    template.innerHTML = html;
    var blocks = [];
    Array.prototype.slice.call(template.content.children).forEach(function (node) {
      var tag = node.tagName.toLowerCase();
      if (/h[1-4]/.test(tag)) blocks.push({ type: "heading", data: { level: Number(tag.slice(1)), text: sanitizeHTML(node.innerHTML) } });
      else if (tag === "blockquote") blocks.push({ type: "quote", data: { text: sanitizeHTML(node.innerHTML), caption: "" } });
      else if (tag === "ul" || tag === "ol") blocks.push({ type: "list", data: { style: tag === "ol" ? "ordered" : "unordered", items: Array.prototype.slice.call(node.children).map(function (li) { return sanitizeHTML(li.innerHTML); }) } });
      else if (tag === "figure" && node.querySelector("img")) blocks.push({ type: "image", data: { url: node.querySelector("img").src, caption: (node.querySelector("figcaption") || {}).textContent || "" } });
      else if (tag === "pre") blocks.push({ type: "code", data: { code: node.textContent || "", language: "" } });
      else if (tag === "hr") blocks.push({ type: "delimiter", data: {} });
      else blocks.push({ type: "paragraph", data: { text: sanitizeHTML(node.innerHTML || node.textContent) } });
    });
    this.render({ blocks: blocks });
  };

  return BlockWriteAI;
});
