/*!
 * BlockWriteAI Advanced Blocks Plugin
 * Adds chart, LaTeX, audio, and columns/layout blocks.
 */
(function (global) {
  "use strict";

  var BlockWriteAI = global.BlockWriteAI;
  if (!BlockWriteAI || BlockWriteAI.__advancedBlocksInstalled) return;
  BlockWriteAI.__advancedBlocksInstalled = true;

  var H = BlockWriteAI.helpers || {};
  var el = H.el;
  var esc = H.escapeHTML || function (value) {
    return String(value == null ? "" : value).replace(/[&<>"]/g, function (ch) {
      return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[ch];
    });
  };
  var sanitize = H.sanitizeHTML || function (value) { return String(value || ""); };
  var inline = H.renderOutputInline || sanitize;
  var svgIcon = H.svgIcon || function (name, label) {
    name = String(name || "circle").trim();
    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="' + esc(name) + '"><circle cx="12" cy="12" r="8"/></svg><span class="rbe-sr-only">' + esc(label || name) + "</span>";
  };

  function injectStyles() {
    if (document.querySelector("style[data-blockwriteai-advanced-blocks]")) {
      bindOutputChartZoom();
      return;
    }
    var style = document.createElement("style");
    style.dataset.blockwriteaiAdvancedBlocks = "true";
    style.textContent =
      ".rbe-plugin-card{border:1px solid var(--rbe-border,#d7dee8);border-radius:8px;background:#fff;padding:12px;display:grid;gap:12px;}" +
      ".rbe-plugin-toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;}" +
      ".rbe-plugin-toolbar label{display:inline-flex;align-items:center;gap:7px;font-size:12px;font-weight:750;color:#667085;}" +
      ".rbe-plugin-toolbar input,.rbe-plugin-toolbar select,.rbe-plugin-card textarea{border:1px solid var(--rbe-border,#d7dee8);border-radius:7px;background:#fff;color:#172033;font:14px/1.4 Arial,sans-serif;padding:8px 9px;}" +
      ".rbe-plugin-toolbar input[type='color']{width:40px;min-width:40px;height:34px;padding:3px;}" +
      ".rbe-plugin-card textarea{width:100%;min-height:150px;resize:vertical;font-family:Consolas,'Liberation Mono',monospace;}" +
      ".rbe-plugin-preview{border:1px dashed #cbd5e1;border-radius:8px;background:#f8fbff;min-height:120px;padding:12px;overflow:auto;}" +
      ".rbe-chart-block{position:relative;min-width:0;max-width:100%;overflow:hidden;}" +
      ".rbe-chart-block .rbe-plugin-toolbar{align-items:flex-start;min-width:0;max-width:100%;}" +
      ".rbe-chart-block .rbe-chart-title,.rbe-chart-block .rbe-chart-labels,.rbe-chart-block .rbe-chart-values{flex:1 1 260px;min-width:min(220px,100%);max-width:100%;}" +
      ".rbe-chart-preview-wrap{position:relative;min-width:0;max-width:100%;overflow:hidden;}" +
      ".rbe-chart-zoom-bar{position:absolute;right:10px;top:10px;z-index:5;display:flex;gap:5px;align-items:center;border:1px solid #cbd5e1;border-radius:999px;background:rgba(255,255,255,.96);padding:4px;box-shadow:0 10px 24px rgba(15,23,42,.12);}" +
      ".rbe-chart-zoom-btn{width:28px;height:28px;border:1px solid #d7dee8;border-radius:999px;background:#fff;color:#344054;display:inline-grid;place-items:center;cursor:pointer;font-weight:900;line-height:1;}" +
      ".rbe-chart-zoom-btn:hover,.rbe-chart-zoom-btn:focus-visible{border-color:#2563eb;background:#eaf1ff;color:#1d4ed8;}" +
      ".rbe-chart-zoom-value{min-width:38px;text-align:center;font-size:11px;font-weight:800;color:#475467;}" +
      ".rbe-chart-type-bar{position:absolute;right:10px;bottom:10px;z-index:4;display:flex;gap:6px;align-items:center;border:1px solid #cbd5e1;border-radius:999px;background:rgba(255,255,255,.96);padding:7px;box-shadow:0 16px 40px rgba(15,23,42,.16);opacity:0;pointer-events:none;transform:translateY(6px) scale(.96);transition:opacity .16s ease,transform .16s ease;}" +
      ".rbe-chart-preview-wrap:hover .rbe-chart-type-bar,.rbe-chart-type-bar:focus-within{opacity:1;pointer-events:auto;transform:translateY(0) scale(1);}" +
      ".rbe-chart-type-btn{width:34px;height:34px;border:1px solid #d7dee8;border-radius:999px;background:#fff;color:#344054;display:inline-grid;place-items:center;cursor:pointer;}" +
      ".rbe-chart-type-btn:hover,.rbe-chart-type-btn.is-active{border-color:#2563eb;background:#eaf1ff;color:#1d4ed8;}" +
      ".rbe-chart-slice-colors{display:flex;flex-wrap:wrap;gap:8px;align-items:center;}" +
      ".rbe-chart-slice-colors[hidden],.rbe-chart-series-color[hidden]{display:none!important;}" +
      ".rbe-chart-slice-label{font-size:12px;font-weight:800;color:#667085;}" +
      ".rbe-chart-slice-list{display:flex;flex-wrap:wrap;gap:6px;align-items:center;}" +
      ".rbe-chart-slice-chip{display:inline-flex;align-items:center;gap:4px;border:1px solid #d7dee8;border-radius:999px;background:#fff;padding:3px 6px;font-size:11px;font-weight:750;color:#475467;}" +
      ".rbe-chart-slice-chip input{width:28px!important;min-width:28px!important;height:24px!important;padding:2px!important;border-radius:999px;}" +
      ".rbe-chart-preview{display:block;width:100%;max-width:100%;min-width:0;box-sizing:border-box;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-width:thin;text-align:center;contain:inline-size;}" +
      ".rbe-chart-preview svg{display:block;width:min(var(--rbe-chart-svg-width,640px),5200px)!important;min-width:0;height:auto;min-height:230px;max-height:330px;max-width:none;margin:0 auto;overflow:visible;}" +
      ".rbe-output-chart{max-width:100%;overflow-x:auto;overflow-y:visible;overscroll-behavior-x:contain;scrollbar-width:thin;}" +
      ".rbe-output-chart svg{display:block;width:min(var(--rbe-chart-svg-width,760px),6400px)!important;max-width:none;height:auto;min-height:340px;max-height:none;margin:0 auto;overflow:visible;}" +
      ".rbe-output-chart,.rbe-output-mermaid,.rbe-output-latex,.rbe-output-audio,.rbe-output-drawing,.rbe-output-columns{margin:0 0 15px;}" +
      ".rbe-output-chart{position:relative;border:1px solid #d7dee8;border-radius:8px;padding:12px;background:#fff;}" +
      ".rbe-output-chart .rbe-chart-zoom-bar{right:12px;top:10px;}" +
      ".rbe-output-chart-title{display:block;margin-bottom:8px;font-weight:760;}" +
      ".rbe-chart-caption{width:min(560px,100%);min-height:42px;margin:0 auto;border:1px solid var(--rbe-border,#d7dee8);border-radius:8px;background:#fff;padding:10px 12px;text-align:center;outline:none;}" +
      ".rbe-chart-caption:focus{border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,.12);}" +
      ".rbe-chart-caption:empty:before{content:attr(data-placeholder);color:#98a2b3;}" +
      ".rbe-output-chart-caption{display:block;margin:10px auto 0;color:#475467;font-size:13px;font-weight:650;}" +
      ".rbe-latex-preview,.rbe-output-latex{font-family:Georgia,'Times New Roman',serif;font-size:20px;line-height:1.6;text-align:center;font-style:italic;}" +
      ".rbe-output-latex{display:block;width:100%;}" +
      ".rbe-latex-frac{display:inline-grid;grid-template-rows:auto auto;vertical-align:middle;text-align:center;line-height:1.1;}.rbe-latex-frac span:first-child{border-bottom:1px solid currentColor;padding:0 2px;}" +
      ".rbe-latex-loading{color:#667085;font:13px/1.45 Arial,sans-serif;font-style:normal;}" +
      ".rbe-latex-error{border:1px solid #fecaca;border-radius:8px;background:#fff1f2;color:#b42318;padding:10px 12px;text-align:left;font:13px/1.45 Arial,sans-serif;font-style:normal;}" +
      ".rbe-latex-error strong{display:block;margin-bottom:4px;font-weight:800;}" +
      ".rbe-latex-error small{display:block;color:#7f1d1d;word-break:break-word;}" +
      ".rbe-output-align-left{text-align:left;}" +
      ".rbe-output-align-center{text-align:center;}" +
      ".rbe-output-align-right{text-align:right;}" +
      ".rbe-output-align-center>.rbe-output-mermaid-diagram,.rbe-output-align-center>.rbe-output-mermaid,.rbe-output-align-center>svg{margin-left:auto;margin-right:auto;}" +
      ".rbe-output-align-right>.rbe-output-mermaid-diagram,.rbe-output-align-right>.rbe-output-mermaid,.rbe-output-align-right>svg{margin-left:auto;margin-right:0;}" +
      ".rbe-mermaid-block{gap:10px;}" +
      ".rbe-mermaid-card{position:relative;min-height:220px;display:grid;gap:10px;align-items:stretch;border:1px solid #d7dee8;border-radius:10px;background:#fbfdff;padding:14px;}" +
      ".rbe-mermaid-card-head{display:flex;align-items:center;justify-content:space-between;gap:10px;}" +
      ".rbe-mermaid-card-title{font-weight:820;color:#172033;font-size:14px;}" +
      ".rbe-mermaid-card-preview{min-height:160px;display:grid;place-items:center;border:1px dashed #cbd5e1;border-radius:9px;background:#fff;padding:12px;overflow:auto;}" +
      ".rbe-mermaid-card-preview .rbe-output-mermaid-diagram{max-width:100%;width:100%;}" +
      ".rbe-mermaid-card-empty{color:#667085;font-weight:750;font-size:13px;}" +
      ".rbe-mermaid-edit-btn{position:absolute;right:12px;top:12px;width:34px;height:34px;border:1px solid #cbd5e1;border-radius:999px;background:#fff;color:#172033;display:grid;place-items:center;cursor:pointer;box-shadow:0 12px 28px rgba(15,23,42,.12);}" +
      ".rbe-mermaid-edit-btn:empty:before{content:'';width:17px;height:17px;background:currentColor;display:block;-webkit-mask:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 20h4l11-11-4-4L4 16z'/%3E%3Cpath d='m13 7 4 4'/%3E%3Cpath d='M14 5l1-1a2 2 0 0 1 3 0l2 2a2 2 0 0 1 0 3l-1 1'/%3E%3C/svg%3E\") center/contain no-repeat;mask:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 20h4l11-11-4-4L4 16z'/%3E%3Cpath d='m13 7 4 4'/%3E%3Cpath d='M14 5l1-1a2 2 0 0 1 3 0l2 2a2 2 0 0 1 0 3l-1 1'/%3E%3C/svg%3E\") center/contain no-repeat;}" +
      ".rbe-mermaid-edit-btn:hover,.rbe-mermaid-edit-btn:focus-visible{border-color:#2563eb;color:#1d4ed8;background:#eaf1ff;}" +
      ".rbe-mermaid-modal-open{overflow:hidden!important;}" +
      ".rbe-mermaid-modal{position:fixed;inset:0;z-index:10000;background:rgba(15,23,42,.46);display:grid;place-items:stretch;padding:22px;}" +
      ".rbe-mermaid-dialog{display:grid;grid-template-rows:auto 1fr auto;min-height:0;border-radius:14px;background:#fff;box-shadow:0 28px 80px rgba(15,23,42,.32);overflow:hidden;}" +
      ".rbe-mermaid-modal-head,.rbe-mermaid-modal-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border-bottom:1px solid #e2e8f0;}" +
      ".rbe-mermaid-modal-foot{border-top:1px solid #e2e8f0;border-bottom:0;justify-content:flex-end;}" +
      ".rbe-mermaid-modal-title{margin:0;font-size:17px;font-weight:820;color:#172033;}" +
      ".rbe-mermaid-close{width:34px;height:34px;border:1px solid #cbd5e1;border-radius:999px;background:#fff;display:grid;place-items:center;cursor:pointer;}" +
      ".rbe-mermaid-modal-save,.rbe-mermaid-modal-cancel{border:1px solid #cbd5e1;border-radius:8px;background:#fff;color:#172033;font-weight:800;padding:9px 14px;cursor:pointer;}" +
      ".rbe-mermaid-modal-save{border-color:#2563eb;background:#2563eb;color:#fff;}" +
      ".rbe-mermaid-modal-body{min-height:0;overflow:hidden;padding:0;background:#f8fafc;}" +
      ".rbe-mermaid-modal .rbe-mermaid-block{height:100%;border:0;border-radius:0;background:transparent;padding:0;display:grid;grid-template-rows:auto 1fr;gap:0;}" +
      ".rbe-mermaid-toolbar{display:flex;flex-wrap:wrap;gap:7px;align-items:center;padding:8px;border:1px solid #d7dee8;border-radius:12px;background:#eef4fb;}" +
      ".rbe-mermaid-modal .rbe-mermaid-toolbar{position:relative;z-index:5;border-width:0 0 1px;border-radius:0;background:#edf2f8;padding:9px 14px;}" +
      ".rbe-mermaid-tool,.rbe-mermaid-icon-btn{width:34px;height:34px;border:1px solid transparent;border-radius:8px;background:transparent;color:#344054;display:inline-grid;place-items:center;cursor:pointer;}" +
      ".rbe-mermaid-icon-btn{border-color:#d7dee8;background:#fff;}" +
      ".rbe-mermaid-tool:hover,.rbe-mermaid-tool.is-active,.rbe-mermaid-icon-btn:hover,.rbe-mermaid-icon-btn.is-active{background:#dbeafe;border-color:#93c5fd;color:#1d4ed8;}" +
      ".rbe-mermaid-tool:disabled,.rbe-mermaid-icon-btn:disabled{opacity:.45;cursor:not-allowed;}" +
      ".rbe-mermaid-menu{position:relative;}" +
      ".rbe-mermaid-menu-btn{width:34px;height:34px;border:1px solid transparent;border-radius:8px;background:transparent;color:#344054;display:inline-grid;place-items:center;cursor:pointer;}" +
      ".rbe-mermaid-menu-btn:hover,.rbe-mermaid-menu:focus-within>.rbe-mermaid-menu-btn{background:#dbeafe;border-color:#93c5fd;color:#1d4ed8;}" +
      ".rbe-mermaid-popover{position:absolute;left:0;top:calc(100% + 6px);z-index:12;display:none;min-width:166px;border:1px solid #d7dee8;border-radius:8px;background:#fff;box-shadow:0 16px 34px rgba(15,23,42,.18);padding:6px;}" +
      ".rbe-mermaid-menu:focus-within>.rbe-mermaid-popover,.rbe-mermaid-menu:hover>.rbe-mermaid-popover{display:block;}" +
      ".rbe-mermaid-menu-row{position:relative;display:flex;align-items:center;gap:10px;min-height:34px;padding:7px 8px;border-radius:6px;font-size:14px;color:#344054;white-space:nowrap;}" +
      ".rbe-mermaid-menu-row:hover{background:#eef2f7;}" +
      ".rbe-mermaid-menu-row:after{content:'>';margin-left:auto;color:#667085;}" +
      ".rbe-mermaid-submenu{position:absolute;left:100%;top:-7px;display:none;min-width:270px;border:1px solid #d7dee8;border-radius:8px;background:#fff;box-shadow:0 16px 34px rgba(15,23,42,.18);padding:8px;}" +
      ".rbe-mermaid-menu-row:hover>.rbe-mermaid-submenu{display:block;}" +
      ".rbe-mermaid-popover:not(:has(.rbe-mermaid-menu-row:hover)) .rbe-mermaid-menu-row:first-child>.rbe-mermaid-submenu{display:block;}" +
      ".rbe-mermaid-menu-row-icon{width:18px;display:inline-grid;place-items:center;color:#344054;}" +
      ".rbe-mermaid-choice-grid{display:grid;grid-template-columns:repeat(10,22px);gap:7px;align-items:center;}" +
      ".rbe-mermaid-choice-grid:has(+ .rbe-mermaid-choice-grid){border-bottom:1px solid #e5e7eb;padding-bottom:8px;margin-bottom:8px;}" +
      ".rbe-mermaid-choice{width:22px;height:22px;border:0;background:transparent;color:#111827;display:grid;place-items:center;cursor:pointer;font:14px/1 Arial,sans-serif;border-radius:4px;}" +
      ".rbe-mermaid-choice:hover,.rbe-mermaid-choice.is-active{background:#dbeafe;color:#1d4ed8;}" +
      ".rbe-mermaid-line-menu .rbe-mermaid-popover{min-width:210px;}" +
      ".rbe-mermaid-line-menu .rbe-mermaid-menu-row:after{content:'';}" +
      ".rbe-mermaid-line-choice{width:100%;border:0;border-radius:6px;background:transparent;color:#344054;display:flex;align-items:center;gap:10px;padding:8px;text-align:left;cursor:pointer;font:14px/1.2 Arial,sans-serif;}" +
      ".rbe-mermaid-line-choice:hover{background:#eef2f7;color:#1d4ed8;}" +
      ".rbe-mermaid-menu-select,.rbe-mermaid-action-select,.rbe-mermaid-stylebar select,.rbe-mermaid-workspace input{border:1px solid #d7dee8;border-radius:8px;background:#fff;color:#172033;font:13px/1.2 Arial,sans-serif;padding:7px 9px;max-width:156px;}" +
      ".rbe-mermaid-workspace input{width:72px;}" +
      ".rbe-mermaid-stylebar,.rbe-mermaid-workspace{display:flex;flex-wrap:wrap;gap:7px;align-items:center;border:1px solid #d7dee8;border-radius:10px;background:#fbfdff;padding:6px;}" +
      ".rbe-mermaid-stylebar[hidden]{display:none!important;}" +
      ".rbe-mermaid-stylebar label,.rbe-mermaid-workspace label{display:inline-flex;gap:6px;align-items:center;font-size:12px;font-weight:800;color:#667085;}" +
      ".rbe-mermaid-stylebar label{height:32px;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:3px 6px;color:#344054;}" +
      ".rbe-mermaid-stylebar label:hover{border-color:#93c5fd;background:#eef6ff;color:#1d4ed8;}" +
      ".rbe-mermaid-stylebar .rbe-mermaid-icon-control,.rbe-mermaid-stylebar .rbe-mermaid-style-btn{position:relative;width:34px;height:32px;box-sizing:border-box;border:1px solid transparent;border-radius:8px;background:#fff;color:#344054;display:inline-grid;place-items:center;padding:0;cursor:pointer;}" +
      ".rbe-mermaid-stylebar .rbe-mermaid-icon-control:hover,.rbe-mermaid-stylebar .rbe-mermaid-style-btn:hover,.rbe-mermaid-stylebar .rbe-mermaid-style-btn.is-active{border-color:#93c5fd;background:#dbeafe;color:#1d4ed8;}" +
      ".rbe-mermaid-stylebar .rbe-mermaid-icon-control input,.rbe-mermaid-stylebar .rbe-mermaid-icon-control select{position:absolute!important;inset:0!important;width:100%!important;height:100%!important;opacity:0!important;cursor:pointer;}" +
      ".rbe-mermaid-control-swatch{position:absolute;left:7px;right:7px;bottom:3px;height:3px;border-radius:999px;background:#2563eb;pointer-events:none;}" +
      ".rbe-mermaid-fill-swatch{background:var(--rbe-mermaid-fill,#eef5ff);}" +
      ".rbe-mermaid-stroke-swatch{background:var(--rbe-mermaid-stroke,#111827);}" +
      ".rbe-mermaid-text-swatch{background:var(--rbe-mermaid-text,#172033);}" +
      ".rbe-mermaid-highlight-swatch{background:var(--rbe-mermaid-highlight,transparent);}" +
      ".rbe-mermaid-stylebar.is-line-selection .rbe-mermaid-fill-control,.rbe-mermaid-stylebar.is-line-selection .rbe-mermaid-text-control{display:none;}" +
      ".rbe-mermaid-stylebar:not(.is-text-capable) .rbe-mermaid-text-control{display:none;}" +
      ".rbe-mermaid-stylebar .rbe-mermaid-hidden-control{display:none!important;}" +
      ".rbe-mermaid-hint{font-size:12px;font-weight:700;color:#667085;}" +
      ".rbe-mermaid-stylebar input[type='color']{width:28px;height:24px;padding:1px;border:1px solid #d7dee8;border-radius:6px;background:#fff;}" +
      ".rbe-mermaid-stylebar input[type='range']{width:90px;}" +
      ".rbe-mermaid-stylebar select{height:24px;max-width:86px;padding:2px 5px;font-size:12px;}" +
      ".rbe-mermaid-stylebar .rbe-mermaid-icon-control.is-active{border-color:#93c5fd;background:#dbeafe;color:#1d4ed8;}" +
      ".rbe-mermaid-toolbar-sep{width:1px;height:24px;background:#cbd5e1;margin:0 2px;}" +
      ".rbe-mermaid-stage-shell{display:grid;grid-template-columns:28px minmax(0,1fr);grid-template-rows:26px minmax(0,1fr);min-height:0;height:100%;background:#fff;}" +
      ".rbe-mermaid-ruler-corner{grid-column:1;grid-row:1;border-right:1px solid #cbd5e1;border-bottom:1px solid #cbd5e1;background:#f8fafc;}" +
      ".rbe-mermaid-top-ruler{grid-column:2;grid-row:1;position:relative;border-bottom:1px solid #cbd5e1;background:#f8fafc;background-image:repeating-linear-gradient(90deg,#cbd5e1 0,#cbd5e1 1px,transparent 1px,transparent 10px),repeating-linear-gradient(90deg,#94a3b8 0,#94a3b8 1px,transparent 1px,transparent 100px);}" +
      ".rbe-mermaid-side-ruler{grid-column:1;grid-row:2;position:relative;border-right:1px solid #cbd5e1;background:#f8fafc;background-image:repeating-linear-gradient(0deg,#cbd5e1 0,#cbd5e1 1px,transparent 1px,transparent 10px),repeating-linear-gradient(0deg,#94a3b8 0,#94a3b8 1px,transparent 1px,transparent 100px);}" +
      ".rbe-mermaid-top-ruler span,.rbe-mermaid-side-ruler span{position:absolute;color:#475467;font:11px/1 Arial,sans-serif;user-select:none;}" +
      ".rbe-mermaid-top-ruler span{top:5px;transform:translateX(-50%);}" +
      ".rbe-mermaid-side-ruler span{left:8px;transform:translateY(-50%) rotate(-90deg);transform-origin:center;}" +
      ".rbe-mermaid-stage{position:relative;border:1px solid #d7dee8;border-radius:8px;background-color:#fff;background-image:linear-gradient(45deg,#f8fafc 25%,transparent 25%),linear-gradient(-45deg,#f8fafc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#f8fafc 75%),linear-gradient(-45deg,transparent 75%,#f8fafc 75%);background-position:0 0,0 12px,12px -12px,-12px 0;background-size:24px 24px;overflow:auto;resize:vertical;min-height:320px;max-height:560px;}" +
      ".rbe-mermaid-modal .rbe-mermaid-stage{grid-column:2;grid-row:2;height:100%;min-height:0;max-height:none;border:0;border-radius:0;resize:none;}" +
      ".rbe-mermaid-canvas{display:block;width:auto;height:auto;min-width:100%;touch-action:none;cursor:crosshair;background:rgba(255,255,255,.72);}" +
      ".rbe-mermaid-canvas [data-el-id]{cursor:move;}" +
      ".rbe-mermaid-canvas .is-selected{outline:none;filter:drop-shadow(0 0 0 #2563eb);}" +
      ".rbe-mermaid-selected-stroke{stroke:#2563eb!important;stroke-width:2!important;fill:none!important;pointer-events:none;}" +
      ".rbe-mermaid-handle{fill:#fff;stroke:#2563eb;stroke-width:2;cursor:nwse-resize;transition:fill .12s ease;}" +
      ".rbe-mermaid-handle:hover{fill:var(--rbe-handle-color,#2563eb);}" +
      ".rbe-mermaid-handle[data-handle='ne'],.rbe-mermaid-handle[data-handle='sw']{cursor:nesw-resize;}" +
      ".rbe-mermaid-handle[data-handle='n'],.rbe-mermaid-handle[data-handle='s']{cursor:ns-resize;}" +
      ".rbe-mermaid-handle[data-handle='e'],.rbe-mermaid-handle[data-handle='w']{cursor:ew-resize;}" +
      ".rbe-mermaid-rotate{cursor:grab;}" +
      ".rbe-mermaid-rotate circle{fill:#2563eb;stroke:#fff;stroke-width:2;}" +
      ".rbe-mermaid-rotate text{fill:#fff;stroke:none;font:800 12px/1 Arial,sans-serif;pointer-events:none;}" +
      ".rbe-mermaid-rotate-icon{pointer-events:none;}" +
      ".rbe-mermaid-marquee{fill:rgba(37,99,235,.09);stroke:#2563eb;stroke-width:1.5;stroke-dasharray:6 4;pointer-events:none;}" +
      ".rbe-mermaid-arrow-origin{fill:#fff;stroke:#2563eb;stroke-width:2;pointer-events:none;}" +
      ".rbe-mermaid-hidden-text{visibility:hidden!important;}" +
      ".rbe-mermaid-text-editor{position:absolute;z-index:20;box-sizing:border-box;border:0;border-radius:0;background:transparent;outline:none;overflow:hidden;resize:none;color:#172033;box-shadow:none;text-align:center;white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;caret-color:#0f172a;font-weight:400;}" +
      ".rbe-mermaid-text-editor::placeholder{color:#667085;opacity:.62;}" +
      ".rbe-mermaid-options{display:flex;flex-wrap:wrap;gap:8px;align-items:center;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:8px;}" +
      ".rbe-mermaid-options[hidden]{display:none;}" +
      ".rbe-mermaid-options input,.rbe-mermaid-options select{border:1px solid #d7dee8;border-radius:7px;padding:7px 8px;}" +
      ".rbe-mermaid-source{margin-top:0;}" +
      ".rbe-mermaid-source summary{cursor:pointer;font-weight:750;color:#475467;}" +
      ".rbe-output-mermaid-diagram{display:block;width:fit-content;max-width:100%;max-height:560px;margin:0 auto;overflow:auto;border:1px solid #d7dee8;border-radius:8px;background:#fff;}" +
      ".rbe-output-mermaid-diagram svg{display:block;width:100%;max-width:100%;height:auto;min-width:0;border:0;border-radius:0;background:#fff;}" +
      ".rbe-output-mermaid-error{display:inline-block;border:1px solid #fecaca;border-radius:8px;background:#fff1f2;color:#991b1b;padding:10px 12px;font:13px/1.45 Consolas,'Liberation Mono',monospace;white-space:pre-wrap;}" +
      ".rbe-audio-dropzone{min-height:170px;}" +
      ".rbe-audio-error{border:1px solid #fecaca;border-radius:8px;background:#fff1f2;color:#991b1b;padding:8px 10px;font-size:13px;font-weight:700;}" +
      ".rbe-audio-card{display:grid;gap:10px;justify-items:center;text-align:center;}" +
      ".rbe-audio-card audio,.rbe-output-audio audio{width:min(560px,100%);}" +
      ".rbe-output-audio{display:grid;justify-items:center;text-align:center;}" +
      ".rbe-output-audio figcaption{font-weight:760;margin-bottom:8px;}" +
      ".rbe-drawing-text-input{min-width:220px;flex:1 1 260px;}" +
      ".rbe-drawing-text-apply{min-width:38px;}" +
      ".rbe-drawing-shell{display:grid;gap:10px;}" +
      ".rbe-drawing-canvas{width:100%;height:260px;border:1px solid #cbd5e1;border-radius:8px;background:#fff;touch-action:none;}" +
      ".rbe-output-drawing img{max-width:100%;border:1px solid #d7dee8;border-radius:8px;background:#fff;}" +
      ".rbe-columns-block{overflow-x:auto;}" +
      ".rbe-columns-grid,.rbe-output-columns{display:grid;gap:12px;}" +
      ".rbe-column-field{display:grid;gap:8px;}" +
      ".rbe-column-header{min-height:38px;border:1px solid #d7dee8;border-radius:8px;padding:9px 10px;font-weight:760;background:#eef5ff!important;outline:none;}" +
      ".rbe-column-header:focus,.rbe-column-editor:focus{border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,.12);}" +
      ".rbe-column-header:empty:before{content:attr(data-placeholder);color:#98a2b3;font-weight:650;}" +
      ".rbe-column-editor{min-height:120px;border:1px solid #d7dee8;border-radius:8px;padding:10px;background:#fbfdff;outline:none;}" +
      ".rbe-column-editor:empty:before{content:attr(data-placeholder);color:#98a2b3;}" +
      ".rbe-output-columns{grid-template-columns:repeat(var(--rbe-columns,2),minmax(180px,1fr));overflow-x:auto;}" +
      ".rbe-output-column{border:1px solid #e2e8f0;border-radius:8px;padding:12px;background:#fff;}" +
      ".rbe-output-column-header{margin:-12px -12px 10px;padding:10px 12px;border-bottom:1px solid #e2e8f0;border-radius:8px 8px 0 0;background:#eef5ff;font-weight:760;text-align:left;}" +
      ".rbe-output-column-body{min-width:0;}" +
      ".rbe-image-stage.is-media-menu-open{min-width:176px;}" +
      ".rbe-image-transform-panel{position:absolute;left:50%;top:10px;z-index:4;transform:translateX(-50%);display:grid;grid-template-columns:repeat(3,30px);gap:5px;align-items:center;justify-content:center;max-width:calc(100% - 16px);opacity:0;pointer-events:none;transition:opacity .18s ease,transform .18s ease;}" +
      ".rbe-image-stage.is-media-menu-open>.rbe-image-transform-panel{opacity:1;pointer-events:auto;transform:translateX(-50%) translateY(0);}" +
      ".rbe-image-transform-panel .rbe-media-action{position:static;left:auto;top:auto;width:30px;height:30px;opacity:1;pointer-events:auto;transform:none;box-shadow:0 8px 18px rgba(15,23,42,.14);}" +
      ".rbe-image-transform.is-active{background:#2563eb;color:#fff;}" +
      "@media (max-width:900px){.rbe-plugin-toolbar input,.rbe-plugin-toolbar select,.rbe-plugin-card textarea{min-width:0;max-width:100%;}.rbe-chart-preview svg{min-height:220px;max-height:300px;}.rbe-output-chart svg{min-height:300px;}.rbe-mermaid-modal{padding:10px;}.rbe-mermaid-dialog{border-radius:10px;}.rbe-mermaid-modal .rbe-mermaid-toolbar{overflow-x:auto;align-content:flex-start;}.rbe-mermaid-popover{max-width:calc(100vw - 28px);max-height:62dvh;overflow:auto;}.rbe-mermaid-submenu{max-width:calc(100vw - 44px);max-height:58dvh;overflow:auto;}.rbe-output-mermaid-diagram{max-height:60dvh;}.rbe-columns-grid{grid-template-columns:1fr!important;}.rbe-output-columns{grid-template-columns:1fr!important;}}" +
      "@media (max-width:640px){.rbe-plugin-card{padding:10px;gap:10px;}.rbe-plugin-toolbar{display:grid;grid-template-columns:1fr;align-items:stretch;}.rbe-plugin-toolbar label{width:100%;justify-content:space-between;}.rbe-plugin-toolbar input,.rbe-plugin-toolbar select{width:100%;}.rbe-chart-type-bar{top:auto;bottom:10px;transform:translate(-50%,0) scale(.96);}.rbe-chart-preview-wrap:hover .rbe-chart-type-bar,.rbe-chart-type-bar:focus-within{transform:translate(-50%,0) scale(1);}.rbe-chart-slice-chip{width:100%;justify-content:space-between;}.rbe-latex-preview,.rbe-output-latex{font-size:18px;}.rbe-mermaid-modal{padding:0;}.rbe-mermaid-dialog{border-radius:0;}.rbe-mermaid-modal-head,.rbe-mermaid-modal-foot{padding:10px 12px;}.rbe-mermaid-modal-foot{flex-wrap:wrap;}.rbe-mermaid-modal-save,.rbe-mermaid-modal-cancel{flex:1 1 140px;}.rbe-mermaid-modal .rbe-mermaid-toolbar{gap:5px;padding:7px 8px;}.rbe-mermaid-tool,.rbe-mermaid-icon-btn,.rbe-mermaid-menu-btn{width:32px;height:32px;}.rbe-mermaid-stage-shell{grid-template-columns:22px minmax(0,1fr);grid-template-rows:22px minmax(0,1fr);}.rbe-mermaid-choice-grid{grid-template-columns:repeat(6,22px);}.rbe-mermaid-submenu{left:0;top:calc(100% + 6px);}.rbe-mermaid-stage{min-height:280px;}.rbe-mermaid-card-preview{min-height:140px;padding:8px;}.rbe-audio-card audio,.rbe-output-audio audio{width:100%;}.rbe-drawing-canvas{height:220px;}.rbe-image-transform-panel{grid-template-columns:repeat(3,28px);gap:4px;}.rbe-image-transform-panel .rbe-media-action{width:28px;height:28px;}.rbe-image-stage.is-media-menu-open{min-width:min(176px,100%);}}" +
      "@media (max-width:900px){.rbe-mermaid-card,.rbe-mermaid-card-preview{min-width:0;max-width:100%;}.rbe-mermaid-card-preview .rbe-output-mermaid-diagram{width:100%;max-width:100%;}.rbe-mermaid-modal{overflow:hidden;}.rbe-mermaid-dialog{width:100%;max-width:100%;height:calc(100dvh - 20px);max-height:calc(100dvh - 20px);}.rbe-mermaid-stage-shell{width:100%;min-width:0;overflow:hidden;}.rbe-mermaid-modal .rbe-mermaid-stage{width:100%;min-width:0;overflow:auto;}.rbe-mermaid-modal .rbe-mermaid-block{min-width:0;}.rbe-mermaid-card-preview .rbe-output-mermaid-diagram svg{max-width:100%;}}" +
      "@media (max-width:640px){.rbe-mermaid-card{padding:10px;min-height:180px;}.rbe-mermaid-card-preview{width:100%;max-width:100%;overflow:auto;}.rbe-mermaid-card-preview .rbe-output-mermaid-diagram{max-height:340px;overflow:hidden;}.rbe-mermaid-card-preview .rbe-output-mermaid-diagram svg{width:100%!important;min-width:0!important;max-width:100%;height:auto!important;}.rbe-mermaid-dialog{height:100dvh;max-height:100dvh;}.rbe-mermaid-modal-body{min-height:0;overflow:hidden;}.rbe-mermaid-modal .rbe-mermaid-toolbar{max-width:100%;overflow-x:auto;overflow-y:hidden;white-space:nowrap;}.rbe-mermaid-stage-shell{width:100%;min-width:0;overflow:hidden;}.rbe-mermaid-modal .rbe-mermaid-stage{width:100%;min-width:0;overflow:auto;}.rbe-mermaid-top-ruler{min-width:0;}.rbe-mermaid-canvas{min-width:100%;}.rbe-mermaid-toolbar-sep{display:none;}}" +
      ".rbe-mermaid-modal .rbe-mermaid-dialog{min-width:0;}" +
      ".rbe-mermaid-modal .rbe-mermaid-modal-body{display:grid;min-height:0;}" +
      ".rbe-mermaid-modal .rbe-mermaid-block{min-width:0;min-height:0;overflow:hidden;}" +
      ".rbe-mermaid-modal .rbe-mermaid-stage-shell{min-width:0;min-height:0;}" +
      ".rbe-mermaid-modal .rbe-mermaid-stage{overscroll-behavior:contain;-webkit-overflow-scrolling:touch;}" +
      "@media (max-width:900px){.rbe-mermaid-modal{height:100dvh;max-height:100dvh;padding:8px;overflow:hidden;}.rbe-mermaid-dialog{width:100%;height:100%;max-height:100%;}.rbe-mermaid-modal-body{overflow:hidden;}.rbe-mermaid-modal .rbe-mermaid-block{grid-template-rows:auto minmax(0,1fr);}.rbe-mermaid-modal .rbe-mermaid-toolbar{flex-wrap:nowrap;align-items:center;max-width:100%;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-width:thin;}.rbe-mermaid-modal .rbe-mermaid-toolbar::-webkit-scrollbar{height:6px;}.rbe-mermaid-modal .rbe-mermaid-toolbar::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:999px;}.rbe-mermaid-modal .rbe-mermaid-stylebar{flex:0 0 auto;flex-wrap:nowrap;min-width:max-content;}.rbe-mermaid-modal .rbe-mermaid-popover{position:fixed;left:12px!important;right:12px;top:98px;width:auto;min-width:0;max-width:none;max-height:calc(100dvh - 180px);overflow:auto;}.rbe-mermaid-modal .rbe-mermaid-stage-shell{height:100%;}.rbe-mermaid-modal .rbe-mermaid-stage{height:100%;min-height:0;max-height:none;}.rbe-mermaid-modal .rbe-mermaid-canvas{max-width:none;max-height:none;}}" +
      "@media (max-width:640px){.rbe-mermaid-modal{padding:0;}.rbe-mermaid-dialog{width:100vw;height:100dvh;max-height:100dvh;border-radius:0;}.rbe-mermaid-modal-head{min-height:46px;padding:7px 10px;}.rbe-mermaid-modal-foot{display:flex;justify-content:center;align-items:center;width:100%;max-width:100%;min-height:56px;padding:8px 10px;background:#fff;overflow:hidden;}.rbe-mermaid-modal-save,.rbe-mermaid-modal-cancel{flex:0 0 auto;width:auto;min-width:112px;min-height:40px;white-space:nowrap;font-size:13px;}.rbe-mermaid-modal .rbe-mermaid-toolbar{gap:5px;padding:7px 8px;}.rbe-mermaid-modal .rbe-mermaid-stage-shell{grid-template-columns:20px minmax(0,1fr);grid-template-rows:22px minmax(0,1fr);}.rbe-mermaid-modal .rbe-mermaid-top-ruler span,.rbe-mermaid-modal .rbe-mermaid-side-ruler span{font-size:10px;}.rbe-mermaid-modal .rbe-mermaid-side-ruler span{left:5px;}.rbe-mermaid-modal .rbe-mermaid-popover{left:8px!important;right:8px;top:86px;max-height:calc(100dvh - 154px);}.rbe-mermaid-modal .rbe-mermaid-submenu{max-width:calc(100vw - 28px);}}" +
      "@media (max-width:520px){.rbe-mermaid-modal .rbe-mermaid-popover{padding:6px;}.rbe-mermaid-modal .rbe-mermaid-menu-row{position:static;flex-wrap:wrap;white-space:normal;}.rbe-mermaid-modal .rbe-mermaid-menu-row:after{content:'';}.rbe-mermaid-modal .rbe-mermaid-menu-row>.rbe-mermaid-submenu{position:static;flex:1 1 100%;min-width:0;max-width:100%;margin-top:6px;border-radius:7px;box-shadow:none;}.rbe-mermaid-modal .rbe-mermaid-choice-grid{grid-template-columns:repeat(auto-fill,minmax(22px,1fr));}.rbe-mermaid-modal .rbe-mermaid-line-choice{font-size:13px;padding:7px;}}" +
      "@media (max-width:420px){.rbe-mermaid-modal-title{font-size:15px;}.rbe-mermaid-modal-head{padding:7px 9px;}.rbe-mermaid-choice-grid{grid-template-columns:repeat(auto-fill,minmax(22px,1fr));}.rbe-mermaid-popover{max-width:calc(100vw - 16px);}.rbe-mermaid-submenu{max-width:calc(100vw - 24px);}.rbe-mermaid-modal .rbe-mermaid-tool,.rbe-mermaid-modal .rbe-mermaid-icon-btn,.rbe-mermaid-modal .rbe-mermaid-menu-btn{width:31px;height:31px;}.rbe-mermaid-modal .rbe-mermaid-stage-shell{grid-template-columns:18px minmax(0,1fr);grid-template-rows:20px minmax(0,1fr);}.rbe-mermaid-modal-foot{gap:8px;}.rbe-mermaid-modal-save,.rbe-mermaid-modal-cancel{min-width:104px;padding:8px 10px;}.rbe-mermaid-modal-save{font-size:0;}.rbe-mermaid-modal-save:after{content:'Save';font-size:13px;}}";
    document.head.appendChild(style);
    bindOutputChartZoom();
  }

  function numberList(value) {
    return String(value || "")
      .split(/[\s,]+/)
      .map(function (item) { return Number(item); })
      .filter(function (item) { return Number.isFinite(item); });
  }

  function labelList(value) {
    return String(value || "")
      .split(",")
      .map(function (item) { return item.trim(); })
      .filter(Boolean);
  }

  var DEFAULT_CHART_COLORS = ["#2563eb", "#16a34a", "#f59e0b", "#dc2626", "#7c3aed", "#0891b2", "#db2777", "#65a30d"];

  function normalizeChartColor(value, fallback) {
    value = String(value || "").trim();
    return /^#[0-9a-f]{3}([0-9a-f]{3})?$/i.test(value) ? value : fallback;
  }

  function chartHexParts(color) {
    color = normalizeChartColor(color, "#2563eb").replace("#", "");
    if (color.length === 3) color = color.replace(/(.)/g, "$1$1");
    return [
      parseInt(color.slice(0, 2), 16),
      parseInt(color.slice(2, 4), 16),
      parseInt(color.slice(4, 6), 16)
    ];
  }

  function chartContrastText(color) {
    var rgb = chartHexParts(color);
    var luminance = (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255;
    return luminance < 0.28 ? "#ffffff" : "#111827";
  }

  function chartLabelText(value, maxChars) {
    value = String(value == null ? "" : value);
    maxChars = Math.max(4, maxChars || 12);
    return value.length > maxChars ? value.slice(0, Math.max(1, maxChars - 1)) + "..." : value;
  }

  function chartMetricDisplayName(value) {
    return String(value == null ? "" : value)
      .replace(/_/g, " ")
      .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
      .replace(/\bManufacturing\b/gi, "Mfg")
      .replace(/\bMedication\b/gi, "Med")
      .replace(/\bInsurance\b/gi, "Ins")
      .replace(/\bTreatment\b/gi, "Treat")
      .replace(/\bPercentage\b/gi, "%")
      .replace(/\bPercent\b/gi, "%")
      .replace(/\bPer\s+Unit\b/gi, "/Unit")
      .replace(/\s+/g, " ")
      .trim();
  }

  function chartSvgOpen(width, height, label) {
    return '<svg style="--rbe-chart-svg-width:' + width + 'px" viewBox="0 0 ' + width + " " + height + '" preserveAspectRatio="xMidYMid meet" role="img" aria-label="' + esc(label || "Chart") + '">';
  }

  function chartColorList(value, count) {
    var colors = String(value || "")
      .split(",")
      .map(function (item, index) { return normalizeChartColor(item, DEFAULT_CHART_COLORS[index % DEFAULT_CHART_COLORS.length]); })
      .filter(Boolean);
    while (colors.length < count) colors.push(DEFAULT_CHART_COLORS[colors.length % DEFAULT_CHART_COLORS.length]);
    return colors.slice(0, count);
  }

  function chartValueLabel(value) {
    if (!Number.isFinite(value)) return "0";
    return String(Math.round(value * 100) / 100);
  }

  function chartCompactValueLabel(value) {
    if (!Number.isFinite(value)) return "0";
    var abs = Math.abs(value);
    function trim(number) {
      return String(Math.round(number * 100) / 100).replace(/\.?0+$/, "");
    }
    if (abs >= 1000000000) return trim(value / 1000000000) + "B";
    if (abs >= 1000000) return trim(value / 1000000) + "M";
    if (abs >= 1000) return trim(value / 1000) + "K";
    return chartValueLabel(value);
  }

  function normalizeChartZoom(value) {
    value = Number(value);
    if (!Number.isFinite(value)) value = 1;
    return Math.max(0.6, Math.min(2.5, Math.round(value * 10) / 10));
  }

  function chartZoomControls(zoom) {
    zoom = normalizeChartZoom(zoom);
    return '<div class="rbe-chart-zoom-bar" data-rbe-chart-zoom-controls aria-label="Chart zoom controls">' +
      '<button type="button" class="rbe-chart-zoom-btn" data-chart-zoom-action="out" title="Zoom out" aria-label="Zoom out">-</button>' +
      '<span class="rbe-chart-zoom-value">' + Math.round(zoom * 100) + '%</span>' +
      '<button type="button" class="rbe-chart-zoom-btn" data-chart-zoom-action="in" title="Zoom in" aria-label="Zoom in">+</button>' +
      "</div>";
  }

  function chartOutputZoomControls(zoom) {
    zoom = normalizeChartZoom(zoom);
    return '<div class="rbe-chart-zoom-bar" data-rbe-chart-zoom-controls aria-label="Chart zoom controls">' +
      '<button type="button" class="rbe-chart-zoom-btn" data-chart-zoom-action="out" title="Zoom out" aria-label="Zoom out">-</button>' +
      '<span class="rbe-chart-zoom-value">' + Math.round(zoom * 100) + '%</span>' +
      '<button type="button" class="rbe-chart-zoom-btn" data-chart-zoom-action="in" title="Zoom in" aria-label="Zoom in">+</button>' +
      "</div>";
  }

  function applyOutputChartZoom(figure, zoom) {
    if (!figure) return;
    zoom = normalizeChartZoom(zoom);
    var svg = figure.querySelector("svg");
    var zoomValue = figure.querySelector(".rbe-chart-zoom-value");
    figure.dataset.chartZoom = zoom;
    if (zoomValue) zoomValue.textContent = Math.round(zoom * 100) + "%";
    if (svg && svg.viewBox && svg.viewBox.baseVal) {
      svg.style.setProperty("--rbe-chart-svg-width", Math.round(svg.viewBox.baseVal.width * zoom) + "px");
    }
  }

  function bindOutputChartZoom() {
    if (document.__rbeOutputChartZoomBound) return;
    document.__rbeOutputChartZoomBound = true;
    document.addEventListener("click", function (event) {
      var button = event.target.closest && event.target.closest(".rbe-output-chart .rbe-chart-zoom-btn");
      if (!button) return;
      var figure = button.closest(".rbe-output-chart");
      if (!figure) return;
      event.preventDefault();
      event.stopPropagation();
      var current = normalizeChartZoom(figure.dataset.chartZoom);
      applyOutputChartZoom(figure, current + (button.dataset.chartZoomAction === "in" ? 0.1 : -0.1));
    });
  }

  function chartTickValues(max) {
    var steps = 4;
    var ticks = [];
    for (var index = 0; index <= steps; index += 1) {
      ticks.push((max / steps) * index);
    }
    return ticks;
  }

  var CHART_TYPES = [
    { type: "bar", title: "Bar chart", icon: "chart-column" },
    { type: "line", title: "Line chart", icon: "chart-line" },
    { type: "pie", title: "Pie chart", icon: "chart-pie" },
    { type: "doughnut", title: "Doughnut chart", icon: "chart-doughnut" },
    { type: "area", title: "Area chart", icon: "chart-area" },
    { type: "scatter", title: "Scatter chart", icon: "chart-scatter" },
    { type: "stackedBar", title: "Stacked bar chart", icon: "chart-stacked" },
    { type: "comparison", title: "Comparison chart", icon: "chart-comparison" },
    { type: "trend", title: "Trend chart", icon: "trending-up" },
    { type: "kpi", title: "KPI cards", icon: "gauge-high" }
  ];

  function normalizeChartType(value) {
    value = String(value || "").trim();
    return CHART_TYPES.some(function (item) { return item.type === value; }) ? value : "bar";
  }

  function chartTypeButtons() {
    return CHART_TYPES.map(function (item) {
      return '<button type="button" class="rbe-chart-type-btn" data-chart-type="' + item.type + '" title="' + item.title + '">' + svgIcon(item.icon, item.title) + "</button>";
    }).join("");
  }

  function chartSeriesList(value, labels) {
    var groups = String(value || "")
      .split(";")
      .map(function (group) { return numberList(group); })
      .filter(function (group) { return group.length; });
    if (!groups.length) groups = [numberList(value || "12,19,7,14")];
    groups = groups.map(function (group) {
      var series = group.slice(0, labels.length);
      for (var index = 0; index < labels.length; index += 1) {
        if (!Number.isFinite(series[index])) series[index] = 0;
      }
      return series;
    });
    return groups;
  }

  function chartSeriesLabels(value, count) {
    var labels = labelList(value || "");
    for (var index = 0; index < count; index += 1) {
      if (!labels[index]) labels[index] = "Series " + (index + 1);
    }
    return labels.slice(0, count);
  }

  function chartSvg(data) {
    var type = normalizeChartType(data.type);
    var labels = labelList(data.labels || "Q1,Q2,Q3,Q4");
    var lineLike = type === "line" || type === "area" || type === "trend";
    var multiLineSeries = lineLike && String(data.values || "").indexOf(";") !== -1
      ? chartSeriesList(data.values, labels.length ? labels : ["A", "B", "C", "D"])
      : [];
    var values = multiLineSeries.length ? multiLineSeries[0].slice() : numberList(data.values || "12,19,7,14");
    var zoom = normalizeChartZoom(data.zoom);
    if (!values.length) values = [12, 19, 7, 14];
    if (labels.length && values.length > labels.length) values = values.slice(0, labels.length);
    var count = labels.length ? labels.length : Math.max(labels.length, values.length);
    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 color = data.color || "#2563eb";
    var maxValues = multiLineSeries.length ? multiLineSeries.reduce(function (all, group) { return all.concat(group); }, []) : values;
    var max = Math.max.apply(null, maxValues.map(function (value) { return Math.max(0, value); }).concat([1]));
    var width = type === "pie" || type === "doughnut" ? 720 : (type === "scatter" ? 920 : Math.max(640, Math.min(4800, count * 92 + 180)));
    var renderWidth = Math.round(width * zoom);
    var height = 340;
    if (type === "kpi") {
      var kpiColors = chartColorList(data.colors, values.length);
      var valueTexts = values.map(function (value) { return chartValueLabel(value); });
      var labelTexts = labels.map(function (label, index) { return label || "KPI " + (index + 1); });
      var longestValue = valueTexts.reduce(function (maxLength, value) { return Math.max(maxLength, String(value).length); }, 1);
      var longestLabel = labelTexts.reduce(function (maxLength, value) { return Math.max(maxLength, String(value).length); }, 1);
      var cardWidth = Math.max(172, Math.min(260, Math.max(longestValue * 17 + 42, longestLabel * 8 + 42)));
      var cardHeight = 110;
      var gap = 16;
      var cardCount = values.length;
      var kpiWidth = Math.max(640, Math.min(6400, cardCount * (cardWidth + gap) + 120));
      var kpiRenderWidth = Math.round(kpiWidth * zoom);
      var startX = (kpiWidth - (cardCount * cardWidth + Math.max(0, cardCount - 1) * gap)) / 2;
      var cards = values.map(function (value, index) {
        var x = startX + index * (cardWidth + gap);
        var valueText = valueTexts[index] || "";
        var labelText = labelTexts[index] || "";
        var valueFontSize = valueText.length > 12 ? 20 : (valueText.length > 9 ? 22 : 24);
        var valueMaxChars = Math.max(8, Math.floor((cardWidth - 32) / (valueFontSize * 0.56)));
        var labelMaxChars = Math.max(12, Math.floor((cardWidth - 32) / 7));
        return '<g><rect x="' + x + '" y="106" width="' + cardWidth + '" height="' + cardHeight + '" rx="8" fill="#fff" stroke="#d7dee8"/><path d="M' + (x + 16) + ' 132h34" stroke="' + esc(kpiColors[index % kpiColors.length]) + '" stroke-width="5" stroke-linecap="round"/><text x="' + (x + 16) + '" y="166" font-size="' + valueFontSize + '" fill="#172033" font-weight="800"><title>' + esc(valueText) + '</title>' + esc(chartLabelText(valueText, valueMaxChars)) + '</text><text x="' + (x + 16) + '" y="196" font-size="12" fill="#667085" font-weight="700"><title>' + esc(labelText) + '</title>' + esc(chartLabelText(labelText, labelMaxChars)) + "</text></g>";
      }).join("");
      return chartSvgOpen(kpiRenderWidth, height, data.title || "KPI cards").replace('viewBox="0 0 ' + kpiRenderWidth + " " + height + '"', 'viewBox="0 0 ' + kpiWidth + " " + height + '"') + cards + "</svg>";
    }
    if (type === "pie" || type === "doughnut") {
      var total = values.reduce(function (sum, item) { return sum + Math.max(0, item); }, 0) || 1;
      var start = -Math.PI / 2;
      var colors = chartColorList(data.colors, values.length);
      var centerX = 360;
      var centerY = 164;
      var radius = 116;
      var labelRadius = 154;
      var labelItems = [];
      var sliceItems = values.map(function (value, index) {
        var angle = (Math.max(0, value) / total) * Math.PI * 2;
        var end = start + angle;
        var mid = start + angle / 2;
        var x1 = centerX + Math.cos(start) * radius;
        var y1 = centerY + Math.sin(start) * radius;
        var x2 = centerX + Math.cos(end) * radius;
        var y2 = centerY + Math.sin(end) * radius;
        var large = angle > Math.PI ? 1 : 0;
        var path = "M" + centerX + " " + centerY + " L" + x1 + " " + y1 + " A" + radius + " " + radius + " 0 " + large + " 1 " + x2 + " " + y2 + " Z";
        start = end;
        var sliceColor = colors[index % colors.length];
        var labelText = chartValueLabel(value);
        if (value > 0 && angle > 0.01) {
          labelItems.push({
            mid: mid,
            value: value,
            label: labelText + " (" + chartValueLabel(value / total * 100) + "%)",
            title: labels[index] || "",
            color: sliceColor,
            side: Math.cos(mid) >= 0 ? "right" : "left",
            anchorX: centerX + Math.cos(mid) * (radius + 2),
            anchorY: centerY + Math.sin(mid) * (radius + 2),
            elbowX: centerX + Math.cos(mid) * labelRadius,
            elbowY: centerY + Math.sin(mid) * labelRadius
          });
        }
        return { path: path, color: sliceColor, title: (labels[index] || "") + ": " + value };
      });
      function distributePieLabels(side) {
        var sideItems = labelItems.filter(function (item) { return item.side === side; }).sort(function (a, b) { return a.elbowY - b.elbowY; });
        var minY = 28;
        var maxY = height - 28;
        var gap = 18;
        sideItems.forEach(function (item, index) {
          item.textY = Math.max(minY, index ? Math.max(item.elbowY, sideItems[index - 1].textY + gap) : item.elbowY);
        });
        for (var i = sideItems.length - 1; i >= 0; i -= 1) {
          var cap = i === sideItems.length - 1 ? maxY : sideItems[i + 1].textY - gap;
          sideItems[i].textY = Math.min(sideItems[i].textY, cap);
        }
        sideItems.forEach(function (item) {
          item.textX = side === "right" ? 548 : 172;
          item.elbowX = side === "right" ? Math.min(item.elbowX, item.textX - 18) : Math.max(item.elbowX, item.textX + 18);
        });
      }
      distributePieLabels("left");
      distributePieLabels("right");
      var slices = sliceItems.map(function (item) {
        return '<path d="' + item.path + '" fill="' + esc(item.color) + '"><title>' + esc(item.title) + '</title></path>';
      }).join("") + labelItems.map(function (item) {
        var anchor = item.side === "right" ? "start" : "end";
        var lineEndX = item.side === "right" ? item.textX - 8 : item.textX + 8;
        return '<polyline points="' + item.anchorX + "," + item.anchorY + " " + item.elbowX + "," + item.textY + " " + lineEndX + "," + item.textY + '" fill="none" stroke="' + esc(item.color) + '" stroke-width="1.5" opacity=".85"/><text x="' + item.textX + '" y="' + item.textY + '" text-anchor="' + anchor + '" dominant-baseline="central" fill="#172033" stroke="#ffffff" stroke-opacity=".72" stroke-width="2.5" paint-order="stroke fill" font-size="11" font-weight="850"><title>' + esc(item.title + ": " + item.value) + '</title>' + esc(item.label) + "</text>";
      }).join("");
      var hole = type === "doughnut" ? '<circle cx="360" cy="164" r="58" fill="#fff"/><text x="360" y="170" text-anchor="middle" font-size="18" fill="#172033" font-weight="800">' + esc(chartValueLabel(total)) + "</text>" : "";
      return chartSvgOpen(renderWidth, height, data.title || (type === "doughnut" ? "Doughnut chart" : "Pie chart")).replace('viewBox="0 0 ' + renderWidth + " " + height + '"', 'viewBox="0 0 ' + width + " " + height + '"') + slices + hole + "</svg>";
    }
    var padLeft = 58;
    var padRight = 28;
    var padTop = 36;
    var padBottom = 64;
    var plotWidth = width - padLeft - padRight;
    var plotHeight = height - padTop - padBottom;
    var axisBottom = height - padBottom;
    if (type === "scatter") {
      var xValues = numberList(data.labels || "");
      for (var scatterFill = 0; scatterFill < values.length; scatterFill += 1) {
        if (!Number.isFinite(xValues[scatterFill])) xValues[scatterFill] = scatterFill + 1;
      }
      xValues = xValues.slice(0, values.length);
      function scatterScale(list) {
        var clean = list.filter(function (value) { return Number.isFinite(value); }).sort(function (a, b) { return a - b; });
        if (!clean.length) return { min: 0, max: 1 };
        var rawMin = clean[0];
        var rawMax = clean[clean.length - 1];
        var p5 = clean[Math.floor((clean.length - 1) * 0.05)];
        var p95 = clean[Math.ceil((clean.length - 1) * 0.95)];
        var min = rawMin >= 0 ? Math.max(0, p5) : p5;
        var max = rawMax > p95 * 1.25 ? p95 : rawMax;
        if (max <= min) {
          min = rawMin;
          max = rawMax;
        }
        var spread = Math.max(1, max - min);
        var pad = spread * 0.08;
        return { min: rawMin >= 0 ? Math.max(0, min - pad) : min - pad, max: max + pad };
      }
      var xScale = scatterScale(xValues);
      var yScale = scatterScale(values);
      var xForScatter = function (value) { return padLeft + ((Math.max(xScale.min, Math.min(xScale.max, value)) - xScale.min) / Math.max(1, xScale.max - xScale.min)) * plotWidth; };
      var yForScatter = function (value) { return axisBottom - ((Math.max(yScale.min, Math.min(yScale.max, value)) - yScale.min) / Math.max(1, yScale.max - yScale.min)) * plotHeight; };
      var xAxisName = data.xAxisLabel || (String(data.caption || "").match(/\bX\s*=\s*([^.;]+)/i) || [])[1] || "X values";
      var yAxisName = data.yAxisLabel || (String(data.caption || "").match(/\bY\s*=\s*([^.;]+)/i) || [])[1] || "Y values";
      xAxisName = String(xAxisName).trim();
      yAxisName = String(yAxisName).trim();
      var scatterNote = "Each dot = one row/order";
      var scatterAxis =
        '<path d="M' + padLeft + " " + axisBottom + "H" + (width - padRight) + "M" + padLeft + " " + padTop + "V" + axisBottom + '" stroke="#cbd5e1" fill="none"/>' +
        '<text x="' + (padLeft + 4) + '" y="' + (padTop - 14) + '" font-size="12" fill="#344054" font-weight="800">' + esc(scatterNote) + '</text>' +
        chartTickValues(yScale.max - yScale.min).map(function (tick) {
          tick += yScale.min;
          var y = yForScatter(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">' + esc(chartValueLabel(tick)) + "</text></g>";
        }).join("") +
        chartTickValues(xScale.max - xScale.min).map(function (tick) {
          tick += xScale.min;
          var x = xForScatter(tick);
          return '<g><path d="M' + x + " " + axisBottom + "V" + (axisBottom + 5) + '" stroke="#cbd5e1"/><text x="' + x + '" y="' + (height - 32) + '" text-anchor="middle" font-size="11" fill="#475569">' + esc(chartValueLabel(tick)) + "</text></g>";
        }).join("") +
        '<text x="' + (padLeft + plotWidth / 2) + '" y="' + (height - 10) + '" text-anchor="middle" font-size="12" fill="#475569" font-weight="750">' + esc(xAxisName) + '</text>' +
        '<text x="16" y="' + (padTop + plotHeight / 2) + '" text-anchor="middle" font-size="12" fill="#475569" font-weight="750" transform="rotate(-90 16 ' + (padTop + plotHeight / 2) + ')">' + esc(yAxisName) + "</text>";
      var scatterPoints = values.map(function (value, index) {
        var xValue = xValues[index] || 0;
        var cx = xForScatter(xValue);
        var cy = yForScatter(value);
        return '<circle cx="' + cx + '" cy="' + cy + '" r="5.5" fill="' + esc(normalizeChartColor(color, "#2563eb")) + '" opacity=".78"><title>' + esc(xAxisName + ": " + chartValueLabel(xValue) + "; " + yAxisName + ": " + chartValueLabel(value)) + "</title></circle>";
      }).join("");
      return chartSvgOpen(renderWidth, height, data.title || "Scatter chart").replace('viewBox="0 0 ' + renderWidth + " " + height + '"', 'viewBox="0 0 ' + width + " " + height + '"') + scatterAxis + scatterPoints + "</svg>";
    }
    var step = plotWidth / values.length;
    var yFor = function (value) {
      return axisBottom - (Math.max(0, value) / max) * plotHeight;
    };
    var points = values.map(function (value, index) {
      var x = padLeft + step * index + step / 2;
      var y = yFor(value);
      return [x, y];
    });
    var labelEvery = type === "scatter" && labels.length > 24 ? Math.ceil(labels.length / 12) : 1;
    var axis =
      '<path d="M' + padLeft + " " + axisBottom + "H" + (width - padRight) + "M" + padLeft + " " + padTop + "V" + axisBottom + '" stroke="#cbd5e1" fill="none"/>' +
      chartTickValues(max).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">' + esc(chartValueLabel(tick)) + "</text></g>";
      }).join("") +
      labels.map(function (label, index) {
        if (labelEvery > 1 && index % labelEvery !== 0 && index !== labels.length - 1) return "";
        var x = padLeft + step * index + step / 2;
        var labelChars = Math.max(5, Math.floor(step / 7));
        return '<text x="' + x + '" y="' + (height - 30) + '" text-anchor="middle" font-size="11" fill="#475569"><title>' + esc(label) + '</title>' + esc(chartLabelText(label, labelChars)) + "</text>";
      }).join("");
    if (type === "line" || type === "area" || type === "trend") {
      var activeSeries = multiLineSeries.length ? multiLineSeries : [values];
      var lineColors = chartColorList(data.colors || data.color, activeSeries.length);
      var lineNames = chartSeriesLabels(data.seriesLabels || "", activeSeries.length);
      var showLegend = activeSeries.length > 1;
      var legend = showLegend ? lineNames.map(function (name, seriesIndex) {
        var lx = padLeft + seriesIndex * 132;
        var ly = 18;
        return '<g><rect x="' + lx + '" y="' + (ly - 9) + '" width="12" height="12" rx="3" fill="' + esc(lineColors[seriesIndex % lineColors.length]) + '"/><text x="' + (lx + 18) + '" y="' + (ly + 1) + '" font-size="12" fill="#344054" font-weight="750">' + esc(chartLabelText(name, 18)) + "</text></g>";
      }).join("") : "";
      var lineMarkup = activeSeries.map(function (seriesValues, seriesIndex) {
        var seriesColor = normalizeChartColor(lineColors[seriesIndex % lineColors.length], normalizeChartColor(color, "#2563eb"));
        var seriesPoints = seriesValues.map(function (value, index) {
          var x = padLeft + step * index + step / 2;
          var y = yFor(value);
          return [x, y];
        });
        var seriesPointString = seriesPoints.map(function (point) { return point.join(","); }).join(" ");
        var seriesArea = (type === "area" || type === "trend") && !showLegend ? '<polygon points="' + seriesPoints[0][0] + "," + axisBottom + " " + seriesPointString + " " + seriesPoints[seriesPoints.length - 1][0] + "," + axisBottom + '" fill="' + esc(seriesColor) + '" opacity=".16"/>' : "";
        var dotEvery = showLegend && labels.length > 16 ? Math.ceil(labels.length / 12) : 1;
        var circles = seriesPoints.map(function (point, index) {
          if (dotEvery > 1 && index % dotEvery !== 0 && index !== seriesPoints.length - 1) return "";
          var titleText = (showLegend ? lineNames[seriesIndex] + " - " : "") + (labels[index] || "") + ": " + chartValueLabel(seriesValues[index]);
          return '<circle cx="' + point[0] + '" cy="' + point[1] + '" r="' + (showLegend ? 4 : 5) + '" fill="' + esc(seriesColor) + '"><title>' + esc(titleText) + "</title></circle>";
        }).join("");
        var valueLabels = showLegend ? "" : seriesPoints.map(function (point, index) {
          var labelY = Math.max(padTop + 12, point[1] - 10);
          return '<text x="' + point[0] + '" y="' + labelY + '" text-anchor="middle" font-size="11" fill="#111827" font-weight="750">' + esc(chartLabelText(chartValueLabel(seriesValues[index]), Math.max(5, Math.floor(step / 8)))) + "</text>";
        }).join("");
        var endPoint = seriesPoints[seriesPoints.length - 1] || [width - padRight, padTop];
        var endLabel = showLegend ? '<text x="' + Math.min(width - padRight - 4, endPoint[0] + 8) + '" y="' + Math.max(padTop + 12, endPoint[1] - 8) + '" font-size="11" fill="' + esc(seriesColor) + '" font-weight="800">' + esc(chartLabelText(lineNames[seriesIndex], 14)) + "</text>" : "";
        return seriesArea + '<polyline points="' + seriesPointString + '" fill="none" stroke="' + esc(seriesColor) + '" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>' + circles + valueLabels + endLabel;
      }).join("");
      var trendBadge = type === "trend" && !showLegend ? '<text x="' + (width - padRight - 4) + '" y="' + (padTop + 4) + '" text-anchor="end" font-size="13" fill="' + (values[values.length - 1] >= values[0] ? "#07845f" : "#cf2f2f") + '" font-weight="800">' + esc((values[values.length - 1] >= values[0] ? "Up " : "Down ") + chartValueLabel(values[values.length - 1] - values[0])) + "</text>" : "";
      return chartSvgOpen(renderWidth, height, data.title || (type === "area" ? "Area chart" : type === "trend" ? "Trend chart" : "Line chart")).replace('viewBox="0 0 ' + renderWidth + " " + height + '"', 'viewBox="0 0 ' + width + " " + height + '"') + legend + axis + lineMarkup + trendBadge + "</svg>";
    }
    if (type === "stackedBar" || type === "comparison") {
      var series = chartSeriesList(data.values, labels);
      var singleSeries = series.length <= 1;
      var seriesColors = chartColorList(data.colors || data.color, singleSeries ? labels.length : series.length);
      var namedSeries = chartSeriesLabels(data.seriesLabels || "", series.length);
      var stackedMax = type === "stackedBar"
        ? Math.max.apply(null, labels.map(function (_, index) { return series.reduce(function (sum, group) { return sum + Math.max(0, group[index] || 0); }, 0); }).concat([1]))
        : Math.max.apply(null, series.reduce(function (all, group) { return all.concat(group); }, []).map(function (value) { return Math.max(0, value); }).concat([1]));
      yFor = function (value) { return axisBottom - (Math.max(0, value) / stackedMax) * plotHeight; };
      labelEvery = labels.length > 24 ? Math.ceil(labels.length / 12) : 1;
      var legend = singleSeries ? "" : namedSeries.map(function (name, seriesIndex) {
        var legendSlot = Math.max(176, Math.min(320, plotWidth / Math.max(1, namedSeries.length)));
        var lx = padLeft + seriesIndex * legendSlot;
        var ly = 18;
        var displayName = chartMetricDisplayName(name);
        var legendChars = Math.max(14, Math.floor((legendSlot - 26) / 6.6));
        return '<g><rect x="' + lx + '" y="' + (ly - 9) + '" width="12" height="12" rx="3" fill="' + esc(seriesColors[seriesIndex % seriesColors.length]) + '"/><text x="' + (lx + 18) + '" y="' + (ly + 1) + '" font-size="12" fill="#344054" font-weight="750"><title>' + esc(name) + '</title>' + esc(chartLabelText(displayName, legendChars)) + "</text></g>";
      }).join("");
      axis = legend + '<path d="M' + padLeft + " " + axisBottom + "H" + (width - padRight) + "M" + padLeft + " " + padTop + "V" + axisBottom + '" stroke="#cbd5e1" fill="none"/>' + chartTickValues(stackedMax).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">' + esc(chartValueLabel(tick)) + "</text></g>"; }).join("") + labels.map(function (label, index) { if (labelEvery > 1 && index % labelEvery !== 0 && index !== labels.length - 1) return ""; var x = padLeft + step * index + step / 2; return '<text x="' + x + '" y="' + (height - 30) + '" text-anchor="middle" font-size="11" fill="#475569"><title>' + esc(label) + '</title>' + esc(chartLabelText(label, Math.max(5, Math.floor(step / 7)))) + "</text>"; }).join("");
      var bars = labels.map(function (label, labelIndex) {
        var comparisonGap = type === "comparison" ? Math.max(14, Math.min(24, step * 0.16)) : 0;
        var slotWidth = type === "comparison" ? step * 0.82 : step * 0.64;
        var groupWidth = type === "comparison" ? Math.max(24, slotWidth) : slotWidth;
        var xBase = padLeft + step * labelIndex + (step - slotWidth) / 2;
        var running = 0;
        return series.map(function (group, seriesIndex) {
          var value = Math.max(0, group[labelIndex] || 0);
          var barHeight = (value / stackedMax) * plotHeight;
          var comparisonBarWidth = type === "comparison" ? Math.max(7, (groupWidth - comparisonGap * Math.max(0, series.length - 1)) / Math.max(1, series.length)) : slotWidth;
          var x = type === "comparison" ? xBase + (slotWidth - (comparisonBarWidth * series.length + comparisonGap * Math.max(0, series.length - 1))) / 2 + (comparisonBarWidth + comparisonGap) * seriesIndex : xBase;
          var widthValue = type === "comparison" ? comparisonBarWidth : slotWidth;
          var y = type === "comparison" ? axisBottom - barHeight : axisBottom - ((running + value) / stackedMax) * plotHeight;
          running += value;
          var segmentColor = singleSeries ? seriesColors[labelIndex % seriesColors.length] : seriesColors[seriesIndex % seriesColors.length];
          var fullValueLabel = chartValueLabel(value);
          var compactValueLabel = type === "comparison" || fullValueLabel.length > Math.max(5, Math.floor(widthValue / 7)) ? chartCompactValueLabel(value) : fullValueLabel;
          var labelNaturalWidth = compactValueLabel.length * (type === "comparison" ? 6.4 : 7.2);
          var tinySegment = barHeight < 16 || value / stackedMax < 0.055;
          var labelMaxWidth = type === "comparison" ? Math.max(28, Math.min(70, labelNaturalWidth)) : Math.max(18, Math.min(Math.max(18, widthValue - 5), labelNaturalWidth));
          var labelFontSize = tinySegment ? 9 : (type === "comparison" ? (compactValueLabel.length > 7 ? 10 : 10.5) : (compactValueLabel.length > 8 ? 9 : (compactValueLabel.length > 6 ? 10 : 11)));
          var comparisonInside = type === "comparison" && barHeight > 28;
          var stackedInside = type === "stackedBar" && barHeight > 18 && widthValue > 24;
          var canLabel = type === "comparison" ? widthValue > 10 && barHeight > 1 : stackedInside;
          var labelY = comparisonInside || stackedInside ? y + barHeight / 2 + 4 : Math.max(padTop + 10, y - (tinySegment ? 4 : 7));
          var labelColor = comparisonInside || stackedInside ? chartContrastText(segmentColor) : "#111827";
          var segmentTextLength = labelNaturalWidth > labelMaxWidth ? ' textLength="' + labelMaxWidth + '" lengthAdjust="spacingAndGlyphs"' : "";
          var segmentLabel = canLabel ? '<text x="' + (x + widthValue / 2) + '" y="' + labelY + '" text-anchor="middle" font-size="' + labelFontSize + '" fill="' + esc(labelColor) + '" stroke="' + (labelColor === "#ffffff" ? "#0f172a" : "#ffffff") + '" stroke-opacity=".22" stroke-width="2" paint-order="stroke fill" font-weight="' + (tinySegment ? "750" : "800") + '"' + segmentTextLength + '><title>' + esc(fullValueLabel) + '</title>' + esc(compactValueLabel) + "</text>" : "";
          var titleText = singleSeries ? label + ": " + fullValueLabel : label + " - " + namedSeries[seriesIndex] + ": " + fullValueLabel;
          return '<g><rect x="' + x + '" y="' + y + '" width="' + widthValue + '" height="' + barHeight + '" rx="4" fill="' + esc(segmentColor) + '"><title>' + esc(titleText) + "</title></rect>" + segmentLabel + "</g>";
        }).join("");
      }).join("");
      return chartSvgOpen(renderWidth, height, data.title || (type === "stackedBar" ? "Stacked bar chart" : "Comparison chart")).replace('viewBox="0 0 ' + renderWidth + " " + height + '"', 'viewBox="0 0 ' + width + " " + height + '"') + axis + bars + "</svg>";
    }
    return chartSvgOpen(renderWidth, height, data.title || "Bar chart").replace('viewBox="0 0 ' + renderWidth + " " + height + '"', 'viewBox="0 0 ' + width + " " + height + '"') + axis + 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 = step * 0.64;
      var barColors = chartColorList(data.colors || color, values.length);
      var barColor = normalizeChartColor(barColors[index], normalizeChartColor(color, "#2563eb"));
      var inside = barHeight > 26 && barWidth > 34;
      var tinyBar = barHeight < 16 || value / max < 0.055;
      var labelY = inside ? y + barHeight / 2 + 5 : Math.max(padTop + 10, y - (tinyBar ? 4 : 7));
      var labelColor = inside ? chartContrastText(barColor) : "#111827";
      var fullValueLabel = chartValueLabel(value);
      var compactValueLabel = fullValueLabel.length > Math.max(5, Math.floor(barWidth / 7)) ? chartCompactValueLabel(value) : fullValueLabel;
      var barLabelNaturalWidth = compactValueLabel.length * 7.2;
      var labelMaxWidth = Math.max(18, Math.min(Math.max(18, barWidth - 6), barLabelNaturalWidth));
      var labelFontSize = tinyBar ? 9 : (compactValueLabel.length > 8 ? 10 : (compactValueLabel.length > 6 ? 11 : 12));
      var barTextLength = barLabelNaturalWidth > labelMaxWidth ? ' textLength="' + labelMaxWidth + '" lengthAdjust="spacingAndGlyphs"' : "";
      return '<g><rect x="' + x + '" y="' + y + '" width="' + barWidth + '" height="' + barHeight + '" rx="5" fill="' + esc(barColor) + '"><title>' + esc((labels[index] || "") + ": " + fullValueLabel) + '</title></rect><text x="' + (x + barWidth / 2) + '" y="' + labelY + '" text-anchor="middle" font-size="' + labelFontSize + '" fill="' + esc(labelColor) + '" stroke="' + (labelColor === "#ffffff" ? "#0f172a" : "#ffffff") + '" stroke-opacity=".22" stroke-width="2" paint-order="stroke fill" font-weight="' + (tinyBar ? "750" : "800") + '"' + barTextLength + '><title>' + esc(fullValueLabel) + '</title>' + esc(compactValueLabel) + "</text></g>";
    }).join("") + "</svg>";
  }

  function loadScriptOnce(src, key, callback) {
    var existing = document.querySelector("script[data-blockwriteai-lib='" + key + "']");
    if (existing) {
      if (existing.dataset.loaded === "true") callback();
      else existing.addEventListener("load", callback, { once: true });
      return;
    }
    var script = document.createElement("script");
    script.src = src;
    script.dataset.blockwriteaiLib = key;
    script.onload = function () {
      script.dataset.loaded = "true";
      callback();
    };
    script.onerror = callback;
    document.head.appendChild(script);
  }

  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-advanced-blocks(?:\.min)?\.js(?:\?|#|$)/.test(src)) {
            scriptSrc = src;
            break;
          }
        }
      }
    }
    var prefix = scriptSrc.indexOf("/blockwriteai_editor/static/blockwriteai/plugins/") !== -1 ? "../../../../" : "../../";
    try {
      return scriptSrc ? new URL(prefix + path, scriptSrc).href : path;
    } catch (error) {
      return prefix + path;
    }
  }

  function loadStyleOnce(href, key) {
    if (document.querySelector("link[data-blockwriteai-lib='" + key + "']")) return;
    var link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = href;
    link.dataset.blockwriteaiLib = key;
    document.head.appendChild(link);
  }

  function renderMermaidPreview(target, code) {
    target.innerHTML = '<pre>' + esc(code || "") + "</pre>";
    if (!code || !code.trim()) return;
    loadScriptOnce(localVendorAsset("vendor/mermaid/mermaid.min.js"), "mermaid", function () {
      if (!global.mermaid) return;
      try {
        global.mermaid.initialize({ startOnLoad: false, securityLevel: "strict" });
        var render = function () {
          global.mermaid.render("rbe-mermaid-" + Math.random().toString(36).slice(2), code).then(function (result) {
            target.innerHTML = result.svg;
          }).catch(function () {
            target.innerHTML = '<pre class="rbe-output-mermaid-error">' + esc(code || "") + "</pre>";
          });
        };
        if (global.mermaid.parse) {
          Promise.resolve(global.mermaid.parse(code)).then(render).catch(function () {
            target.innerHTML = '<pre class="rbe-output-mermaid-error">' + esc(code || "") + "</pre>";
          });
        } else {
          render();
        }
      } catch (error) {}
    });
  }

  var latexRenderCache = {};

  function decodeHTML(value) {
    if (value == null) return "";
    value = String(value);
    if (value.indexOf("&") === -1) return value;
    if (typeof document === "undefined") return value;
    var textarea = document.createElement("textarea");
    textarea.innerHTML = value;
    return textarea.value;
  }

  function normalizeLatexSource(code, displayMode) {
    var source = decodeHTML(code || "").replace(/\r\n?/g, "\n");
    var trimmed = source.trim();
    var detectedDisplay = displayMode !== false;
    var match;
    if ((match = trimmed.match(/^\$\$([\s\S]*)\$\$$/))) {
      return { source: source, expression: match[1].trim(), display: true };
    }
    if ((match = trimmed.match(/^\\\[([\s\S]*)\\\]$/))) {
      return { source: source, expression: match[1].trim(), display: true };
    }
    if ((match = trimmed.match(/^\\\(([\s\S]*)\\\)$/))) {
      return { source: source, expression: match[1].trim(), display: false };
    }
    if ((match = trimmed.match(/^\$([\s\S]*)\$$/)) && trimmed.slice(0, 2) !== "$$" && trimmed.slice(-2) !== "$$") {
      return { source: source, expression: match[1].trim(), display: false };
    }
    return { source: source, expression: trimmed, display: detectedDisplay };
  }

  function latexFallbackHTML(code) {
    var normalized = normalizeLatexSource(code, true);
    var html = esc(normalized.expression || "");
    html = html
      .replace(/\\int/g, "&int;")
      .replace(/\\sum/g, "&sum;")
      .replace(/\\sqrt\{([^}]*)\}/g, "&radic;<span style=\"text-decoration:overline\">$1</span>")
      .replace(/\\frac\{([^}]*)\}\{([^}]*)\}/g, '<span class="rbe-latex-frac"><span>$1</span><span>$2</span></span>')
      .replace(/\^\{([^}]*)\}/g, "<sup>$1</sup>")
      .replace(/\^([A-Za-z0-9+\-=]+)/g, "<sup>$1</sup>")
      .replace(/_\{([^}]*)\}/g, "<sub>$1</sub>")
      .replace(/_([A-Za-z0-9+\-=]+)/g, "<sub>$1</sub>")
      .replace(/\\,/g, " ")
      .replace(/\\ /g, " ");
    return html;
  }

  function renderLatexPreview(target, code, displayMode) {
    if (!target) return;
    var normalized = normalizeLatexSource(code || "", displayMode);
    var expression = normalized.expression;
    var display = normalized.display;
    target.classList.remove("rbe-latex-invalid");
    if (!expression) {
      target.innerHTML = "";
      return;
    }
    target.innerHTML = '<span class="rbe-latex-loading">Rendering LaTeX...</span>';
    loadStyleOnce(localVendorAsset("vendor/katex/css/katex.min.css?v=katex-font-paths-v2"), "katex-css");
    loadScriptOnce(localVendorAsset("vendor/katex/js/katex.min.js"), "katex", function () {
      if (!global.katex) {
        target.innerHTML = latexFallbackHTML(expression);
        return;
      }
      try {
        var key = (display ? "1" : "0") + "\n" + expression;
        if (!latexRenderCache[key]) {
          global.katex.renderToString(expression, { throwOnError: true, displayMode: display, trust: false });
          latexRenderCache[key] = global.katex.renderToString(expression, { throwOnError: false, displayMode: display, trust: false });
        }
        target.innerHTML = latexRenderCache[key];
      } catch (error) {
        target.classList.add("rbe-latex-invalid");
        target.innerHTML = '<div class="rbe-latex-error"><strong>Invalid LaTeX syntax</strong><small>Check delimiters, braces, and command arguments.</small></div>';
      }
    });
  }

  var DIAGRAM_LINE_TYPES = /^(line|arrow|leftArrow|rightArrow|upArrow|downArrow|biArrow|elbow|curved|curve|polyline|scribble)$/;
  var DIAGRAM_SHAPE_TYPES = /^(rect|roundedRect|circle|oval|triangle|diamond|pentagon|hexagon|parallelogram|trapezoid|octagon|heart|cloud|cylinder|cube|callout|roundCallout|thought|plus|minus|multiply|divide|equal|brackets|text|wordart|image)$/;

  function defaultMermaidWorkspace() {
    return { width: 1100, height: 620, zoom: 1 };
  }

  var MERMAID_WORKSPACE_MAX = 6000;
  var MERMAID_WORKSPACE_GROW_PADDING = 260;
  var MERMAID_WORKSPACE_GROW_STEP = 240;

  function defaultMermaidDiagram() {
    return [
      { id: "shape-start", type: "roundedRect", x: 120, y: 140, w: 170, h: 76, text: "Start", fill: "#eef5ff", stroke: "#111827", fontSize: 16, strokeWidth: 1 },
      { id: "shape-blockwriteai", type: "roundedRect", x: 560, y: 140, w: 190, h: 76, text: "BlockWriteAI", fill: "#ecfdf3", stroke: "#111827", fontSize: 16, strokeWidth: 1 },
      { id: "line-main", type: "arrow", x1: 290, y1: 178, x2: 560, y2: 178, stroke: "#475467", strokeWidth: 1 }
    ];
  }

  function simpleMermaidToDiagram(code) {
    code = String(code || "");
    if (/^\s*sequenceDiagram\b/i.test(code)) return sequenceMermaidToDiagram(code);
    if (!/^\s*(flowchart|graph)\b/im.test(code)) return null;
    var nodes = {};
    var order = [];
    var edges = [];
    var direction = /^\s*(?:flowchart|graph)\s+LR\b/im.test(code) ? "LR" : "TD";

    function cleanLabel(value) {
      return String(value || "").replace(/^["']|["']$/g, "").replace(/<br\s*\/?>/gi, "\n").replace(/\s+/g, " ").trim();
    }
    function ensureNode(id, label, kind) {
      id = String(id || "").trim();
      if (!id) return;
      if (!nodes[id]) {
        nodes[id] = { id: id, label: cleanLabel(label) || id, kind: kind || "process" };
        order.push(id);
      } else {
        if (label && (!nodes[id].label || nodes[id].label === id)) nodes[id].label = cleanLabel(label);
        if (kind && nodes[id].kind === "process") nodes[id].kind = kind;
      }
    }
    function parseNode(raw) {
      raw = String(raw || "").trim();
      var idMatch = raw.match(/^([A-Za-z][A-Za-z0-9_-]*)/);
      if (!idMatch) return null;
      var id = idMatch[1];
      var rest = raw.slice(id.length).trim();
      var label = id;
      var kind = "process";
      var match;
      if ((match = rest.match(/^\(\[([\s\S]*?)\]\)/))) { label = match[1]; kind = "start"; }
      else if ((match = rest.match(/^\(\(([\s\S]*?)\)\)/))) { label = match[1]; kind = "end"; }
      else if ((match = rest.match(/^\{([\s\S]*?)\}/))) { label = match[1]; kind = "decision"; }
      else if ((match = rest.match(/^\[\[([\s\S]*?)\]\]/))) { label = match[1]; kind = "service"; }
      else if ((match = rest.match(/^\[\(([\s\S]*?)\)\]/))) { label = match[1]; kind = "database"; }
      else if ((match = rest.match(/^\[\/([\s\S]*?)\/\]/))) { label = match[1]; kind = "input"; }
      else if ((match = rest.match(/^\[([\s\S]*?)\]/))) { label = match[1]; kind = "process"; }
      return { id: id, label: cleanLabel(label), kind: kind };
    }
    function parseEdge(line) {
      var normalized = String(line || "").replace(/\s+/g, " ").trim();
      var match = normalized.match(/^([A-Za-z][A-Za-z0-9_-]*)\s*(?:--[^>]*-->|-->|---|-\.-|==>)\s*(?:\|[^|]*\|\s*)?([A-Za-z][A-Za-z0-9_-]*)/);
      if (!match) match = normalized.match(/^([A-Za-z][A-Za-z0-9_-]*)\s*-->\|[^|]*\|\s*([A-Za-z][A-Za-z0-9_-]*)/);
      return match ? { from: match[1], to: match[2] } : null;
    }
    code.split(/\r?\n/).forEach(function (line) {
      line = line.replace(/%%.*$/, "").trim();
      if (!line || /^(flowchart|graph|subgraph|end|classDef|class|style|linkStyle)\b/i.test(line)) return;
      var edge = parseEdge(line);
      if (edge) {
        edges.push(edge);
        ensureNode(edge.from);
        ensureNode(edge.to);
        line.split(/(?:--[^>]*-->|-->|---|-\.-|==>)/).forEach(function (part) {
          var node = parseNode(part.replace(/^\|[^|]*\|/, ""));
          if (node) ensureNode(node.id, node.label, node.kind);
        });
        return;
      }
      var node = parseNode(line);
      if (node) ensureNode(node.id, node.label, node.kind);
    });
    if (!order.length) return null;
    var elements = [];
    var placed = {};
    var cols = direction === "LR" ? Math.min(order.length, 4) : Math.min(order.length, 3);
    order.forEach(function (id, index) {
      var node = nodes[id];
      var col = direction === "LR" ? Math.floor(index / 3) : index % cols;
      var row = direction === "LR" ? index % 3 : Math.floor(index / cols);
      var type = node.kind === "decision" ? "diamond" : node.kind === "database" ? "cylinder" : node.kind === "end" ? "oval" : "roundedRect";
      var fill = node.kind === "decision" ? "#fff7ed" : node.kind === "database" ? "#f0f9ff" : node.kind === "service" ? "#eef2ff" : node.kind === "end" ? "#ecfdf3" : "#eef5ff";
      var x = 100 + col * 250;
      var y = 90 + row * 132;
      var w = node.kind === "decision" ? 180 : 190;
      var h = node.kind === "decision" ? 96 : 76;
      placed[id] = { x: x, y: y, w: w, h: h };
      elements.push({ id: "shape-" + id, type: type, x: x, y: y, w: w, h: h, text: node.label, fill: fill, stroke: "#111827", fontSize: 15, strokeWidth: 1 });
    });
    edges.forEach(function (edge, index) {
      var from = placed[edge.from];
      var to = placed[edge.to];
      if (!from || !to) return;
      var horizontal = Math.abs((to.x + to.w / 2) - (from.x + from.w / 2)) >= Math.abs((to.y + to.h / 2) - (from.y + from.h / 2));
      elements.push({ id: "line-" + edge.from + "-" + edge.to + "-" + index, type: "arrow", x1: horizontal && to.x >= from.x ? from.x + from.w : from.x + from.w / 2, y1: horizontal && to.x >= from.x ? from.y + from.h / 2 : from.y + from.h, x2: horizontal && to.x >= from.x ? to.x : to.x + to.w / 2, y2: horizontal && to.x >= from.x ? to.y + to.h / 2 : to.y, stroke: "#475467", strokeWidth: 1 });
    });
    return elements.length ? elements : null;
  }

  function sequenceMermaidToDiagram(code) {
    var participants = [];
    var messages = [];
    function addParticipant(id, label) {
      id = String(id || "").trim();
      if (!id) return;
      if (!participants.some(function (item) { return item.id === id; })) participants.push({ id: id, label: label || id });
    }
    String(code || "").split(/\r?\n/).forEach(function (line) {
      line = line.trim();
      var declaration = line.match(/^(?:actor|participant)\s+([A-Za-z][A-Za-z0-9_-]*)(?:\s+as\s+(.+))?/i);
      if (declaration) {
        addParticipant(declaration[1], declaration[2] || declaration[1]);
        return;
      }
      var message = line.match(/^([A-Za-z][A-Za-z0-9_-]*)\s*-{1,2}>>\s*([A-Za-z][A-Za-z0-9_-]*)\s*:?\s*(.*)$/);
      if (message) {
        addParticipant(message[1]);
        addParticipant(message[2]);
        messages.push({ from: message[1], to: message[2], text: message[3] || "" });
      }
    });
    if (!participants.length) return null;
    var elements = [];
    var xById = {};
    participants.forEach(function (participant, index) {
      var x = 100 + index * 220;
      xById[participant.id] = x + 85;
      elements.push({ id: "shape-" + participant.id, type: "roundedRect", x: x, y: 70, w: 170, h: 64, text: participant.label, fill: "#eef5ff", stroke: "#111827", fontSize: 15, strokeWidth: 1 });
      elements.push({ id: "life-" + participant.id, type: "line", x1: x + 85, y1: 145, x2: x + 85, y2: 145 + Math.max(260, messages.length * 58), stroke: "#cbd5e1", strokeWidth: 1, dash: "dashed" });
    });
    messages.slice(0, 14).forEach(function (message, index) {
      var y = 180 + index * 58;
      if (xById[message.from] == null || xById[message.to] == null) return;
      elements.push({ id: "msg-" + index, type: "arrow", x1: xById[message.from], y1: y, x2: xById[message.to], y2: y, stroke: "#475467", strokeWidth: 1 });
      if (message.text) elements.push({ id: "msg-text-" + index, type: "text", x: Math.min(xById[message.from], xById[message.to]) + 16, y: y - 28, w: Math.abs(xById[message.to] - xById[message.from]) - 32 || 170, h: 26, text: message.text, fill: "transparent", stroke: "#111827", fontSize: 12, strokeWidth: 1 });
    });
    return elements;
  }

  function isLineType(type) {
    return DIAGRAM_LINE_TYPES.test(type || "");
  }

  function isTextDiagramType(type) {
    return type === "text" || type === "wordart";
  }

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

  function normalizeDiagramWorkspace(data) {
    var workspace = data && data.diagram && data.diagram.workspace ? data.diagram.workspace : data && data.workspace ? data.workspace : {};
    return {
      width: clampNumber(workspace.width, 640, MERMAID_WORKSPACE_MAX, 1100),
      height: clampNumber(workspace.height, 360, MERMAID_WORKSPACE_MAX, 620),
      zoom: clampNumber(workspace.zoom, 0.35, 2.5, 1)
    };
  }

  function normalizeDiagramDash(value) {
    return value === "dotted" || value === "dashed" || value === "longDash" || value === "dashDot" ? value : "solid";
  }

  function dashArray(value) {
    value = normalizeDiagramDash(value);
    if (value === "dotted") return "2 8";
    if (value === "dashed") return "10 8";
    if (value === "longDash") return "18 10";
    if (value === "dashDot") return "14 7 2 7";
    return "";
  }

  function normalizeDiagramLineHeight(value) {
    value = Number(value);
    if (!Number.isFinite(value)) value = 1.25;
    return clampNumber(value, 1, 2.5, 1.25);
  }

  function normalizeDiagramListType(value) {
    return value === "bullet" || value === "number" ? value : "none";
  }

  function normalizeDiagramListStyle(type, value) {
    if (type === "bullet") {
      return value === "circle" || value === "square" || value === "none" ? value : "disc";
    }
    if (type === "number") {
      return value === "lower-alpha" || value === "upper-alpha" || value === "lower-roman" || value === "upper-roman" || value === "none" ? value : "decimal";
    }
    return "none";
  }

  function normalizeDiagramElement(item, index) {
    item = item || {};
    var type = DIAGRAM_SHAPE_TYPES.test(item.type) || DIAGRAM_LINE_TYPES.test(item.type) ? item.type : "roundedRect";
    var isLine = isLineType(type);
    var text = item.text || "";
    if (!text && type === "wordart") text = "WordArt";
    if (!text && /^(plus|minus|multiply|divide|equal|brackets)$/.test(type)) {
      text = { plus: "+", minus: "-", multiply: "x", divide: "/", equal: "=", brackets: "[ ]" }[type] || "";
    }
    var fillValue = String(item.fill || "").trim();
    var fillFallback = type === "wordart" ? "#f59e0b" : type === "text" ? "transparent" : "#eef5ff";
    return {
      id: item.id || "diagram-" + index + "-" + Math.random().toString(36).slice(2),
      type: type,
      x: clampNumber(item.x, -500, 4000, 80),
      y: clampNumber(item.y, -500, 4000, 80),
      w: clampNumber(item.w, 20, 2000, isTextDiagramType(type) ? 180 : 150),
      h: clampNumber(item.h, 20, 2000, isTextDiagramType(type) ? 70 : 80),
      x1: clampNumber(item.x1 == null ? item.x : item.x1, -500, 4000, 80),
      y1: clampNumber(item.y1 == null ? item.y : item.y1, -500, 4000, 80),
      x2: clampNumber(item.x2 == null ? Number(item.x || 80) + 160 : item.x2, -500, 4000, 240),
      y2: clampNumber(item.y2 == null ? item.y : item.y2, -500, 4000, 80),
      text: text,
      fill: isLine ? "transparent" : fillValue === "transparent" ? "transparent" : normalizeChartColor(fillValue, fillFallback),
      stroke: normalizeChartColor(item.stroke, isLine ? "#475467" : "#111827"),
      strokeWidth: clampNumber(item.strokeWidth, 1, 16, 1),
      dash: normalizeDiagramDash(item.dash),
      opacity: clampNumber(item.opacity, 0.1, 1, 1),
      fontFamily: item.fontFamily || "Arial",
      fontSize: clampNumber(item.fontSize, 8, 96, type === "wordart" ? 36 : 16),
      bold: !!item.bold || type === "wordart",
      italic: !!item.italic,
      underline: !!item.underline,
      textColor: normalizeChartColor(item.textColor, "#172033"),
      highlightColor: String(item.highlightColor || "").trim() === "transparent" ? "transparent" : normalizeChartColor(item.highlightColor, "transparent"),
      textAlign: item.textAlign === "left" || item.textAlign === "right" || item.textAlign === "justify" ? item.textAlign : "center",
      lineHeight: normalizeDiagramLineHeight(item.lineHeight),
      spacingBefore: clampNumber(item.spacingBefore, 0, 48, 0),
      spacingAfter: clampNumber(item.spacingAfter, 0, 48, 0),
      indent: clampNumber(item.indent, 0, 8, 0),
      listType: normalizeDiagramListType(item.listType),
      listStyle: normalizeDiagramListStyle(normalizeDiagramListType(item.listType), item.listStyle),
      rotation: clampNumber(item.rotation, -360, 360, 0),
      group: item.group || "",
      flipX: !!item.flipX,
      flipY: !!item.flipY,
      image: item.image || "",
      name: item.name || ""
    };
  }

  function diagramElements(data) {
    var source = data && data.diagram && Array.isArray(data.diagram.elements) ? data.diagram.elements : Array.isArray(data && data.diagram) ? data.diagram : null;
    if (!source && data && data.code) source = simpleMermaidToDiagram(data.code);
    return (source ? source : defaultMermaidDiagram()).map(normalizeDiagramElement);
  }

  function selectedIdList(selectedIds) {
    return String(selectedIds || "").split(",").map(function (id) { return id.trim(); }).filter(Boolean);
  }

  function polygonPoints(cx, cy, rx, ry, count, startAngle) {
    var points = [];
    for (var i = 0; i < count; i += 1) {
      var angle = (startAngle || -Math.PI / 2) + (i / count) * Math.PI * 2;
      points.push((cx + Math.cos(angle) * rx) + "," + (cy + Math.sin(angle) * ry));
    }
    return points.join(" ");
  }

  function shapeMarkup(elm) {
    var x = elm.x;
    var y = elm.y;
    var w = elm.w;
    var h = elm.h;
    var cx = x + w / 2;
    var cy = y + h / 2;
    var fill = esc(elm.fill);
    var stroke = esc(elm.stroke);
    var sw = esc(elm.strokeWidth);
    var dash = dashArray(elm.dash);
    var common = ' fill="' + fill + '" stroke="' + stroke + '" stroke-width="' + sw + '"' + (dash ? ' stroke-dasharray="' + dash + '"' : "") + ' opacity="' + esc(elm.opacity) + '"';
    if (elm.type === "circle") return '<circle cx="' + cx + '" cy="' + cy + '" r="' + Math.min(w, h) / 2 + '"' + common + "></circle>";
    if (elm.type === "oval") return '<ellipse cx="' + cx + '" cy="' + cy + '" rx="' + w / 2 + '" ry="' + h / 2 + '"' + common + "></ellipse>";
    if (elm.type === "triangle") return '<polygon points="' + cx + "," + y + " " + (x + w) + "," + (y + h) + " " + x + "," + (y + h) + '"' + common + "></polygon>";
    if (elm.type === "diamond") return '<polygon points="' + cx + "," + y + " " + (x + w) + "," + cy + " " + cx + "," + (y + h) + " " + x + "," + cy + '"' + common + "></polygon>";
    if (elm.type === "pentagon") return '<polygon points="' + polygonPoints(cx, cy, w / 2, h / 2, 5) + '"' + common + "></polygon>";
    if (elm.type === "hexagon") return '<polygon points="' + polygonPoints(cx, cy, w / 2, h / 2, 6, 0) + '"' + common + "></polygon>";
    if (elm.type === "octagon") return '<polygon points="' + polygonPoints(cx, cy, w / 2, h / 2, 8, Math.PI / 8) + '"' + common + "></polygon>";
    if (elm.type === "parallelogram") return '<polygon points="' + (x + w * .18) + "," + y + " " + (x + w) + "," + y + " " + (x + w * .82) + "," + (y + h) + " " + x + "," + (y + h) + '"' + common + "></polygon>";
    if (elm.type === "trapezoid") return '<polygon points="' + (x + w * .18) + "," + y + " " + (x + w * .82) + "," + y + " " + (x + w) + "," + (y + h) + " " + x + "," + (y + h) + '"' + common + "></polygon>";
    if (elm.type === "heart") return '<path d="M' + cx + " " + (y + h * .86) + " C" + (x - w * .1) + " " + (y + h * .35) + " " + (x + w * .12) + " " + y + " " + cx + " " + (y + h * .28) + " C" + (x + w * .88) + " " + y + " " + (x + w * 1.1) + " " + (y + h * .35) + " " + cx + " " + (y + h * .86) + ' Z"' + common + "></path>";
    if (elm.type === "cloud") return '<path d="M' + (x + w * .22) + " " + (y + h * .72) + " C" + (x + w * .02) + " " + (y + h * .68) + " " + (x + w * .04) + " " + (y + h * .37) + " " + (x + w * .28) + " " + (y + h * .4) + " C" + (x + w * .34) + " " + (y + h * .12) + " " + (x + w * .68) + " " + (y + h * .1) + " " + (x + w * .74) + " " + (y + h * .38) + " C" + (x + w * 1.02) + " " + (y + h * .36) + " " + (x + w * 1.02) + " " + (y + h * .72) + " " + (x + w * .78) + " " + (y + h * .72) + ' Z"' + common + "></path>";
    if (elm.type === "cylinder") return '<path d="M' + x + " " + (y + h * .18) + " C" + x + " " + y + " " + (x + w) + " " + y + " " + (x + w) + " " + (y + h * .18) + " V" + (y + h * .82) + " C" + (x + w) + " " + (y + h) + " " + x + " " + (y + h) + " " + x + " " + (y + h * .82) + ' Z"' + common + "></path>" + '<ellipse cx="' + cx + '" cy="' + (y + h * .18) + '" rx="' + w / 2 + '" ry="' + h * .18 + '"' + common + "></ellipse>";
    if (elm.type === "cube") return '<path d="M' + x + " " + (y + h * .2) + " L" + (x + w * .2) + " " + y + " H" + (x + w) + " V" + (y + h * .8) + " L" + (x + w * .8) + " " + (y + h) + " H" + x + ' Z"' + common + "></path>" + '<path d="M' + (x + w * .2) + " " + y + " V" + (y + h * .8) + " H" + (x + w) + '" stroke="' + stroke + '" stroke-width="' + sw + '" fill="none" opacity="' + esc(elm.opacity) + '"></path>';
    if (elm.type === "callout") return '<polygon points="' + x + "," + y + " " + (x + w) + "," + y + " " + (x + w) + "," + (y + h * .75) + " " + (x + w * .62) + "," + (y + h * .75) + " " + (x + w * .5) + "," + (y + h) + " " + (x + w * .38) + "," + (y + h * .75) + " " + x + "," + (y + h * .75) + '"' + common + "></polygon>";
    if (elm.type === "roundCallout") return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + (h * .78) + '" rx="18"' + common + "></rect>" + '<path d="M' + (x + w * .44) + " " + (y + h * .78) + " L" + (x + w * .5) + " " + (y + h) + " L" + (x + w * .58) + " " + (y + h * .78) + ' Z"' + common + "></path>";
    if (elm.type === "thought") return '<ellipse cx="' + cx + '" cy="' + (y + h * .42) + '" rx="' + w / 2 + '" ry="' + h * .36 + '"' + common + "></ellipse>" + '<circle cx="' + (x + w * .62) + '" cy="' + (y + h * .82) + '" r="' + Math.min(w, h) * .08 + '"' + common + "></circle>" + '<circle cx="' + (x + w * .72) + '" cy="' + (y + h * .96) + '" r="' + Math.min(w, h) * .05 + '"' + common + "></circle>";
    if (elm.type === "text") return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" rx="4" fill="transparent" stroke="transparent"></rect>';
    if (elm.type === "wordart") return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" fill="transparent" stroke="transparent"></rect>';
    if (elm.type === "image") return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" rx="8" fill="#f8fafc" stroke="#d7dee8"></rect>' + (elm.image ? '<image href="' + esc(elm.image) + '" xlink:href="' + esc(elm.image) + '" x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" preserveAspectRatio="xMidYMid slice"></image>' : diagramText(elm));
    return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" rx="' + (elm.type === "roundedRect" ? 14 : 6) + '"' + common + "></rect>";
  }

  function diagramTextContent(elm) {
    var lines = String(elm.text || "").split(/\r?\n/);
    var highlight = elm.highlightColor && elm.highlightColor !== "transparent" ? 'background-color:' + esc(elm.highlightColor) + ";" : "";
    var lineStyle = highlight ? ' style="' + highlight + 'box-decoration-break:clone;-webkit-box-decoration-break:clone;"' : "";
    var items = lines.map(function (line) {
      return '<span' + lineStyle + ">" + (line ? esc(line) : "&nbsp;") + "</span>";
    });
    var listStyle = esc(normalizeDiagramListStyle(elm.listType, elm.listStyle));
    if (elm.listType === "bullet") {
      return '<ul style="margin:0;list-style-type:' + listStyle + ';padding-left:' + (18 + elm.indent * 18) + 'px;">' + items.map(function (item) { return "<li>" + item + "</li>"; }).join("") + "</ul>";
    }
    if (elm.listType === "number") {
      return '<ol style="margin:0;list-style-type:' + listStyle + ';padding-left:' + (22 + elm.indent * 18) + 'px;">' + items.map(function (item) { return "<li>" + item + "</li>"; }).join("") + "</ol>";
    }
    return items.map(function (item, index) {
      var margin = "margin:0;";
      if (index === 0 && elm.spacingBefore) margin += "padding-top:" + esc(elm.spacingBefore) + "px;";
      if (index === lines.length - 1 && elm.spacingAfter) margin += "padding-bottom:" + esc(elm.spacingAfter) + "px;";
      return '<div style="' + margin + '">' + item + "</div>";
    }).join("");
  }

  function diagramText(elm) {
    if (!elm.text) return "";
    if (elm.type === "wordart") {
      var anchor = elm.textAlign === "left" ? "start" : elm.textAlign === "right" ? "end" : "middle";
      var x = elm.textAlign === "left" ? elm.x + 12 : elm.textAlign === "right" ? elm.x + elm.w - 12 : elm.x + elm.w / 2;
      var y = elm.y + elm.h / 2;
      var decoration = elm.underline ? ' text-decoration="underline"' : "";
      var stroke = ' stroke="' + esc(elm.stroke) + '" stroke-width="' + Math.max(1, elm.strokeWidth) + '" paint-order="stroke fill"';
      return '<text x="' + x + '" y="' + y + '" text-anchor="' + anchor + '" dominant-baseline="middle" font-family="' + esc(elm.fontFamily) + ', Arial, sans-serif" font-size="' + esc(elm.fontSize) + '" font-weight="' + (elm.bold ? "800" : "400") + '" font-style="' + (elm.italic ? "italic" : "normal") + '"' + decoration + stroke + ' fill="' + esc(elm.textColor) + '">' + esc(elm.text || "") + "</text>";
    }
    var textBox = { x: elm.x, y: elm.y, w: elm.w, h: elm.h };
    if (elm.type === "callout") textBox.h = elm.h * .72;
    if (elm.type === "roundCallout") textBox.h = elm.h * .7;
    if (elm.type === "thought") textBox.h = elm.h * .72;
    var align = elm.textAlign || "center";
    var justify = align === "left" ? "flex-start" : align === "right" ? "flex-end" : "center";
    var decorationStyle = elm.underline ? "text-decoration:underline;" : "";
    var contentStyle = "box-sizing:border-box;width:100%;height:100%;display:flex;align-items:center;justify-content:" + justify + ";overflow:hidden;padding:4px 8px 4px " + (8 + elm.indent * 10) + "px;color:" + esc(elm.textColor) + ";font-family:" + esc(elm.fontFamily) + ",Arial,sans-serif;font-size:" + esc(elm.fontSize) + "px;font-weight:" + (elm.bold ? "800" : "400") + ";font-style:" + (elm.italic ? "italic" : "normal") + ";" + decorationStyle + "text-align:" + esc(align) + ";line-height:" + esc(elm.lineHeight) + ";white-space:normal;overflow-wrap:anywhere;word-break:break-word;";
    var innerStyle = "max-width:100%;";
    if (align === "justify") innerStyle += "text-align:justify;";
    return '<foreignObject class="rbe-mermaid-text-layer" x="' + esc(textBox.x) + '" y="' + esc(textBox.y) + '" width="' + esc(textBox.w) + '" height="' + esc(textBox.h) + '"><div xmlns="http://www.w3.org/1999/xhtml" style="' + contentStyle + '"><div style="' + innerStyle + '">' + diagramTextContent(elm) + "</div></div></foreignObject>";
  }

  function linePath(elm) {
    if (elm.type === "elbow") return "M" + elm.x1 + " " + elm.y1 + " L" + elm.x2 + " " + elm.y1 + " L" + elm.x2 + " " + elm.y2;
    if (elm.type === "curved" || elm.type === "curve") {
      var midX = (elm.x1 + elm.x2) / 2;
      return "M" + elm.x1 + " " + elm.y1 + " C" + midX + " " + elm.y1 + " " + midX + " " + elm.y2 + " " + elm.x2 + " " + elm.y2;
    }
    if (elm.type === "polyline") return "M" + elm.x1 + " " + elm.y1 + " L" + ((elm.x1 + elm.x2) / 2) + " " + (elm.y1 - 45) + " L" + elm.x2 + " " + elm.y2;
    if (elm.type === "scribble") {
      var d = "M" + elm.x1 + " " + elm.y1;
      for (var i = 1; i <= 8; i += 1) {
        var t = i / 8;
        d += " L" + (elm.x1 + (elm.x2 - elm.x1) * t) + " " + (elm.y1 + (elm.y2 - elm.y1) * t + (i % 2 ? 8 : -8));
      }
      return d;
    }
    return "M" + elm.x1 + " " + elm.y1 + " L" + elm.x2 + " " + elm.y2;
  }

  function elementBounds(elm) {
    if (isLineType(elm.type)) {
      return { x: Math.min(elm.x1, elm.x2), y: Math.min(elm.y1, elm.y2), w: Math.abs(elm.x2 - elm.x1), h: Math.abs(elm.y2 - elm.y1) };
    }
    return { x: elm.x, y: elm.y, w: elm.w, h: elm.h };
  }

  function rectsIntersect(a, b) {
    return a.x <= b.x + b.w && a.x + a.w >= b.x && a.y <= b.y + b.h && a.y + a.h >= b.y;
  }

  function selectionMarkup(elm, isSingle) {
    var b = elementBounds(elm);
    var markup = '<rect class="rbe-mermaid-selected-stroke" x="' + b.x + '" y="' + b.y + '" width="' + b.w + '" height="' + b.h + '" rx="6"></rect>';
    var handleStyle = ' style="--rbe-handle-color:#2563eb"';
    if (!isSingle || isLineType(elm.type)) {
      if (isSingle && isLineType(elm.type)) {
        markup += '<circle class="rbe-mermaid-handle" data-handle="start" cx="' + elm.x1 + '" cy="' + elm.y1 + '" r="5"' + handleStyle + '></circle><circle class="rbe-mermaid-handle" data-handle="end" cx="' + elm.x2 + '" cy="' + elm.y2 + '" r="5"' + handleStyle + '></circle>';
      }
      return markup;
    }
    [["nw", b.x, b.y], ["ne", b.x + b.w, b.y], ["se", b.x + b.w, b.y + b.h], ["sw", b.x, b.y + b.h]].forEach(function (handle) {
      markup += '<circle class="rbe-mermaid-handle" data-handle="' + handle[0] + '" cx="' + handle[1] + '" cy="' + handle[2] + '" r="5"' + handleStyle + '></circle>';
    });
    [
      ["n", b.x + b.w / 2, b.y, 24, 6],
      ["e", b.x + b.w, b.y + b.h / 2, 6, 24],
      ["s", b.x + b.w / 2, b.y + b.h, 24, 6],
      ["w", b.x, b.y + b.h / 2, 6, 24]
    ].forEach(function (handle) {
      markup += '<rect class="rbe-mermaid-handle" data-handle="' + handle[0] + '" x="' + (handle[1] - handle[3] / 2) + '" y="' + (handle[2] - handle[4] / 2) + '" width="' + handle[3] + '" height="' + handle[4] + '" rx="3"' + handleStyle + '></rect>';
    });
    var rotateX = b.x + b.w / 2;
    var rotateY = b.y - 28;
    markup += '<g class="rbe-mermaid-rotate" data-handle="rotate"><circle cx="' + rotateX + '" cy="' + rotateY + '" r="9"></circle><path d="M' + (rotateX + 2) + " " + (rotateY - 3) + " A4 4 0 1 0 " + (rotateX + 4) + " " + (rotateY + 2) + '" fill="none" stroke="#fff" stroke-width="1.6" stroke-linecap="round"></path><path d="M' + (rotateX + 3) + " " + (rotateY - 6) + " L" + (rotateX + 7) + " " + (rotateY - 3) + " L" + (rotateX + 3) + " " + rotateY + ' Z" fill="#fff"></path></g>';
    return markup;
  }

  function diagramSvg(elements, selectedIds, workspace, marquee) {
    elements = (elements || []).map(normalizeDiagramElement);
    workspace = workspace || defaultMermaidWorkspace();
    var selected = selectedIdList(selectedIds);
    var body = elements.map(function (elm) {
      var isSelected = selected.indexOf(elm.id) !== -1;
      var selectMarkup = isSelected ? selectionMarkup(elm, selected.length === 1) : "";
      var transform = "";
      if (!isLineType(elm.type) && (elm.rotation || elm.flipX || elm.flipY)) {
        var tx = elm.x + elm.w / 2;
        var ty = elm.y + elm.h / 2;
        transform = ' transform="' + (elm.rotation ? "rotate(" + esc(elm.rotation) + " " + tx + " " + ty + ") " : "") + ((elm.flipX || elm.flipY) ? "translate(" + tx + " " + ty + ") scale(" + (elm.flipX ? -1 : 1) + " " + (elm.flipY ? -1 : 1) + ") translate(" + (-tx) + " " + (-ty) + ")" : "") + '"';
      }
      if (isLineType(elm.type)) {
        var markerStart = elm.type === "leftArrow" || elm.type === "biArrow" ? ' marker-start="url(#rbe-arrow-head-start)"' : "";
        var markerEnd = /^(arrow|rightArrow|upArrow|downArrow|biArrow|elbow|curved)$/.test(elm.type) ? ' marker-end="url(#rbe-arrow-head)"' : "";
        var dash = dashArray(elm.dash);
        var originDot = isSelected && (markerStart || markerEnd) ? '<circle class="rbe-mermaid-arrow-origin" cx="' + esc(elm.x1) + '" cy="' + esc(elm.y1) + '" r="4"></circle>' : "";
        return '<g data-el-id="' + esc(elm.id) + '">' + selectMarkup + originDot + '<path d="' + linePath(elm) + '" fill="none" stroke="' + esc(elm.stroke) + '" stroke-width="' + esc(elm.strokeWidth) + '" stroke-linecap="round" stroke-linejoin="round"' + (dash ? ' stroke-dasharray="' + dash + '"' : "") + ' opacity="' + esc(elm.opacity) + '"' + markerStart + markerEnd + "></path></g>";
      }
      return '<g data-el-id="' + esc(elm.id) + '"' + transform + ">" + selectMarkup + shapeMarkup(elm) + (elm.type !== "image" ? diagramText(elm) : "") + "</g>";
    }).join("");
    if (marquee) body += '<rect class="rbe-mermaid-marquee" x="' + marquee.x + '" y="' + marquee.y + '" width="' + marquee.w + '" height="' + marquee.h + '"></rect>';
    return '<svg class="rbe-mermaid-canvas" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' + workspace.width * workspace.zoom + '" height="' + workspace.height * workspace.zoom + '" viewBox="0 0 ' + workspace.width + " " + workspace.height + '" role="img" aria-label="Diagram" tabindex="0"><defs><marker id="rbe-arrow-head" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth"><path d="M0,0 L0,6 L9,3 z" fill="#475467"></path></marker><marker id="rbe-arrow-head-start" markerWidth="10" markerHeight="10" refX="1" refY="3" orient="auto" markerUnits="strokeWidth"><path d="M9,0 L9,6 L0,3 z" fill="#475467"></path></marker></defs>' + body + "</svg>";
  }

  function renderDiagramCanvas(wrap) {
    var hidden = wrap.querySelector(".rbe-mermaid-diagram-json");
    var elements = [];
    try {
      elements = JSON.parse(hidden.value || "[]");
    } catch (error) {
      elements = defaultMermaidDiagram();
    }
    var workspace = readWorkspaceFromBlock(wrap);
    var marquee = null;
    try {
      marquee = wrap.dataset.marquee ? JSON.parse(wrap.dataset.marquee) : null;
    } catch (error) {}
    wrap.querySelector(".rbe-mermaid-stage").innerHTML = diagramSvg(elements, wrap.dataset.selectedIds || "", workspace, marquee);
  }

  function readWorkspaceFromBlock(wrap) {
    return {
      width: clampNumber((wrap.querySelector(".rbe-mermaid-workspace-w") || {}).value, 640, MERMAID_WORKSPACE_MAX, 1100),
      height: clampNumber((wrap.querySelector(".rbe-mermaid-workspace-h") || {}).value, 360, MERMAID_WORKSPACE_MAX, 620),
      zoom: clampNumber((wrap.querySelector(".rbe-mermaid-zoom") || {}).value, 0.35, 2.5, 1)
    };
  }

  function bindMermaidDesigner(editor, wrap) {
    var drag = null;
    var lastShapePointer = { id: "", time: 0 };

    function elements() {
      try {
        return JSON.parse(wrap.querySelector(".rbe-mermaid-diagram-json").value || "[]");
      } catch (error) {
        return defaultMermaidDiagram();
      }
    }

    function resizeWorkspaceToFit(items, point) {
      var workspace = readWorkspaceFromBlock(wrap);
      var base = defaultMermaidWorkspace();
      var maxX = point ? point.x : 0;
      var maxY = point ? point.y : 0;
      (items || []).forEach(function (elm) {
        var bounds = elementBounds(elm);
        maxX = Math.max(maxX, bounds.x + bounds.w);
        maxY = Math.max(maxY, bounds.y + bounds.h);
      });
      var width = Math.min(MERMAID_WORKSPACE_MAX, Math.max(base.width, Math.ceil((maxX + MERMAID_WORKSPACE_GROW_PADDING) / MERMAID_WORKSPACE_GROW_STEP) * MERMAID_WORKSPACE_GROW_STEP));
      var height = Math.min(MERMAID_WORKSPACE_MAX, Math.max(base.height, Math.ceil((maxY + MERMAID_WORKSPACE_GROW_PADDING) / MERMAID_WORKSPACE_GROW_STEP) * MERMAID_WORKSPACE_GROW_STEP));
      if (width !== workspace.width) wrap.querySelector(".rbe-mermaid-workspace-w").value = width;
      if (height !== workspace.height) wrap.querySelector(".rbe-mermaid-workspace-h").value = height;
    }

    function setElements(next, skipRender, growPoint) {
      var normalized = next.map(normalizeDiagramElement);
      resizeWorkspaceToFit(normalized, growPoint || null);
      wrap.querySelector(".rbe-mermaid-diagram-json").value = JSON.stringify(normalized);
      if (!skipRender) {
        renderDiagramCanvas(wrap);
        updateOptions();
      }
    }

    function historyStack(name) {
      try {
        return JSON.parse(wrap.dataset[name] || "[]");
      } catch (error) {
        return [];
      }
    }

    function writeHistory(name, stack) {
      wrap.dataset[name] = JSON.stringify(stack.slice(-60));
    }

    function snapshot() {
      return JSON.stringify({ elements: elements(), workspace: readWorkspaceFromBlock(wrap) });
    }

    function restoreSnapshot(value) {
      var state = JSON.parse(value);
      setElements(state.elements || []);
      if (state.workspace) {
        wrap.querySelector(".rbe-mermaid-workspace-w").value = state.workspace.width || 1100;
        wrap.querySelector(".rbe-mermaid-workspace-h").value = state.workspace.height || 620;
        wrap.querySelector(".rbe-mermaid-zoom").value = state.workspace.zoom || 1;
      }
      renderDiagramCanvas(wrap);
      updateOptions();
    }

    function pushUndo() {
      var undo = historyStack("undoStack");
      undo.push(snapshot());
      writeHistory("undoStack", undo);
      writeHistory("redoStack", []);
      refreshHistoryButtons();
    }

    function refreshHistoryButtons() {
      var undo = wrap.querySelector("[data-diagram-action='undo']");
      var redo = wrap.querySelector("[data-diagram-action='redo']");
      if (undo) undo.disabled = !historyStack("undoStack").length;
      if (redo) redo.disabled = !historyStack("redoStack").length;
    }

    function undo() {
      var undoStack = historyStack("undoStack");
      if (!undoStack.length) return;
      var redoStack = historyStack("redoStack");
      redoStack.push(snapshot());
      restoreSnapshot(undoStack.pop());
      writeHistory("undoStack", undoStack);
      writeHistory("redoStack", redoStack);
      refreshHistoryButtons();
      editor.changed(true);
    }

    function redo() {
      var redoStack = historyStack("redoStack");
      if (!redoStack.length) return;
      var undoStack = historyStack("undoStack");
      undoStack.push(snapshot());
      restoreSnapshot(redoStack.pop());
      writeHistory("undoStack", undoStack);
      writeHistory("redoStack", redoStack);
      refreshHistoryButtons();
      editor.changed(true);
    }

    function selectedIds() {
      return selectedIdList(wrap.dataset.selectedIds || "");
    }

    function setSelected(ids) {
      wrap.dataset.selectedIds = (ids || []).filter(Boolean).join(",");
      renderDiagramCanvas(wrap);
      updateOptions();
    }

    function selectedElements() {
      var ids = selectedIds();
      return elements().filter(function (elm) { return ids.indexOf(elm.id) !== -1; });
    }

    function setTool(tool, sourceSelect) {
      wrap.dataset.tool = tool;
      Array.prototype.slice.call(wrap.querySelectorAll(".rbe-mermaid-tool[data-tool]")).forEach(function (button) {
        button.classList.toggle("is-active", button.dataset.tool === tool);
      });
      Array.prototype.slice.call(wrap.querySelectorAll(".rbe-mermaid-menu-select")).forEach(function (select) {
        if (select !== sourceSelect) select.value = "";
      });
    }

    function point(event) {
      var svg = wrap.querySelector(".rbe-mermaid-canvas");
      var rect = svg.getBoundingClientRect();
      var workspace = readWorkspaceFromBlock(wrap);
      var raw = { x: ((event.clientX - rect.left) / rect.width) * workspace.width, y: ((event.clientY - rect.top) / rect.height) * workspace.height };
      return event.altKey ? raw : { x: Math.round(raw.x / 10) * 10, y: Math.round(raw.y / 10) * 10 };
    }

    function autoScrollStage(event) {
      var stage = wrap.querySelector(".rbe-mermaid-stage");
      if (!stage) return;
      var rect = stage.getBoundingClientRect();
      var edge = 56;
      var step = 30;
      var dx = 0;
      var dy = 0;
      if (event.clientX > rect.right - edge) dx = step;
      else if (event.clientX < rect.left + edge) dx = -step;
      if (event.clientY > rect.bottom - edge) dy = step;
      else if (event.clientY < rect.top + edge) dy = -step;
      if (dx || dy) {
        stage.scrollLeft += dx;
        stage.scrollTop += dy;
      }
    }

    function constrainLinePoint(start, current) {
      var dx = current.x - start.x;
      var dy = current.y - start.y;
      if (!dx && !dy) return current;
      var length = Math.sqrt(dx * dx + dy * dy);
      var snappedAngle = Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) * (Math.PI / 4);
      return {
        x: Math.round((start.x + Math.cos(snappedAngle) * length) / 10) * 10,
        y: Math.round((start.y + Math.sin(snappedAngle) * length) / 10) * 10
      };
    }

    function selectedBounds() {
      var list = selectedElements();
      if (!list.length) return null;
      var bounds = list.map(elementBounds);
      var minX = Math.min.apply(null, bounds.map(function (b) { return b.x; }));
      var minY = Math.min.apply(null, bounds.map(function (b) { return b.y; }));
      var maxX = Math.max.apply(null, bounds.map(function (b) { return b.x + b.w; }));
      var maxY = Math.max.apply(null, bounds.map(function (b) { return b.y + b.h; }));
      return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
    }

    function updateOptions() {
      var selected = selectedElements();
      var panel = wrap.querySelector(".rbe-mermaid-stylebar");
      if (!panel || !wrap.querySelector(".rbe-mermaid-option-text")) return;
      panel.hidden = !selected.length;
      if (!selected.length) return;
      var first = selected[0];
      var isLineSelection = selected.every(function (elm) { return isLineType(elm.type); });
      var isTextCapable = selected.some(function (elm) { return !isLineType(elm.type) && elm.type !== "image"; });
      panel.classList.toggle("is-line-selection", isLineSelection);
      panel.classList.toggle("is-text-capable", isTextCapable);
      wrap.querySelector(".rbe-mermaid-option-text").value = selected.length === 1 ? first.text || "" : selected.length + " objects selected";
      wrap.querySelector(".rbe-mermaid-option-text").disabled = selected.length !== 1;
      wrap.querySelector(".rbe-mermaid-option-font").value = first.fontFamily || "Arial";
      wrap.querySelector(".rbe-mermaid-option-size").value = first.fontSize || 16;
      var fillColor = first.fill === "transparent" ? "#ffffff" : normalizeChartColor(first.fill, "#eef5ff");
      var strokeColor = normalizeChartColor(first.stroke, "#111827");
      var textColor = normalizeChartColor(first.textColor, "#172033");
      var highlightColor = first.highlightColor === "transparent" ? "#fff3b0" : normalizeChartColor(first.highlightColor, "#fff3b0");
      wrap.querySelector(".rbe-mermaid-option-fill").value = fillColor;
      wrap.querySelector(".rbe-mermaid-option-stroke").value = strokeColor;
      wrap.querySelector(".rbe-mermaid-option-text-color").value = textColor;
      wrap.querySelector(".rbe-mermaid-option-highlight").value = highlightColor;
      wrap.querySelector(".rbe-mermaid-option-align").value = first.textAlign || "center";
      wrap.querySelector(".rbe-mermaid-option-line-height").value = String(normalizeDiagramLineHeight(first.lineHeight));
      wrap.querySelector(".rbe-mermaid-option-bullet").value = first.listType === "bullet" ? normalizeDiagramListStyle("bullet", first.listStyle) : "none";
      wrap.querySelector(".rbe-mermaid-option-number").value = first.listType === "number" ? normalizeDiagramListStyle("number", first.listStyle) : "none";
      panel.style.setProperty("--rbe-mermaid-fill", fillColor);
      panel.style.setProperty("--rbe-mermaid-stroke", strokeColor);
      panel.style.setProperty("--rbe-mermaid-text", textColor);
      panel.style.setProperty("--rbe-mermaid-highlight", first.highlightColor === "transparent" ? "transparent" : highlightColor);
      wrap.querySelector(".rbe-mermaid-option-weight").value = first.strokeWidth || 1;
      wrap.querySelector(".rbe-mermaid-option-dash").value = normalizeDiagramDash(first.dash);
      wrap.querySelector(".rbe-mermaid-option-opacity").value = first.opacity || 1;
      Array.prototype.slice.call(wrap.querySelectorAll("[data-format-toggle]")).forEach(function (button) {
        var prop = button.dataset.formatToggle;
        button.classList.toggle("is-active", !!first[prop]);
      });
      var activeControls = [
        [".rbe-mermaid-bullet-control", first.listType === "bullet"],
        [".rbe-mermaid-number-control", first.listType === "number"],
        [".rbe-mermaid-spacing-before", !!first.spacingBefore],
        [".rbe-mermaid-spacing-after", !!first.spacingAfter]
      ];
      activeControls.forEach(function (item) {
        var node = wrap.querySelector(item[0]);
        if (node) node.classList.toggle("is-active", !!item[1]);
      });
    }

    function updateSelected(mutator, skipUndo) {
      var ids = selectedIds();
      if (!ids.length) return;
      if (!skipUndo) pushUndo();
      setElements(elements().map(function (elm) {
        if (ids.indexOf(elm.id) !== -1) mutator(elm);
        return elm;
      }));
      editor.changed(true);
    }

    function applyStylebarChange(event) {
      if (!event.target.closest(".rbe-mermaid-stylebar")) return false;
      var bulletChanged = event.target.classList.contains("rbe-mermaid-option-bullet");
      var numberChanged = event.target.classList.contains("rbe-mermaid-option-number");
      updateSelected(function (elm) {
        if (event.target.classList.contains("rbe-mermaid-option-text")) elm.text = event.target.value;
        elm.fontFamily = wrap.querySelector(".rbe-mermaid-option-font").value || "Arial";
        elm.fontSize = Number(wrap.querySelector(".rbe-mermaid-option-size").value || 16);
        if (!isLineType(elm.type)) elm.fill = wrap.querySelector(".rbe-mermaid-option-fill").value;
        elm.stroke = wrap.querySelector(".rbe-mermaid-option-stroke").value;
        elm.textColor = wrap.querySelector(".rbe-mermaid-option-text-color").value;
        elm.highlightColor = event.target.classList.contains("rbe-mermaid-option-highlight") ? wrap.querySelector(".rbe-mermaid-option-highlight").value : elm.highlightColor;
        elm.textAlign = wrap.querySelector(".rbe-mermaid-option-align").value || "center";
        elm.lineHeight = normalizeDiagramLineHeight(wrap.querySelector(".rbe-mermaid-option-line-height").value);
        if (bulletChanged) {
          var bulletValue = wrap.querySelector(".rbe-mermaid-option-bullet").value || "none";
          elm.listType = bulletValue === "none" ? "none" : "bullet";
          elm.listStyle = bulletValue === "none" ? "none" : bulletValue;
          wrap.querySelector(".rbe-mermaid-option-number").value = "none";
        }
        if (numberChanged) {
          var numberValue = wrap.querySelector(".rbe-mermaid-option-number").value || "none";
          elm.listType = numberValue === "none" ? "none" : "number";
          elm.listStyle = numberValue === "none" ? "none" : numberValue;
          wrap.querySelector(".rbe-mermaid-option-bullet").value = "none";
        }
        elm.strokeWidth = Number(wrap.querySelector(".rbe-mermaid-option-weight").value || 1);
        elm.dash = wrap.querySelector(".rbe-mermaid-option-dash").value;
        elm.opacity = Number(wrap.querySelector(".rbe-mermaid-option-opacity").value || 1);
      }, true);
      return true;
    }

    function createElement(type, p, fileData) {
      var line = isLineType(type);
      var text = "";
      if (type === "wordart") text = "WordArt";
      else if (/^(plus|minus|multiply|divide|equal|brackets)$/.test(type)) text = { plus: "+", minus: "-", multiply: "x", divide: "/", equal: "=", brackets: "[ ]" }[type] || "";
      return normalizeDiagramElement({
        id: "diagram-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 6),
        type: type,
        x: p.x,
        y: p.y,
        w: type === "image" ? 220 : type === "wordart" ? 240 : type === "text" ? 190 : 10,
        h: type === "image" ? 150 : type === "wordart" ? 78 : type === "text" ? 70 : 10,
        x1: p.x,
        y1: p.y,
        x2: p.x,
        y2: p.y,
        text: text,
        image: fileData && fileData.url ? fileData.url : "",
        name: fileData && fileData.name ? fileData.name : "",
        fill: line || type === "text" ? "transparent" : type === "wordart" ? "#f59e0b" : "#eef5ff",
        stroke: line ? "#475467" : "#111827",
        strokeWidth: 1,
        fontSize: type === "wordart" ? 36 : 16
      });
    }

    function editableDiagramElement(item) {
      return item && !isLineType(item.type) && item.type !== "image";
    }

    function restoreTextEditorTargets() {
      Array.prototype.slice.call(wrap.querySelectorAll(".rbe-mermaid-hidden-text")).forEach(function (node) {
        node.classList.remove("rbe-mermaid-hidden-text");
      });
    }

    function hideTextForEditor(id) {
      restoreTextEditorTargets();
      var group = Array.prototype.slice.call(wrap.querySelectorAll(".rbe-mermaid-canvas [data-el-id]")).filter(function (node) {
        return node.getAttribute("data-el-id") === id;
      })[0];
      if (!group) return;
      Array.prototype.slice.call(group.querySelectorAll("text,.rbe-mermaid-text-layer")).forEach(function (node) {
        node.classList.add("rbe-mermaid-hidden-text");
      });
    }

    function placeTextEditorCaretAtEnd(node) {
      if (!node) return;
      node.focus();
      try {
        node.setSelectionRange(node.value.length, node.value.length, "none");
      } catch (error) {}
    }

    function scheduleTextEditorCaretAtEnd(node) {
      if (!node) return;
      node.dataset.forceCaretEnd = "true";
      placeTextEditorCaretAtEnd(node);
      if (window.requestAnimationFrame) {
        window.requestAnimationFrame(function () { placeTextEditorCaretAtEnd(node); });
      }
      [0, 40, 120].forEach(function (delay) {
        setTimeout(function () { placeTextEditorCaretAtEnd(node); }, delay);
      });
    }

    function closeTextEditor(save) {
      var active = wrap.querySelector(".rbe-mermaid-text-editor");
      if (!active) return;
      var id = active.dataset.editId || "";
      var nextText = active.value || active.textContent || "";
      restoreTextEditorTargets();
      active.remove();
      if (!save || !id) return;
      pushUndo();
      setElements(elements().map(function (elm) {
        if (elm.id === id) elm.text = nextText.trim();
        return elm;
      }));
      setSelected([id]);
      editor.changed(true);
    }

    function openTextEditor(id, seedText) {
      var item = elements().filter(function (elm) { return elm.id === id; })[0];
      if (!editableDiagramElement(item)) return;
      closeTextEditor(true);
      var stage = wrap.querySelector(".rbe-mermaid-stage");
      var workspace = readWorkspaceFromBlock(wrap);
      var svg = wrap.querySelector(".rbe-mermaid-canvas");
      var svgRect = svg ? svg.getBoundingClientRect() : null;
      var scaleX = svgRect && svgRect.width ? svgRect.width / workspace.width : workspace.zoom;
      var scaleY = svgRect && svgRect.height ? svgRect.height / workspace.height : workspace.zoom;
      var scale = Math.min(scaleX, scaleY);
      var node = document.createElement("textarea");
      var initial = seedText != null ? seedText : item.text || "";
      node.className = "rbe-mermaid-text-editor";
      node.dataset.editId = id;
      node.spellcheck = false;
      node.wrap = "soft";
      node.setAttribute("role", "textbox");
      node.setAttribute("aria-label", "Shape text");
      if (item.type === "text") node.setAttribute("placeholder", "Text");
      var textBox = { x: item.x, y: item.y, w: item.w, h: item.h };
      if (item.type === "callout") textBox.h = item.h * .72;
      if (item.type === "roundCallout") textBox.h = item.h * .7;
      if (item.type === "thought") textBox.h = item.h * .72;
      var pixelX = textBox.x * scaleX;
      var pixelY = textBox.y * scaleY;
      var pixelW = Math.max(42, textBox.w * scaleX);
      var pixelH = Math.max(32, textBox.h * scaleY);
      node.style.left = pixelX + "px";
      node.style.top = pixelY + "px";
      node.style.width = pixelW + "px";
      node.style.height = pixelH + "px";
      node.style.fontFamily = item.fontFamily || "Arial";
      var fontSize = Math.max(18, (item.fontSize || 16) * scale);
      var lineHeight = Math.round(fontSize * 1.25);
      node.style.fontSize = fontSize + "px";
      lineHeight = Math.round(fontSize * normalizeDiagramLineHeight(item.lineHeight));
      node.style.lineHeight = lineHeight + "px";
      node.style.padding = Math.max(4, Math.floor((pixelH - lineHeight) / 2)) + "px 8px 4px";
      node.style.fontWeight = item.bold ? "800" : "700";
      node.style.fontStyle = item.italic ? "italic" : "normal";
      node.style.color = item.textColor || "#172033";
      node.style.textAlign = item.textAlign || "center";
      node.style.backgroundColor = item.highlightColor && item.highlightColor !== "transparent" ? item.highlightColor : "transparent";
      node.value = initial;
      hideTextForEditor(id);
      stage.appendChild(node);
      scheduleTextEditorCaretAtEnd(node);
      setTimeout(function () {
        node.dataset.allowPointerCaret = "true";
      }, 250);
      node.addEventListener("pointerdown", function () {
        if (node.dataset.allowPointerCaret === "true") node.dataset.forceCaretEnd = "";
      });
      node.addEventListener("keydown", function (event) {
        event.stopPropagation();
        if (event.ctrlKey || event.metaKey || event.altKey) {
          node.dataset.forceCaretEnd = "";
          return;
        }
        if (node.dataset.forceCaretEnd === "true" && (event.key.length === 1 || event.key === "Backspace" || event.key === "Delete")) {
          placeTextEditorCaretAtEnd(node);
          node.dataset.forceCaretEnd = "";
          if (event.key.length === 1 && /^[A-Za-z0-9]$/.test(event.key) && node.value && !/\s$/.test(node.value)) {
            event.preventDefault();
            node.setRangeText(" " + event.key, node.value.length, node.value.length, "end");
            return;
          }
        }
        if (event.key === "Enter" && !event.shiftKey) {
          event.preventDefault();
          closeTextEditor(true);
        }
        if (event.key === "Escape") {
          event.preventDefault();
          closeTextEditor(false);
        }
      });
      node.addEventListener("blur", function () {
        closeTextEditor(true);
      }, { once: true });
    }

    function applyAction(action) {
      var list = elements();
      var ids = selectedIds();
      if (action === "image") {
        wrap.querySelector(".rbe-mermaid-image-input").click();
        return;
      }
      if (!ids.length && !/^(paste|downloadSvg|downloadPng|downloadJpeg|downloadPdf|fit|zoomIn|zoomOut)$/.test(action)) return;
      if (action === "undo") return undo();
      if (action === "redo") return redo();
      if (action === "copy" || action === "cut") {
        wrap.dataset.clipboard = JSON.stringify(selectedElements());
        if (action === "cut") {
          pushUndo();
          setElements(list.filter(function (elm) { return ids.indexOf(elm.id) === -1; }));
          setSelected([]);
          editor.changed(true);
        }
        return;
      }
      if (action === "paste") {
        var clip = [];
        try { clip = JSON.parse(wrap.dataset.clipboard || "[]"); } catch (error) {}
        if (!clip.length) return;
        pushUndo();
        var pasted = clip.map(function (elm) {
          elm = normalizeDiagramElement(elm);
          elm.id = "diagram-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 6);
          if (isLineType(elm.type)) {
            elm.x1 += 30; elm.x2 += 30; elm.y1 += 30; elm.y2 += 30;
          } else {
            elm.x += 30; elm.y += 30;
          }
          return elm;
        });
        setElements(list.concat(pasted));
        setSelected(pasted.map(function (elm) { return elm.id; }));
        editor.changed(true);
        return;
      }
      if (action === "duplicate") {
        wrap.dataset.clipboard = JSON.stringify(selectedElements());
        return applyAction("paste");
      }
      if (action === "delete") {
        pushUndo();
        setElements(list.filter(function (elm) { return ids.indexOf(elm.id) === -1; }));
        setSelected([]);
        editor.changed(true);
        return;
      }
      if (/^download/.test(action)) return downloadDiagram(wrap, action);
      if (action === "zoomIn" || action === "zoomOut" || action === "fit") {
        var zoom = wrap.querySelector(".rbe-mermaid-zoom");
        zoom.value = action === "fit" ? 1 : clampNumber(Number(zoom.value || 1) + (action === "zoomIn" ? .1 : -.1), .35, 2.5, 1);
        renderDiagramCanvas(wrap);
        editor.changed(true);
        return;
      }
      pushUndo();
      if (action === "bringFront") list = list.filter(function (elm) { return ids.indexOf(elm.id) === -1; }).concat(list.filter(function (elm) { return ids.indexOf(elm.id) !== -1; }));
      else if (action === "sendBack") list = list.filter(function (elm) { return ids.indexOf(elm.id) !== -1; }).concat(list.filter(function (elm) { return ids.indexOf(elm.id) === -1; }));
      else if (action === "bringForward" || action === "sendBackward") {
        var step = action === "bringForward" ? 1 : -1;
        ids.forEach(function (id) {
          var index = list.findIndex(function (elm) { return elm.id === id; });
          var nextIndex = index + step;
          if (index < 0 || nextIndex < 0 || nextIndex >= list.length) return;
          var item = list[index];
          list.splice(index, 1);
          list.splice(nextIndex, 0, item);
        });
      }
      else {
        var bounds = selectedBounds();
        if (action === "group") {
          var groupId = "group-" + Date.now().toString(36);
          list.forEach(function (elm) { if (ids.indexOf(elm.id) !== -1) elm.group = groupId; });
        }
        if (action === "ungroup") {
          list.forEach(function (elm) { if (ids.indexOf(elm.id) !== -1) elm.group = ""; });
        }
        if (action === "distributeH" && ids.length > 2) {
          var hItems = list.filter(function (elm) { return ids.indexOf(elm.id) !== -1 && !isLineType(elm.type); }).sort(function (a, b) { return a.x - b.x; });
          var firstX = hItems[0].x;
          var lastX = hItems[hItems.length - 1].x;
          var gapX = (lastX - firstX) / (hItems.length - 1);
          hItems.forEach(function (elm, index) { elm.x = firstX + gapX * index; });
        }
        if (action === "distributeV" && ids.length > 2) {
          var vItems = list.filter(function (elm) { return ids.indexOf(elm.id) !== -1 && !isLineType(elm.type); }).sort(function (a, b) { return a.y - b.y; });
          var firstY = vItems[0].y;
          var lastY = vItems[vItems.length - 1].y;
          var gapY = (lastY - firstY) / (vItems.length - 1);
          vItems.forEach(function (elm, index) { elm.y = firstY + gapY * index; });
        }
        list.forEach(function (elm) {
          if (ids.indexOf(elm.id) === -1) return;
          if (action === "rotateClockwise") elm.rotation = (elm.rotation || 0) + 15;
          if (action === "rotateCounter") elm.rotation = (elm.rotation || 0) - 15;
          if (action === "flipH" && !isLineType(elm.type)) elm.flipX = !elm.flipX;
          if (action === "flipV" && !isLineType(elm.type)) elm.flipY = !elm.flipY;
          if (!bounds || isLineType(elm.type)) return;
          if (action === "alignLeft") elm.x = bounds.x;
          if (action === "alignCenter") elm.x = bounds.x + bounds.w / 2 - elm.w / 2;
          if (action === "alignRight") elm.x = bounds.x + bounds.w - elm.w;
          if (action === "alignTop") elm.y = bounds.y;
          if (action === "alignMiddle") elm.y = bounds.y + bounds.h / 2 - elm.h / 2;
          if (action === "alignBottom") elm.y = bounds.y + bounds.h - elm.h;
        });
      }
      setElements(list);
      editor.changed(true);
    }

    function downloadDiagram(block, action) {
      var svg = diagramSvg(elements(), "", readWorkspaceFromBlock(block), null);
      var mime = "image/svg+xml";
      var ext = "svg";
      if (action === "downloadSvg") {
        triggerDownload("blockwriteai-diagram.svg", "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg));
        return;
      }
      if (action === "downloadPdf") {
        var popup = window.open("", "_blank");
        if (popup) {
          popup.document.write("<!doctype html><title>BlockWriteAI Diagram</title><style>body{margin:24px;font-family:Arial,sans-serif}svg{max-width:100%;height:auto}</style>" + svg + "<script>setTimeout(function(){window.print();},250);<\/script>");
          popup.document.close();
        }
        return;
      }
      var img = new Image();
      var url = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg);
      img.onload = function () {
        var workspace = readWorkspaceFromBlock(block);
        var canvas = document.createElement("canvas");
        canvas.width = workspace.width;
        canvas.height = workspace.height;
        var ctx = canvas.getContext("2d");
        ctx.fillStyle = "#ffffff";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(img, 0, 0);
        mime = action === "downloadJpeg" ? "image/jpeg" : "image/png";
        ext = action === "downloadJpeg" ? "jpg" : "png";
        triggerDownload("blockwriteai-diagram." + ext, canvas.toDataURL(mime, .92));
      };
      img.src = url;
    }

    function triggerDownload(name, href) {
      var link = document.createElement("a");
      link.href = href;
      link.download = name;
      document.body.appendChild(link);
      link.click();
      link.remove();
    }

    wrap.addEventListener("click", function (event) {
      var choiceButton = event.target.closest(".rbe-mermaid-choice[data-tool],.rbe-mermaid-line-choice[data-tool]");
      if (choiceButton) {
        event.preventDefault();
        setTool(choiceButton.dataset.tool || "select");
        return;
      }
      var toolButton = event.target.closest(".rbe-mermaid-tool[data-tool]");
      if (toolButton) {
        event.preventDefault();
        setTool(toolButton.dataset.tool || "select");
        return;
      }
      var textActionButton = event.target.closest("[data-text-action]");
      if (textActionButton) {
        event.preventDefault();
        var textAction = textActionButton.dataset.textAction || "";
        updateSelected(function (elm) {
          if (isLineType(elm.type) || elm.type === "image") return;
          if (textAction === "indent") elm.indent = clampNumber(Number(elm.indent || 0) + 1, 0, 8, 0);
          if (textAction === "outdent") elm.indent = clampNumber(Number(elm.indent || 0) - 1, 0, 8, 0);
          if (textAction === "spacing-before") elm.spacingBefore = elm.spacingBefore ? 0 : 10;
          if (textAction === "spacing-after") elm.spacingAfter = elm.spacingAfter ? 0 : 10;
          if (textAction === "reset-highlight") {
            elm.highlightColor = "transparent";
          }
        });
        updateOptions();
        return;
      }
      var alignButton = event.target.closest(".rbe-mermaid-align");
      if (alignButton) {
        event.preventDefault();
        wrap.querySelector(".rbe-mermaid-alignment").value = alignButton.dataset.align || "center";
        Array.prototype.slice.call(wrap.querySelectorAll(".rbe-mermaid-align")).forEach(function (button) {
          button.classList.toggle("is-active", button === alignButton);
        });
        editor.changed(true);
        return;
      }
      var styleButton = event.target.closest("[data-style-action]");
      if (styleButton) {
        event.preventDefault();
        var sizeInput = wrap.querySelector(".rbe-mermaid-option-size");
        var nextSize = Number(sizeInput.value || 16) + (styleButton.dataset.styleAction === "font-size-up" ? 2 : -2);
        sizeInput.value = clampNumber(nextSize, 8, 96, 16);
        applyStylebarChange({ target: sizeInput });
        updateOptions();
        return;
      }
      var actionButton = event.target.closest("[data-diagram-action]");
      if (actionButton) {
        event.preventDefault();
        applyAction(actionButton.dataset.diagramAction);
      }
    });

    wrap.addEventListener("change", function (event) {
      if (applyStylebarChange(event)) return;
      var picker = event.target.closest(".rbe-mermaid-menu-select");
      if (picker && picker.value) {
        setTool(picker.value, picker);
        return;
      }
      var action = event.target.closest(".rbe-mermaid-action-select");
      if (action && action.value) {
        applyAction(action.value);
        action.value = "";
        return;
      }
      if (event.target.closest(".rbe-mermaid-workspace")) {
        pushUndo();
        renderDiagramCanvas(wrap);
        editor.changed(true);
      }
    });

    wrap.addEventListener("input", function (event) {
      applyStylebarChange(event);
    });

    wrap.addEventListener("click", function (event) {
      var formatButton = event.target.closest("[data-format-toggle]");
      if (!formatButton) return;
      event.preventDefault();
      var prop = formatButton.dataset.formatToggle;
      updateSelected(function (elm) { elm[prop] = !elm[prop]; });
    });

    wrap.addEventListener("dblclick", function (event) {
      var target = event.target.closest("[data-el-id]");
      if (!target) return;
      var item = elements().filter(function (elm) { return elm.id === target.dataset.elId; })[0];
      if (!editableDiagramElement(item)) return;
      event.preventDefault();
      setSelected([item.id]);
      openTextEditor(item.id);
    });

    wrap.addEventListener("pointerdown", function (event) {
      var svg = event.target.closest(".rbe-mermaid-canvas");
      if (!svg) return;
      if (wrap.querySelector(".rbe-mermaid-text-editor") && !event.target.closest(".rbe-mermaid-text-editor")) {
        closeTextEditor(true);
        svg = wrap.querySelector(".rbe-mermaid-canvas");
        if (!svg) return;
      }
      wrap.focus();
      event.preventDefault();
      var p = point(event);
      var handle = event.target.closest("[data-handle]");
      var target = event.target.closest("[data-el-id]");
      var tool = wrap.dataset.tool || "select";
      if (target && tool === "select" && !handle) {
        var targetId = target.dataset.elId;
        var now = Date.now();
        var isFastRepeat = lastShapePointer.id === targetId && now - lastShapePointer.time < 430;
        lastShapePointer = { id: targetId, time: now };
        if (Number(event.detail || 0) >= 2 || isFastRepeat) {
          var doubleClicked = elements().filter(function (elm) { return elm.id === targetId; })[0];
          if (editableDiagramElement(doubleClicked)) {
            drag = null;
            setSelected([doubleClicked.id]);
            openTextEditor(doubleClicked.id);
            return;
          }
        }
      } else if (!target) {
        lastShapePointer = { id: "", time: 0 };
      }
      if (handle && selectedIds().length === 1) {
        pushUndo();
        drag = { mode: handle.dataset.handle === "rotate" ? "rotate" : "resize", handle: handle.dataset.handle, start: p, ids: selectedIds(), snapshot: elements() };
        svg.setPointerCapture(event.pointerId);
        return;
      }
      if (target && tool === "select") {
        var id = target.dataset.elId;
        var clicked = elements().filter(function (elm) { return elm.id === id; })[0];
        var groupIds = clicked && clicked.group ? elements().filter(function (elm) { return elm.group === clicked.group; }).map(function (elm) { return elm.id; }) : [id];
        var current = selectedIds();
        if (event.shiftKey || event.ctrlKey) {
          groupIds.forEach(function (groupId) {
            if (current.indexOf(groupId) === -1) current.push(groupId);
            else current = current.filter(function (item) { return item !== groupId; });
          });
          setSelected(current);
        } else if (current.indexOf(id) === -1) {
          setSelected(groupIds);
        }
        pushUndo();
        drag = { mode: "move", ids: selectedIds(), start: p, snapshot: elements() };
        svg.setPointerCapture(event.pointerId);
        return;
      }
      if (tool === "select") {
        setSelected([]);
        drag = { mode: "marquee", start: p };
        svg.setPointerCapture(event.pointerId);
        return;
      }
      var type = tool;
      if (!DIAGRAM_SHAPE_TYPES.test(type) && !DIAGRAM_LINE_TYPES.test(type)) return;
      pushUndo();
      var created = createElement(type, p);
      setElements(elements().concat([created]));
      setSelected([created.id]);
      drag = { mode: "draw", id: created.id, start: p };
      svg.setPointerCapture(event.pointerId);
    });

    wrap.addEventListener("pointermove", function (event) {
      if (!drag) return;
      autoScrollStage(event);
      var p = point(event);
      resizeWorkspaceToFit(elements(), p);
      if (drag.mode === "marquee") {
        var marquee = { x: Math.min(drag.start.x, p.x), y: Math.min(drag.start.y, p.y), w: Math.abs(p.x - drag.start.x), h: Math.abs(p.y - drag.start.y) };
        wrap.dataset.marquee = JSON.stringify(marquee);
        renderDiagramCanvas(wrap);
        return;
      }
      var next = elements().map(function (elm) {
        if (drag.mode === "move" && drag.ids.indexOf(elm.id) !== -1) {
          var original = drag.snapshot.filter(function (item) { return item.id === elm.id; })[0] || elm;
          var dx = p.x - drag.start.x;
          var dy = p.y - drag.start.y;
          if (isLineType(elm.type)) {
            elm.x1 = original.x1 + dx;
            elm.y1 = original.y1 + dy;
            elm.x2 = original.x2 + dx;
            elm.y2 = original.y2 + dy;
          } else {
            elm.x = original.x + dx;
            elm.y = original.y + dy;
          }
        } else if (drag.mode === "draw" && elm.id === drag.id) {
          if (isLineType(elm.type)) {
            var linePoint = event.shiftKey ? constrainLinePoint(drag.start, p) : p;
            elm.x2 = linePoint.x;
            elm.y2 = linePoint.y;
          } else {
            elm.x = Math.min(drag.start.x, p.x);
            elm.y = Math.min(drag.start.y, p.y);
            elm.w = Math.max(40, Math.abs(p.x - drag.start.x));
            elm.h = Math.max(32, Math.abs(p.y - drag.start.y));
          }
        } else if ((drag.mode === "resize" || drag.mode === "rotate") && drag.ids.indexOf(elm.id) !== -1) {
          var base = drag.snapshot.filter(function (item) { return item.id === elm.id; })[0] || elm;
          if (isLineType(elm.type)) {
            if (drag.handle === "start") { elm.x1 = p.x; elm.y1 = p.y; }
            if (drag.handle === "end") { elm.x2 = p.x; elm.y2 = p.y; }
          } else if (drag.mode === "rotate") {
            var cx = base.x + base.w / 2;
            var cy = base.y + base.h / 2;
            elm.rotation = Math.round(Math.atan2(p.y - cy, p.x - cx) * 180 / Math.PI + 90);
          } else {
            var x2 = base.x + base.w;
            var y2 = base.y + base.h;
            if (/w/.test(drag.handle)) elm.x = p.x;
            if (/n/.test(drag.handle)) elm.y = p.y;
            if (/e/.test(drag.handle)) x2 = p.x;
            if (/s/.test(drag.handle)) y2 = p.y;
            elm.w = Math.max(24, x2 - elm.x);
            elm.h = Math.max(24, y2 - elm.y);
          }
        }
        return elm;
      });
      setElements(next, false, p);
    });

    wrap.addEventListener("pointerup", function () {
      if (!drag) return;
      var completedDrawId = drag.mode === "draw" ? drag.id : "";
      if (drag.mode === "marquee") {
        var marquee = JSON.parse(wrap.dataset.marquee || "{}");
        var ids = elements().filter(function (elm) { return rectsIntersect(elementBounds(elm), marquee); }).map(function (elm) { return elm.id; });
        wrap.dataset.marquee = "";
        setSelected(ids);
      }
      if (drag.mode === "draw") setTool("select");
      drag = null;
      refreshHistoryButtons();
      editor.changed(true);
      if (completedDrawId) {
        var item = elements().filter(function (elm) { return elm.id === completedDrawId; })[0];
        if (editableDiagramElement(item)) setTimeout(function () { openTextEditor(completedDrawId); }, 0);
      }
    });

    wrap.addEventListener("keydown", function (event) {
      if (event.target.closest("input,textarea,select,[contenteditable='true']")) return;
      if (!event.ctrlKey && !event.metaKey && !event.altKey && event.key && event.key.length === 1) {
        var selected = selectedElements();
        if (selected.length === 1 && editableDiagramElement(selected[0])) {
          event.preventDefault();
          var existingText = String(selected[0].text || "").trim();
          var typedText = existingText ? existingText + (event.key === " " ? "" : " ") + event.key : event.key;
          openTextEditor(selected[0].id, typedText);
          return;
        }
      }
      if (event.key === "Delete" || event.key === "Backspace") {
        event.preventDefault();
        applyAction("delete");
      }
      if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "z") {
        event.preventDefault();
        if (event.shiftKey) redo();
        else undo();
      }
      if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "y") {
        event.preventDefault();
        redo();
      }
      if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "c") {
        event.preventDefault();
        applyAction("copy");
      }
      if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "x") {
        event.preventDefault();
        applyAction("cut");
      }
      if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "v") {
        event.preventDefault();
        applyAction("paste");
      }
      if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "d") {
        event.preventDefault();
        applyAction("duplicate");
      }
    });

    var imageInput = wrap.querySelector(".rbe-mermaid-image-input");
    function readMermaidImageAsDataURL(file) {
      return new Promise(function (resolve, reject) {
        var reader = new FileReader();
        reader.onload = function () { resolve({ url: reader.result, name: file.name }); };
        reader.onerror = function () { reject(reader.error || new Error("Image read failed")); };
        reader.readAsDataURL(file);
      });
    }

    function uploadMermaidImage(file) {
      if (editor && typeof editor.uploadFile === "function") {
        return editor.uploadFile(file, "image").catch(function () {
          return readMermaidImageAsDataURL(file);
        });
      }
      return readMermaidImageAsDataURL(file);
    }

    imageInput.addEventListener("change", function (event) {
      var file = event.target.files && event.target.files[0];
      if (!file || !/^image\//.test(file.type || "")) return;
      uploadMermaidImage(file).then(function (uploaded) {
        if (!uploaded || !uploaded.url) return;
        pushUndo();
        var workspace = readWorkspaceFromBlock(wrap);
        var created = createElement("image", { x: workspace.width / 2 - 140, y: 80 }, { url: uploaded.url, name: uploaded.name || file.name });
        created.w = 280;
        created.h = 190;
        setElements(elements().concat([created]));
        setSelected([created.id]);
        setTool("select");
        editor.changed(true);
      }).catch(function () {
        alert("Image upload failed.");
      });
      event.target.value = "";
    });

    setTool("select");
    refreshHistoryButtons();
    updateOptions();
  }

  function mermaidChoice(tool, icon, label) {
    var iconNames = {
      "Rectangle": "square",
      "Rounded rectangle": "square",
      "Circle": "circle",
      "Oval": "circle",
      "Triangle": "play",
      "Diamond": "diamond",
      "Pentagon": "draw-polygon",
      "Hexagon": "draw-polygon",
      "Parallelogram": "draw-polygon",
      "Trapezoid": "draw-polygon",
      "Octagon": "draw-polygon",
      "Heart": "heart",
      "Cloud": "cloud",
      "Cylinder": "database",
      "Cube": "cube",
      "Right arrow": "arrow-right-long",
      "Left arrow": "arrow-left-long",
      "Up arrow": "arrow-up-long",
      "Down arrow": "arrow-down-long",
      "Bidirectional arrow": "arrows-left-right",
      "Callout": "comment",
      "Rounded callout": "comment-dots",
      "Thought bubble": "comments",
      "Plus": "plus",
      "Minus": "minus",
      "Multiply": "xmark",
      "Divide": "divide",
      "Equal": "equals",
      "Brackets": "code"
    };
    icon = /^[a-z0-9-]+$/i.test(String(icon || "")) ? icon : iconNames[label] || "square";
    return '<button type="button" class="rbe-mermaid-choice" data-tool="' + esc(tool) + '" title="' + esc(label) + '">' + svgIcon(icon, label) + "</button>";
  }

  function mermaidChoiceGrid(items) {
    return '<div class="rbe-mermaid-choice-grid">' + items.map(function (item) {
      return mermaidChoice(item[0], item[1], item[2]);
    }).join("") + "</div>";
  }

  function mermaidShapeMenuRow(label, icon, grid) {
    return '<div class="rbe-mermaid-menu-row"><span class="rbe-mermaid-menu-row-icon">' + svgIcon(icon, label) + '</span><span>' + esc(label) + '</span><div class="rbe-mermaid-submenu">' + grid + "</div></div>";
  }

  function mermaidShapeMenu() {
    var basics = mermaidChoiceGrid([
      ["rect", "square", "Rectangle"], ["roundedRect", "square", "Rounded rectangle"], ["circle", "circle", "Circle"], ["oval", "circle", "Oval"], ["triangle", "play", "Triangle"],
      ["diamond", "diamond", "Diamond"], ["pentagon", "draw-polygon", "Pentagon"], ["hexagon", "draw-polygon", "Hexagon"], ["parallelogram", "draw-polygon", "Parallelogram"], ["trapezoid", "draw-polygon", "Trapezoid"],
      ["octagon", "draw-polygon", "Octagon"], ["heart", "heart", "Heart"], ["cloud", "cloud", "Cloud"], ["cylinder", "database", "Cylinder"], ["cube", "cube", "Cube"]
    ]);
    var arrows = mermaidChoiceGrid([
      ["arrow", "arrow-right-long", "Right arrow"], ["leftArrow", "arrow-left-long", "Left arrow"], ["upArrow", "arrow-up-long", "Up arrow"], ["downArrow", "arrow-down-long", "Down arrow"], ["biArrow", "arrows-left-right", "Bidirectional arrow"]
    ]);
    var callouts = mermaidChoiceGrid([
      ["callout", "comment", "Callout"], ["roundCallout", "comment-dots", "Rounded callout"], ["thought", "comments", "Thought bubble"]
    ]);
    var equations = mermaidChoiceGrid([
      ["plus", "plus", "Plus"], ["minus", "minus", "Minus"], ["multiply", "xmark", "Multiply"], ["divide", "divide", "Divide"], ["equal", "equals", "Equal"], ["brackets", "code", "Brackets"]
    ]);
    return '<div class="rbe-mermaid-menu"><button type="button" class="rbe-mermaid-menu-btn" title="Shapes">' + svgIcon("shapes", "Shapes") + '</button><div class="rbe-mermaid-popover">' + mermaidShapeMenuRow("Shapes", "square", basics) + mermaidShapeMenuRow("Arrows", "arrow-right", arrows) + mermaidShapeMenuRow("Callouts", "comment", callouts) + mermaidShapeMenuRow("Equation", "plus", equations) + "</div></div>";
  }

  function mermaidLineChoice(tool, icon, label) {
    return '<button type="button" class="rbe-mermaid-line-choice" data-tool="' + esc(tool) + '"><span class="rbe-mermaid-menu-row-icon">' + svgIcon(icon, label) + '</span><span>' + esc(label) + "</span></button>";
  }

  function mermaidLineMenu() {
    return '<div class="rbe-mermaid-menu rbe-mermaid-line-menu"><button type="button" class="rbe-mermaid-menu-btn" title="Line options">' + svgIcon("caret-down", "Line options") + '</button><div class="rbe-mermaid-popover">' +
      mermaidLineChoice("line", "minus", "Line") +
      mermaidLineChoice("arrow", "arrow-right-long", "Arrow") +
      mermaidLineChoice("elbow", "share-nodes", "Elbow Connector") +
      mermaidLineChoice("curved", "code-branch", "Curved Connector") +
      mermaidLineChoice("curve", "bezier-curve", "Curve") +
      mermaidLineChoice("polyline", "draw-polygon", "Polyline") +
      mermaidLineChoice("scribble", "signature", "Scribble") +
      "</div></div>";
  }

  function mermaidShapeStylebar() {
    return '<div class="rbe-mermaid-stylebar" hidden aria-label="Shape style controls">' +
      '<input class="rbe-mermaid-option-text rbe-mermaid-hidden-control" type="text" aria-hidden="true" tabindex="-1">' +
      '<input class="rbe-mermaid-option-opacity rbe-mermaid-hidden-control" type="hidden" value="1">' +
      '<label class="rbe-mermaid-icon-control rbe-mermaid-fill-control" title="Fill color">' + svgIcon("fill-drip", "Fill color") + '<span class="rbe-mermaid-control-swatch rbe-mermaid-fill-swatch"></span><input class="rbe-mermaid-option-fill" type="color" value="#eef5ff" aria-label="Fill color"></label>' +
      '<label class="rbe-mermaid-icon-control" title="Border color">' + svgIcon("pen-nib", "Border color") + '<span class="rbe-mermaid-control-swatch rbe-mermaid-stroke-swatch"></span><input class="rbe-mermaid-option-stroke" type="color" value="#111827" aria-label="Border color"></label>' +
      '<label class="rbe-mermaid-icon-control" title="Border weight">' + svgIcon("grip-lines", "Border weight") + '<select class="rbe-mermaid-option-weight" aria-label="Border weight"><option value="1">1px</option><option value="2">2px</option><option value="3">3px</option><option value="4">4px</option><option value="8">8px</option><option value="12">12px</option><option value="16">16px</option><option value="24">24px</option></select></label>' +
      '<label class="rbe-mermaid-icon-control" title="Border dash">' + svgIcon("grip-lines-vertical", "Border dash") + '<select class="rbe-mermaid-option-dash" aria-label="Border dash"><option value="solid">Solid</option><option value="dotted">Dotted</option><option value="dashed">Dashed</option><option value="longDash">Long dash</option><option value="dashDot">Dash dot</option></select></label>' +
      '<span class="rbe-mermaid-toolbar-sep rbe-mermaid-text-control"></span>' +
      '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Font family">' + svgIcon("font", "Font family") + '<select class="rbe-mermaid-option-font" aria-label="Font family"><option value="Arial">Arial</option><option value="Verdana">Verdana</option><option value="Tahoma">Tahoma</option><option value="Georgia">Georgia</option><option value="Times New Roman">Times New Roman</option><option value="Courier New">Courier New</option></select></label>' +
      '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-style-action="font-size-down" title="Decrease font size">' + svgIcon("minus", "Decrease font size") + '</button>' +
      '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Font size">' + svgIcon("text-height", "Font size") + '<select class="rbe-mermaid-option-size" aria-label="Font size"><option value="10">10</option><option value="12">12</option><option value="14">14</option><option value="16">16</option><option value="18">18</option><option value="20">20</option><option value="24">24</option><option value="28">28</option><option value="32">32</option><option value="40">40</option><option value="48">48</option><option value="64">64</option><option value="72">72</option></select></label>' +
      '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-style-action="font-size-up" title="Increase font size">' + svgIcon("plus", "Increase font size") + '</button>' +
      '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-format-toggle="bold" title="Bold">' + svgIcon("bold", "Bold") + '</button>' +
      '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-format-toggle="italic" title="Italic">' + svgIcon("italic", "Italic") + '</button>' +
      '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-format-toggle="underline" title="Underline">' + svgIcon("underline", "Underline") + '</button>' +
      '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Text color">' + svgIcon("font", "Text color") + '<span class="rbe-mermaid-control-swatch rbe-mermaid-text-swatch"></span><input class="rbe-mermaid-option-text-color" type="color" value="#172033" aria-label="Text color"></label>' +
      '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Highlight color">' + svgIcon("highlighter", "Highlight color") + '<span class="rbe-mermaid-control-swatch rbe-mermaid-highlight-swatch"></span><input class="rbe-mermaid-option-highlight" type="color" value="#fff3b0" aria-label="Highlight color"></label>' +
      '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-text-action="reset-highlight" title="Reset highlight">' + svgIcon("rotate-left", "Reset highlight") + '</button>' +
      '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Text alignment">' + svgIcon("align-center", "Text alignment") + '<select class="rbe-mermaid-option-align" aria-label="Text alignment"><option value="left">Left</option><option value="center">Center</option><option value="right">Right</option><option value="justify">Justify</option></select></label>' +
      '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Line spacing">' + svgIcon("arrows-up-down", "Line spacing") + '<select class="rbe-mermaid-option-line-height" aria-label="Line spacing"><option value="1">Single</option><option value="1.15">1.15</option><option value="1.5">1.5</option><option value="2">Double</option></select></label>' +
      '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control rbe-mermaid-bullet-control" title="Bulleted list">' + svgIcon("list-ul", "Bulleted list") + '<select class="rbe-mermaid-option-bullet" aria-label="Bulleted list"><option value="none">None</option><option value="disc">Disc</option><option value="circle">Circle</option><option value="square">Square</option></select></label>' +
      '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control rbe-mermaid-number-control" title="Numbered list">' + svgIcon("list-ol", "Numbered list") + '<select class="rbe-mermaid-option-number" aria-label="Numbered list"><option value="none">None</option><option value="decimal">1, 2, 3</option><option value="lower-alpha">a, b, c</option><option value="upper-alpha">A, B, C</option><option value="lower-roman">i, ii, iii</option><option value="upper-roman">I, II, III</option></select></label>' +
      '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-text-action="outdent" title="Decrease indent">' + svgIcon("outdent", "Decrease indent") + '</button>' +
      '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-text-action="indent" title="Increase indent">' + svgIcon("indent", "Increase indent") + '</button>' +
      '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control rbe-mermaid-spacing-before" data-text-action="spacing-before" title="Add space before paragraph">' + svgIcon("arrow-up", "Add space before paragraph") + '</button>' +
      '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control rbe-mermaid-spacing-after" data-text-action="spacing-after" title="Add space after paragraph">' + svgIcon("arrow-down", "Add space after paragraph") + '</button>' +
      "</div>";
  }

  function mermaidDesignerToolbar() {
    return '<div class="rbe-mermaid-toolbar" role="toolbar" aria-label="Drawing tools">' +
      '<button type="button" class="rbe-mermaid-icon-btn" data-diagram-action="undo" title="Undo">' + svgIcon("rotate-left", "Undo") + '</button>' +
      '<button type="button" class="rbe-mermaid-icon-btn" data-diagram-action="redo" title="Redo">' + svgIcon("rotate-right", "Redo") + '</button>' +
      '<span class="rbe-mermaid-toolbar-sep"></span>' +
      '<button type="button" class="rbe-mermaid-tool" data-tool="select" title="Select">' + svgIcon("arrow-pointer", "Select") + '</button>' +
      mermaidShapeMenu() +
      '<button type="button" class="rbe-mermaid-tool" data-tool="arrow" title="Arrow">' + svgIcon("arrow-right-long", "Arrow") + '</button>' +
      mermaidLineMenu() +
      '<button type="button" class="rbe-mermaid-tool" data-tool="text" title="Text box">' + svgIcon("font", "Text box") + '</button>' +
      '<button type="button" class="rbe-mermaid-icon-btn" data-diagram-action="image" title="Insert image">' + svgIcon("image", "Insert image") + '</button>' +
      mermaidShapeStylebar() +
      "</div>";
  }

  function refreshMermaidCardPreview(card) {
    var target = card.querySelector(".rbe-mermaid-card-preview");
    if (!target) return;
    var elements = [];
    try {
      elements = JSON.parse((card.querySelector(".rbe-mermaid-diagram-json") || {}).value || "[]").map(normalizeDiagramElement);
    } catch (error) {}
    if (!elements.length) {
      target.innerHTML = '<span class="rbe-mermaid-card-empty">Open the designer to draw a diagram.</span>';
      return;
    }
    target.innerHTML = '<div class="rbe-output-mermaid-diagram">' + diagramSvg(elements, "", readWorkspaceFromBlock(card), null) + "</div>";
  }

  function mermaidRulerMarkup() {
    var top = "";
    var side = "";
    for (var i = 1; i <= 10; i += 1) top += '<span style="left:' + (i * 100) + 'px">' + i + "</span>";
    for (var j = 1; j <= 7; j += 1) side += '<span style="top:' + (j * 100) + 'px">' + j + "</span>";
    return '<div class="rbe-mermaid-stage-shell"><div class="rbe-mermaid-ruler-corner"></div><div class="rbe-mermaid-top-ruler">' + top + '</div><div class="rbe-mermaid-side-ruler">' + side + '</div><div class="rbe-mermaid-stage"></div></div>';
  }

  function openMermaidDesignerModal(editor, card) {
    var existing = document.querySelector(".rbe-mermaid-modal");
    if (existing) {
      existing.remove();
      document.body.classList.remove("rbe-mermaid-modal-open");
    }
    var modal = document.createElement("div");
    modal.className = "rbe-mermaid-modal";
    modal.innerHTML =
      '<div class="rbe-mermaid-dialog" role="dialog" aria-modal="true" aria-label="Drawing">' +
      '<div class="rbe-mermaid-modal-head"><h2 class="rbe-mermaid-modal-title">Drawing</h2><button type="button" class="rbe-mermaid-close" data-modal-action="close" title="Close">' + svgIcon("xmark", "Close") + '</button></div>' +
      '<div class="rbe-mermaid-modal-body"><div class="rbe-mermaid-block" tabindex="0">' +
      '<input type="hidden" class="rbe-mermaid-alignment"><input type="hidden" class="rbe-mermaid-diagram-json"><input class="rbe-mermaid-image-input" type="file" accept="image/*" hidden><textarea class="rbe-mermaid-code" hidden></textarea>' +
      mermaidDesignerToolbar() +
      '<input class="rbe-mermaid-workspace-w" type="hidden"><input class="rbe-mermaid-workspace-h" type="hidden"><input class="rbe-mermaid-zoom" type="hidden">' + mermaidRulerMarkup() +
      '</div></div>' +
      '<div class="rbe-mermaid-modal-foot"><button type="button" class="rbe-mermaid-modal-cancel" data-modal-action="close">Cancel</button><button type="button" class="rbe-mermaid-modal-save" data-modal-action="save">Save drawing</button></div>' +
      "</div>";
    function closeModal() {
      document.body.classList.remove("rbe-mermaid-modal-open");
      modal.remove();
    }
    document.body.classList.add("rbe-mermaid-modal-open");
    document.body.appendChild(modal);
    var designer = modal.querySelector(".rbe-mermaid-block");
    [".rbe-mermaid-alignment", ".rbe-mermaid-diagram-json", ".rbe-mermaid-code", ".rbe-mermaid-workspace-w", ".rbe-mermaid-workspace-h", ".rbe-mermaid-zoom"].forEach(function (selector) {
      var source = card.querySelector(selector);
      var target = designer.querySelector(selector);
      if (source && target) target.value = source.value || "";
    });
    renderDiagramCanvas(designer);
    bindMermaidDesigner(editor, designer);
    designer.focus();
    modal.addEventListener("click", function (event) {
      var actionButton = event.target.closest("[data-modal-action]");
      var action = actionButton ? actionButton.dataset.modalAction : "";
      if (!action) return;
      event.preventDefault();
      if (action === "save") {
        var textEditor = designer.querySelector(".rbe-mermaid-text-editor");
        if (textEditor && textEditor.dataset.editId) {
          var editId = textEditor.dataset.editId;
          var editedText = textEditor.value || textEditor.textContent || "";
          var current = [];
          try { current = JSON.parse((designer.querySelector(".rbe-mermaid-diagram-json") || {}).value || "[]"); } catch (error) {}
          designer.querySelector(".rbe-mermaid-diagram-json").value = JSON.stringify(current.map(function (elm) {
            elm = normalizeDiagramElement(elm);
            if (elm.id === editId) elm.text = editedText.trim();
            return elm;
          }));
          textEditor.remove();
        }
        [".rbe-mermaid-alignment", ".rbe-mermaid-diagram-json", ".rbe-mermaid-code", ".rbe-mermaid-workspace-w", ".rbe-mermaid-workspace-h", ".rbe-mermaid-zoom"].forEach(function (selector) {
          var source = designer.querySelector(selector);
          var target = card.querySelector(selector);
          if (source && target) target.value = source.value || "";
        });
        refreshMermaidCardPreview(card);
        editor.changed(true);
      }
      closeModal();
    });
  }

  function hydrateAdvancedOutput(root) {
    root = root || document;
    Array.prototype.slice.call(root.querySelectorAll(".rbe-output-latex")).forEach(function (node) {
      var code = decodeHTML(node.dataset.latex || node.textContent || "");
      var normalized = normalizeLatexSource(code, node.dataset.display !== "false");
      var display = normalized.display;
      if (node.dataset.renderedLatex === "true" && node.dataset.lastLatex === code && node.dataset.lastDisplay === String(display)) return;
      node.dataset.renderedLatex = "true";
      node.dataset.lastLatex = code;
      node.dataset.lastDisplay = String(display);
      renderLatexPreview(node, code, display);
    });
    Array.prototype.slice.call(root.querySelectorAll(".rbe-output-mermaid")).forEach(function (node) {
      var code = node.dataset.mermaid || node.textContent || "";
      if (node.dataset.renderedMermaid === "true" && node.dataset.lastMermaid === code) return;
      node.dataset.renderedMermaid = "true";
      node.dataset.lastMermaid = code;
      renderMermaidPreview(node, code);
    });
  }

  function bootOutputHydration() {
    var run = function () { hydrateAdvancedOutput(document); };
    if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", run, { once: true });
    else setTimeout(run, 0);
    if (!document.body) return;
    var timer = 0;
    new MutationObserver(function () {
      clearTimeout(timer);
      timer = setTimeout(run, 60);
    }).observe(document.body, { childList: true, subtree: true });
  }

  function bindChange(editor, block, update) {
    block.addEventListener("input", function () {
      update();
      editor.changed();
    });
    block.addEventListener("change", function () {
      update();
      editor.changed();
    });
  }

  function field(className, value) {
    return "." + className + " => " + value;
  }

  function chartCaptionAlignment(value) {
    value = String(value || "").toLowerCase();
    return value === "left" || value === "right" ? value : "center";
  }

  function plainTextFromHTML(value) {
    var template = document.createElement("template");
    template.innerHTML = sanitize(value || "");
    return template.content.textContent || "";
  }

  BlockWriteAI.registerTool("chart", {
    label: "Chart",
    hint: "Bar, line, pie, doughnut, area, scatter, stacked, comparison, trend, or KPI chart",
    icon: "chart-column",
    defaultData: { type: "bar", title: "Chart", caption: "", labels: "Q1,Q2,Q3,Q4", values: "12,19,7,14", color: "#2563eb", colors: "#2563eb,#16a34a,#f59e0b,#dc2626", zoom: 1 },
    render: function (body, data) {
      injectStyles();
      var editor = this;
      var wrap = el("div", "rbe-plugin-card rbe-chart-block");
      wrap.innerHTML =
        '<input class="rbe-chart-type" type="hidden"><input class="rbe-chart-zoom" type="hidden"><input class="rbe-chart-series-labels" type="hidden">' +
        '<input class="rbe-chart-x-axis-label" type="hidden"><input class="rbe-chart-y-axis-label" type="hidden">' +
        '<div class="rbe-plugin-toolbar"><input class="rbe-chart-title" placeholder="Chart title"><label class="rbe-chart-series-color">Color <input class="rbe-chart-color" type="color"></label><div class="rbe-chart-slice-colors" hidden><span class="rbe-chart-slice-label">Slice colors</span><div class="rbe-chart-slice-list"></div><input class="rbe-chart-colors" type="hidden"></div></div>' +
        '<div class="rbe-plugin-toolbar"><input class="rbe-chart-labels" placeholder="Labels: Q1,Q2,Q3"><input class="rbe-chart-values" placeholder="Values: 10,20,30"></div>' +
        '<div class="rbe-chart-preview-wrap">' + chartZoomControls(data.zoom) + '<div class="rbe-chart-type-bar" role="toolbar" aria-label="Chart type">' + chartTypeButtons() + '</div><div class="rbe-plugin-preview rbe-chart-preview"></div></div>' +
        '<div class="rbe-chart-caption rbe-editable" contenteditable="' + (!editor.readOnly) + '" data-placeholder="Chart caption"></div>';
      body.appendChild(wrap);
      wrap.querySelector(".rbe-chart-type").value = normalizeChartType(data.type);
      wrap.querySelector(".rbe-chart-zoom").value = normalizeChartZoom(data.zoom);
      wrap.querySelector(".rbe-chart-title").value = data.title || "";
      wrap.querySelector(".rbe-chart-caption").innerHTML = sanitize(data.caption || "");
      wrap.querySelector(".rbe-chart-caption").style.textAlign = chartCaptionAlignment(data.captionAlignment || "center");
      wrap.querySelector(".rbe-chart-color").value = data.color || "#2563eb";
      wrap.querySelector(".rbe-chart-colors").value = data.colors || "#2563eb,#16a34a,#f59e0b,#dc2626";
      wrap.querySelector(".rbe-chart-series-labels").value = data.seriesLabels || "";
      wrap.querySelector(".rbe-chart-x-axis-label").value = data.xAxisLabel || "";
      wrap.querySelector(".rbe-chart-y-axis-label").value = data.yAxisLabel || "";
      wrap.querySelector(".rbe-chart-labels").value = data.labels || "";
      wrap.querySelector(".rbe-chart-values").value = data.values || "";
      function syncSliceInputs() {
        var type = normalizeChartType(wrap.querySelector(".rbe-chart-type").value);
        var labels = labelList(wrap.querySelector(".rbe-chart-labels").value);
        var values = numberList(wrap.querySelector(".rbe-chart-values").value);
        var multiLine = (type === "line" || type === "area" || type === "trend") && wrap.querySelector(".rbe-chart-values").value.indexOf(";") !== -1;
        var series = type === "stackedBar" || type === "comparison" || multiLine ? chartSeriesList(wrap.querySelector(".rbe-chart-values").value, labels.length ? labels : ["A", "B", "C"]) : [];
        var seriesLabels = chartSeriesLabels(wrap.querySelector(".rbe-chart-series-labels").value, series.length);
        var singleSeries = series.length <= 1;
        var perLabelPalette = type === "bar" || type === "pie" || type === "doughnut" || type === "kpi" || ((type === "stackedBar" || type === "comparison") && singleSeries);
        var count = type === "stackedBar" || type === "comparison" || multiLine ? (singleSeries ? Math.max(1, labels.length, values.length) : series.length) : Math.max(1, labels.length, values.length);
        var hidden = wrap.querySelector(".rbe-chart-colors");
        var colors = chartColorList(hidden.value, count);
        var list = wrap.querySelector(".rbe-chart-slice-list");
        list.innerHTML = colors.map(function (item, index) {
          var label = perLabelPalette ? (labels[index] || index + 1) : seriesLabels[index];
          return '<label class="rbe-chart-slice-chip"><span>' + esc(label) + '</span><input type="color" value="' + esc(item) + '" data-slice-index="' + index + '"></label>';
        }).join("");
        hidden.value = colors.join(",");
      }
      function refreshTypeState() {
        var type = normalizeChartType(wrap.querySelector(".rbe-chart-type").value);
        var multiLine = (type === "line" || type === "area" || type === "trend") && wrap.querySelector(".rbe-chart-values").value.indexOf(";") !== -1;
        var usesPalette = type === "bar" || type === "pie" || type === "doughnut" || type === "kpi" || type === "stackedBar" || type === "comparison" || multiLine;
        Array.prototype.slice.call(wrap.querySelectorAll(".rbe-chart-type-btn")).forEach(function (button) {
          button.classList.toggle("is-active", button.dataset.chartType === type);
        });
        wrap.querySelector(".rbe-chart-series-color").hidden = usesPalette;
        wrap.querySelector(".rbe-chart-slice-colors").hidden = !usesPalette;
        wrap.querySelector(".rbe-chart-labels").placeholder = type === "scatter" ? "X values: 10,20,30" : "Labels: Q1,Q2,Q3";
        wrap.querySelector(".rbe-chart-values").placeholder = type === "stackedBar" || type === "comparison" || multiLine ? "Values: 10,20,30; 8,16,24" : "Values: 10,20,30";
        if (type === "scatter") wrap.querySelector(".rbe-chart-values").placeholder = "Y values: 12,19,7";
        if (usesPalette) syncSliceInputs();
      }
      var update = function () {
        refreshTypeState();
        var zoom = normalizeChartZoom(wrap.querySelector(".rbe-chart-zoom").value);
        wrap.querySelector(".rbe-chart-zoom").value = zoom;
        var zoomValue = wrap.querySelector(".rbe-chart-zoom-value");
        if (zoomValue) zoomValue.textContent = Math.round(zoom * 100) + "%";
        wrap.querySelector(".rbe-chart-preview").innerHTML = chartSvg({
          type: wrap.querySelector(".rbe-chart-type").value,
          title: wrap.querySelector(".rbe-chart-title").value,
          color: wrap.querySelector(".rbe-chart-color").value,
          colors: wrap.querySelector(".rbe-chart-colors").value,
          labels: wrap.querySelector(".rbe-chart-labels").value,
          values: wrap.querySelector(".rbe-chart-values").value,
          seriesLabels: wrap.querySelector(".rbe-chart-series-labels").value,
          xAxisLabel: wrap.querySelector(".rbe-chart-x-axis-label").value,
          yAxisLabel: wrap.querySelector(".rbe-chart-y-axis-label").value,
          caption: plainTextFromHTML(wrap.querySelector(".rbe-chart-caption").innerHTML || ""),
          zoom: zoom
        });
      };
      wrap.addEventListener("click", function (event) {
        var zoomButton = event.target.closest(".rbe-chart-zoom-btn");
        if (zoomButton) {
          event.preventDefault();
          var currentZoom = normalizeChartZoom(wrap.querySelector(".rbe-chart-zoom").value);
          var zoomInput = wrap.querySelector(".rbe-chart-zoom");
          zoomInput.value = normalizeChartZoom(currentZoom + (zoomButton.dataset.chartZoomAction === "in" ? 0.1 : -0.1));
          update();
          zoomInput.dispatchEvent(new Event("input", { bubbles: true }));
          zoomInput.dispatchEvent(new Event("change", { bubbles: true }));
          editor.changed(true);
          return;
        }
        var button = event.target.closest(".rbe-chart-type-btn");
        if (!button) return;
        event.preventDefault();
        wrap.querySelector(".rbe-chart-type").value = normalizeChartType(button.dataset.chartType);
        update();
        editor.changed(true);
      });
      wrap.addEventListener("input", function (event) {
        var color = event.target.closest(".rbe-chart-slice-chip input");
        if (!color) return;
        var inputs = Array.prototype.slice.call(wrap.querySelectorAll(".rbe-chart-slice-chip input"));
        wrap.querySelector(".rbe-chart-colors").value = inputs.map(function (input) { return input.value; }).join(",");
      });
      bindChange(editor, wrap, update);
      update();
    },
    serialize: function (block) {
      var caption = block.querySelector(".rbe-chart-caption");
      return {
        type: (block.querySelector(".rbe-chart-type") || {}).value || "bar",
        title: (block.querySelector(".rbe-chart-title") || {}).value || "",
        caption: caption ? sanitize(caption.innerHTML || "") : "",
        captionAlignment: chartCaptionAlignment(caption && caption.style ? caption.style.textAlign : "center"),
        color: (block.querySelector(".rbe-chart-color") || {}).value || "#2563eb",
        colors: (block.querySelector(".rbe-chart-colors") || {}).value || "",
        seriesLabels: (block.querySelector(".rbe-chart-series-labels") || {}).value || "",
        xAxisLabel: (block.querySelector(".rbe-chart-x-axis-label") || {}).value || "",
        yAxisLabel: (block.querySelector(".rbe-chart-y-axis-label") || {}).value || "",
        labels: (block.querySelector(".rbe-chart-labels") || {}).value || "",
        values: (block.querySelector(".rbe-chart-values") || {}).value || "",
        zoom: normalizeChartZoom((block.querySelector(".rbe-chart-zoom") || {}).value)
      };
    },
    toHTML: function (data) {
      var captionAlignment = chartCaptionAlignment(data.captionAlignment || "center");
      var zoom = normalizeChartZoom(data.zoom);
      return '<figure class="rbe-output-chart" data-chart-zoom="' + zoom + '">' + (data.title ? '<figcaption class="rbe-output-chart-title">' + esc(data.title) + "</figcaption>" : "") + chartSvg(Object.assign({}, data, { zoom: zoom })) + (data.caption ? '<figcaption class="rbe-output-chart-caption" style="text-align:' + captionAlignment + '">' + inline(data.caption) + "</figcaption>" : "") + "</figure>";
    },
    toMarkdown: function (data) {
      return "Chart: " + (data.title || "") + (data.caption ? "\n" + plainTextFromHTML(data.caption) : "") + "\nLabels: " + (data.labels || "") + "\nValues: " + (data.values || "");
    }
  });


  BlockWriteAI.registerTool("latex", {
    label: "LaTeX",
    hint: "Math formula block",
    icon: "square-root-variable",
    defaultData: { code: "\\int_a^b f(x) \\, dx", display: true },
    render: function (body, data) {
      injectStyles();
      var editor = this;
      var wrap = el("div", "rbe-plugin-card rbe-latex-block");
      wrap.innerHTML = '<textarea class="rbe-latex-code" spellcheck="false"></textarea><div class="rbe-plugin-preview rbe-latex-preview"></div>';
      body.appendChild(wrap);
      wrap.querySelector(".rbe-latex-code").value = data.code || "";
      var update = function () { renderLatexPreview(wrap.querySelector(".rbe-latex-preview"), wrap.querySelector(".rbe-latex-code").value, data.display !== false); };
      bindChange(editor, wrap, update);
      update();
    },
    serialize: function (block) {
      return { code: (block.querySelector(".rbe-latex-code") || {}).value || "", display: true };
    },
    toHTML: function (data) {
      var code = data.code || "";
      return '<div class="rbe-output-latex" data-display="true" data-latex="' + esc(code) + '">' + latexFallbackHTML(code) + "</div>";
    },
    toMarkdown: function (data) {
      return "$$" + (data.code || "") + "$$";
    }
  });

  BlockWriteAI.registerTool("audio", {
    label: "Audio",
    hint: "Upload or link audio",
    icon: "volume-high",
    defaultData: { url: "", title: "" },
    render: function (body, data) {
      injectStyles();
      var editor = this;
      var wrap = el("div", "rbe-plugin-card rbe-audio-block");
      wrap.innerHTML =
        '<input class="rbe-audio-title" type="hidden"><input class="rbe-audio-url" type="hidden">' +
        '<label class="rbe-upload-dropzone rbe-audio-dropzone"><input class="rbe-audio-file rbe-upload-input" type="file" accept="audio/*" ' + (editor.readOnly ? "disabled" : "") + '><span class="rbe-upload-icon">' + svgIcon("cloud-arrow-up", "Upload audio") + '</span><strong>Drag and drop or click here</strong><span class="rbe-upload-main">to upload audio</span><small>MP3, WAV, OGG, M4A, AAC, and audio files only</small></label>' +
        '<div class="rbe-audio-preview"></div>';
      body.appendChild(wrap);
      wrap.querySelector(".rbe-audio-title").value = data.title || "";
      wrap.querySelector(".rbe-audio-url").value = data.url || "";
      var update = function () {
        var url = wrap.querySelector(".rbe-audio-url").value;
        var title = wrap.querySelector(".rbe-audio-title").value || "Audio";
        wrap.querySelector(".rbe-audio-preview").innerHTML = url ? '<div class="rbe-audio-card"><strong>' + esc(title) + '</strong><audio controls src="' + esc(url) + '"></audio><small>Drop or click above to replace audio</small></div>' : "";
      };
      function useFile(file) {
        var isAudio = !!file && (/^audio\//.test(file.type || "") || /\.(mp3|wav|ogg|m4a|aac|flac|webm)$/i.test(file.name || ""));
        if (!isAudio) {
          wrap.querySelector(".rbe-audio-preview").innerHTML = '<div class="rbe-audio-error">Please upload an audio file.</div>';
          return;
        }
        editor.uploadFile(file, "file").then(function (result) {
          wrap.querySelector(".rbe-audio-url").value = result.url || "";
          wrap.querySelector(".rbe-audio-title").value = result.name || file.name || "Audio";
          update();
          editor.changed(true);
        }).catch(function (error) {
          wrap.querySelector(".rbe-audio-preview").innerHTML = '<div class="rbe-audio-error">' + esc(error && error.message ? error.message : "Audio upload failed") + "</div>";
        });
      }
      wrap.querySelector(".rbe-audio-file").addEventListener("change", function (event) {
        var file = event.target.files && event.target.files[0];
        useFile(file);
      });
      var audioDropzone = wrap.querySelector(".rbe-audio-dropzone");
      audioDropzone.addEventListener("dragenter", function (event) {
        event.preventDefault();
        event.stopPropagation();
        audioDropzone.classList.add("is-dragging");
      }, true);
      audioDropzone.addEventListener("dragover", function (event) {
        event.preventDefault();
        event.stopPropagation();
        if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
        audioDropzone.classList.add("is-dragging");
      }, true);
      audioDropzone.addEventListener("dragleave", function (event) {
        event.stopPropagation();
        audioDropzone.classList.remove("is-dragging");
      }, true);
      audioDropzone.addEventListener("drop", function (event) {
        event.preventDefault();
        event.stopPropagation();
        audioDropzone.classList.remove("is-dragging");
        useFile(event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0]);
      }, true);
      bindChange(editor, wrap, update);
      update();
    },
    serialize: function (block) {
      return { title: (block.querySelector(".rbe-audio-title") || {}).value || "", url: (block.querySelector(".rbe-audio-url") || {}).value || "" };
    },
    toHTML: function (data) {
      return '<figure class="rbe-output-audio">' + (data.url ? '<audio controls src="' + esc(data.url) + '"></audio>' : "") + "</figure>";
    },
    toMarkdown: function (data) {
      return "[" + (data.title || "Audio") + "](" + (data.url || "#") + ")";
    }
  });


  BlockWriteAI.registerTool("columns", {
    label: "Columns",
    hint: "Multi-column layout",
    icon: "table-columns",
    defaultData: { count: 2, headers: ["Column 1", "Column 2"], columns: ["", ""] },
    render: function (body, data) {
      injectStyles();
      var editor = this;
      var maxColumns = 8;
      var count = Math.min(maxColumns, Math.max(2, Number(data.count || 2)));
      var headers = Array.isArray(data.headers) ? data.headers : [];
      var columns = Array.isArray(data.columns) ? data.columns : [];
      var headerAlignments = Array.isArray(data.headerAlignments) ? data.headerAlignments : [];
      var columnAlignments = Array.isArray(data.columnAlignments) ? data.columnAlignments : [];
      var wrap = el("div", "rbe-plugin-card rbe-columns-block");
      wrap.innerHTML = '<div class="rbe-plugin-toolbar"><label>Columns <select class="rbe-columns-count"></select></label></div><div class="rbe-columns-grid"></div>';
      body.appendChild(wrap);
      var select = wrap.querySelector(".rbe-columns-count");
      var grid = wrap.querySelector(".rbe-columns-grid");
      for (var optionCount = 2; optionCount <= maxColumns; optionCount += 1) {
        select.appendChild(el("option", "", { value: String(optionCount), text: String(optionCount) }));
      }
      select.value = String(count);
      function cleanAlign(value) {
        value = String(value || "").toLowerCase();
        return value === "center" || value === "right" ? value : "left";
      }
      function renderColumns() {
        var nextCount = Number(select.value || 2);
        var currentHeaders = Array.prototype.slice.call(grid.querySelectorAll(".rbe-column-header")).map(function (item) { return item.innerHTML; });
        var current = Array.prototype.slice.call(grid.querySelectorAll(".rbe-column-editor")).map(function (item) { return item.innerHTML; });
        var currentHeaderAlignments = Array.prototype.slice.call(grid.querySelectorAll(".rbe-column-header")).map(function (item) { return cleanAlign(item.style.textAlign); });
        var currentColumnAlignments = Array.prototype.slice.call(grid.querySelectorAll(".rbe-column-editor")).map(function (item) { return cleanAlign(item.style.textAlign); });
        grid.innerHTML = "";
        grid.style.gridTemplateColumns = "repeat(" + nextCount + ", minmax(220px, 1fr))";
        for (var i = 0; i < nextCount; i += 1) {
          var column = el("div", "rbe-column-field");
          var header = el("div", "rbe-column-header rbe-editable", {
            contentEditable: !editor.readOnly,
            html: sanitize(currentHeaders[i] || headers[i] || "Column " + (i + 1)),
            dataset: { placeholder: "Column " + (i + 1) + " header" }
          });
          header.style.textAlign = cleanAlign(currentHeaderAlignments[i] || headerAlignments[i]);
          column.appendChild(header);
          var field = el("div", "rbe-column-editor rbe-editable", {
            contentEditable: !editor.readOnly,
            html: sanitize(current[i] || columns[i] || ""),
            dataset: { placeholder: "Column " + (i + 1) }
          });
          field.style.textAlign = cleanAlign(currentColumnAlignments[i] || columnAlignments[i]);
          column.appendChild(field);
          grid.appendChild(column);
        }
      }
      select.addEventListener("change", function () {
        renderColumns();
        editor.changed(true);
      });
      grid.addEventListener("input", function () { editor.changed(); });
      renderColumns();
    },
    serialize: function (block) {
      function cleanAlign(value) {
        value = String(value || "").toLowerCase();
        return value === "center" || value === "right" ? value : "left";
      }
      return {
        count: Number((block.querySelector(".rbe-columns-count") || {}).value || 2),
        headers: Array.prototype.slice.call(block.querySelectorAll(".rbe-column-header")).map(function (item) { return sanitize(item.innerHTML || ""); }),
        columns: Array.prototype.slice.call(block.querySelectorAll(".rbe-column-editor")).map(function (item) { return sanitize(item.innerHTML || ""); }),
        headerAlignments: Array.prototype.slice.call(block.querySelectorAll(".rbe-column-header")).map(function (item) { return cleanAlign(item.style.textAlign); }),
        columnAlignments: Array.prototype.slice.call(block.querySelectorAll(".rbe-column-editor")).map(function (item) { return cleanAlign(item.style.textAlign); })
      };
    },
    toHTML: function (data) {
      function cleanAlign(value) {
        value = String(value || "").toLowerCase();
        return value === "center" || value === "right" ? value : "left";
      }
      var count = Math.min(8, Math.max(2, Number(data.count || 2)));
      var headers = Array.isArray(data.headers) ? data.headers : [];
      var columns = Array.isArray(data.columns) ? data.columns.slice(0, count) : [];
      var headerAlignments = Array.isArray(data.headerAlignments) ? data.headerAlignments : [];
      var columnAlignments = Array.isArray(data.columnAlignments) ? data.columnAlignments : [];
      while (columns.length < count) columns.push("");
      return '<div class="rbe-output-columns" style="--rbe-columns:' + count + '">' + columns.map(function (column, index) {
        var header = headers[index] || "Column " + (index + 1);
        var headerAlign = cleanAlign(headerAlignments[index]);
        var columnAlign = cleanAlign(columnAlignments[index]);
        return '<div class="rbe-output-column"><div class="rbe-output-column-header" style="text-align:' + headerAlign + '">' + inline(header) + '</div><div class="rbe-output-column-body" style="text-align:' + columnAlign + '">' + inline(column || "") + "</div></div>";
      }).join("") + "</div>";
    },
    toMarkdown: function (data, helpers) {
      return (data.columns || []).map(function (column, index) {
        var headers = Array.isArray(data.headers) ? data.headers : [];
        return (headers[index] || "Column " + (index + 1)) + "\n" + (helpers.text ? helpers.text(column) : column);
      }).join("\n\n");
    }
  });

  function applyImageTransform(stage, editor) {
    if (!stage) return;
    var image = stage.querySelector(".rbe-image-preview");
    var rotation = Number(stage.dataset.rotation || 0);
    stage.dataset.rotation = String(rotation);
    if (editor && typeof editor.applyImageStageState === "function") editor.applyImageStageState(stage);
    else if (image) image.style.transform = "rotate(" + rotation + "deg)";
  }

  function enhanceImageStages(editor, body, data) {
    var items = Array.isArray(data && data.items) ? data.items : data && data.url ? [data] : [];
    Array.prototype.slice.call(body.querySelectorAll(".rbe-image-stage")).forEach(function (stage, index) {
      var item = items[index] || {};
      stage.dataset.rotation = item.rotation || stage.dataset.rotation || "0";
      stage.dataset.crop = item.crop === "custom" ? "custom" : stage.dataset.crop || "free";
      applyImageTransform(stage, editor);
      var menu = stage.querySelector(".rbe-media-gear-menu");
      if (!menu || stage.querySelector(":scope > .rbe-image-transform-panel")) return;
      var panel = document.createElement("div");
      panel.className = "rbe-image-transform-panel";
      panel.innerHTML =
        '<button type="button" class="rbe-media-action rbe-image-transform" data-transform="rotate-left" title="Rotate left">' + svgIcon("rotate-left", "Rotate left") + '</button>' +
        '<button type="button" class="rbe-media-action rbe-image-transform" data-transform="rotate-right" title="Rotate right">' + svgIcon("rotate-right", "Rotate right") + '</button>';
      stage.appendChild(panel);
    });
  }

  var originalRenderImage = BlockWriteAI.prototype.renderImage;
  BlockWriteAI.prototype.renderImage = function (body, data) {
    originalRenderImage.call(this, body, data || {});
    enhanceImageStages(this, body, data || {});
  };

  var originalSerializeBlock = BlockWriteAI.prototype.serializeBlock;
  BlockWriteAI.prototype.serializeBlock = function (block) {
    var result = originalSerializeBlock.call(this, block);
    if (result && result.type === "image") {
      var stages = Array.prototype.slice.call(block.querySelectorAll(".rbe-image-stage"));
      result.data.items = (result.data.items || []).map(function (item, index) {
        var stage = stages[index];
        if (!stage) return item;
        item.rotation = Number(stage.dataset.rotation || 0);
        item.crop = stage.dataset.crop === "custom" ? "custom" : "free";
        return item;
      });
    }
    return result;
  };

  document.addEventListener("click", function (event) {
    var button = event.target.closest(".rbe-image-transform");
    if (!button) return;
    var stage = button.closest(".rbe-image-stage");
    var block = button.closest(".rbe-block");
    if (!stage || !block) return;
    event.preventDefault();
    var action = button.dataset.transform || "";
    if (action === "rotate-left") stage.dataset.rotation = String((Number(stage.dataset.rotation || 0) - 90) % 360);
    if (action === "rotate-right") stage.dataset.rotation = String((Number(stage.dataset.rotation || 0) + 90) % 360);
    var root = block.closest(".rbe");
    if (root && root.__blockwriteaiInstance) {
      applyImageTransform(stage, root.__blockwriteaiInstance);
      root.__blockwriteaiInstance.updateMediaActionAvailability(stage.closest(".rbe-image-gallery") || block);
      root.__blockwriteaiInstance.changed(true);
    } else {
      applyImageTransform(stage);
    }
  });

  injectStyles();
  bootOutputHydration();
})(typeof window !== "undefined" ? window : this);
