function mount(ctx) {
  if (!ctx || !ctx.el || !ctx.el.querySelector) return;

  var root = ctx.el;
  var q = function (selector) { return root.querySelector(selector); };
  var qa = function (selector) { return Array.prototype.slice.call(root.querySelectorAll(selector)); };
  var makeNode = function (tag) {
    var node = document.createElement(tag);
    node.className = "rf-editor-node";
    return node;
  };
  var DEFAULT_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 91 13" preserveAspectRatio="xMidYMid meet"><path id="wordmark" fill="#0B1220" d="M12.0669 12.2308H9.42547L8.37547 9.49919H3.56844L2.57586 12.2308H8.01086e-05L4.68406 0.205053H7.25164L12.0669 12.2308ZM7.59617 7.47302L5.93914 3.01052L4.31492 7.47302H7.59617ZM22.5695 7.80935L24.9238 8.55583C24.5629 9.86833 23.9613 10.8445 23.1191 11.4844C22.2824 12.1187 21.2187 12.4359 19.9281 12.4359C18.3312 12.4359 17.0187 11.8918 15.9906 10.8035C14.9625 9.70974 14.4484 8.21677 14.4484 6.32458C14.4484 4.32302 14.9652 2.7699 15.9988 1.66521C17.0324 0.555053 18.3914 -2.52472e-05 20.0758 -2.52472e-05C21.5468 -2.52472e-05 22.7418 0.43474 23.6605 1.30427C24.2074 1.81833 24.6176 2.55662 24.891 3.51912L22.4875 4.09333C22.3453 3.4699 22.0472 2.97771 21.5933 2.61677C21.1449 2.25583 20.598 2.07537 19.9527 2.07537C19.0613 2.07537 18.3367 2.39529 17.7789 3.03513C17.2265 3.67498 16.9504 4.7113 16.9504 6.14412C16.9504 7.66443 17.2238 8.74724 17.7707 9.39255C18.3176 10.0379 19.0285 10.3605 19.9035 10.3605C20.5488 10.3605 21.1039 10.1554 21.5687 9.74529C22.0336 9.33513 22.3672 8.68982 22.5695 7.80935ZM27.9124 8.31794L30.2749 8.08826C30.4171 8.88123 30.7042 9.46365 31.1362 9.83552C31.5737 10.2074 32.1616 10.3933 32.8999 10.3933C33.6819 10.3933 34.2698 10.2293 34.6635 9.90115C35.0628 9.56755 35.2624 9.17927 35.2624 8.7363C35.2624 8.45193 35.1776 8.2113 35.0081 8.01443C34.844 7.81208 34.5542 7.63708 34.1385 7.48943C33.8542 7.39099 33.2061 7.21599 32.1944 6.96443C30.8928 6.64177 29.9796 6.24529 29.4546 5.77498C28.7163 5.11326 28.3471 4.30662 28.3471 3.35505C28.3471 2.74255 28.5194 2.17107 28.8639 1.6406C29.2139 1.10466 29.7143 0.69724 30.3651 0.418334C31.0213 0.139428 31.8116 -2.52472e-05 32.7358 -2.52472e-05C34.2452 -2.52472e-05 35.3799 0.330834 36.1401 0.992553C36.9057 1.65427 37.3077 2.53747 37.346 3.64216L34.9178 3.7488C34.8139 3.13083 34.5897 2.68787 34.2452 2.4199C33.9061 2.14646 33.3948 2.00974 32.7112 2.00974C32.0057 2.00974 31.4534 2.15466 31.0542 2.44451C30.7971 2.63044 30.6686 2.87927 30.6686 3.19099C30.6686 3.47537 30.7889 3.71873 31.0296 3.92107C31.3358 4.1781 32.0796 4.44607 33.2608 4.72498C34.4421 5.00388 35.3143 5.29373 35.8776 5.59451C36.4463 5.88982 36.8893 6.29724 37.2065 6.81677C37.5292 7.33083 37.6905 7.96794 37.6905 8.7281C37.6905 9.41716 37.4991 10.0625 37.1163 10.664C36.7335 11.2656 36.1921 11.714 35.4921 12.0094C34.7921 12.2992 33.9198 12.4441 32.8753 12.4441C31.3549 12.4441 30.1874 12.0941 29.3725 11.3941C28.5577 10.6886 28.071 9.66326 27.9124 8.31794ZM41.2451 12.2308V0.205053H50.1619V2.23943H43.6732V4.90544H49.7107V6.93162H43.6732V10.2047H50.3916V12.2308H41.2451ZM56.6696 12.2308V2.23943H53.1012V0.205053H62.6578V2.23943H59.0977V12.2308H56.6696ZM65.7531 12.2308V0.205053H70.8636C72.1488 0.205053 73.0812 0.314428 73.6609 0.533178C74.246 0.746459 74.7136 1.12927 75.0636 1.68162C75.4136 2.23396 75.5886 2.8656 75.5886 3.57654C75.5886 4.47888 75.3234 5.22537 74.7929 5.81599C74.2624 6.40115 73.4695 6.77029 72.414 6.92341C72.939 7.22966 73.371 7.56599 73.7101 7.9324C74.0546 8.2988 74.5167 8.94958 75.0964 9.88474L76.5648 12.2308H73.6609L71.9054 9.61404C71.282 8.67888 70.8554 8.09099 70.6257 7.85037C70.396 7.60427 70.1527 7.43748 69.8956 7.34998C69.6386 7.25701 69.2312 7.21052 68.6734 7.21052H68.1812V12.2308H65.7531ZM68.1812 5.29099H69.9777C71.1425 5.29099 71.8698 5.24177 72.1597 5.14333C72.4495 5.0449 72.6765 4.87537 72.8406 4.63474C73.0046 4.39412 73.0866 4.09333 73.0866 3.7324C73.0866 3.32771 72.9773 3.00232 72.7585 2.75622C72.5452 2.50466 72.2417 2.34607 71.848 2.28044C71.6511 2.2531 71.0605 2.23943 70.0761 2.23943H68.1812V5.29099ZM90.242 12.2308H87.6006L86.5506 9.49919H81.7436L80.751 12.2308H78.1752L82.8592 0.205053H85.4268L90.242 12.2308ZM85.7713 7.47302L84.1143 3.01052L82.4901 7.47302H85.7713Z" transform="translate(0.3719749450683594,-4.0244140625)"/></svg>';

  var projectTitle = q('[data-role="project-title"]');
  var saveState = q('[data-role="save-state"]');
  var projectList = q('[data-role="project-list"]');
  var stage = q('[data-role="stage"]');
  var artboard = q('[data-role="artboard"]');
  var selection = q('[data-role="selection"]');
  var svgHost = q('[data-role="svg-host"]');
  var fileInput = q('[data-role="file-input"]');
  var codeEditor = q('[data-role="code-editor"]');
  var codeStatus = q('[data-role="code-status"]');
  var lunaPrompt = q('[data-role="luna-prompt"]');
  var lunaStatus = q('[data-role="luna-status"]');
  var transcript = q('[data-role="luna-transcript"]');
  var toast = q('[data-role="toast"]');
  var toastTimer = null;
  var busy = false;
  var dirty = true;
  var zoom = 1;
  var gridOn = true;
  var snapOn = true;
  var history = [];
  var future = [];
  var lunaHistory = [];
  var projects = [];
  var activeRow = null;
  var activeProjectId = makeId();
  var inputCheckpoint = null;

  var defaults = {
    canvasSize: 1024,
    shape: "square",
    background: "#ffffff",
    transparent: false,
    corner: 0,
    x: 0,
    y: 0,
    scale: 72,
    rotation: 0,
    fill: "#0b1220",
    stroke: "#0b1220",
    strokeWidth: 0,
    opacity: 100,
    blend: "normal",
    curveDirection: "straight",
    curveAmount: 54,
    curveEmphasis: 7,
    curveTangent: true,
    warp: "flat",
    warpAmount: 36,
    effect: "none",
    intensity: 35
  };

  var state = {
    svg: DEFAULT_SVG,
    settings: copy(defaults)
  };

  var presets = [
    { name: "Core", settings: { background: "#ffffff", fill: "#0b1220", shape: "square", corner: 0, scale: 72, warp: "flat", effect: "none" } },
    { name: "Midnight PFP", settings: { background: "#0b1220", fill: "#ffffff", shape: "circle", scale: 67, warp: "wide", warpAmount: 22, effect: "glow", intensity: 18 } },
    { name: "Signal Blue", settings: { background: "#0a84ff", fill: "#ffffff", shape: "square", corner: 18, scale: 70, warp: "flat", effect: "soft", intensity: 20 } },
    { name: "Arc Lift", settings: { background: "#f0f5fb", fill: "#0b1220", shape: "circle", scale: 63, rotation: -3, warp: "arc", warpAmount: 52, effect: "soft", intensity: 28 } },
    { name: "Widebody", settings: { background: "#f5f1e8", fill: "#171717", shape: "square", corner: 7, scale: 68, warp: "wide", warpAmount: 64, effect: "hard", intensity: 28 } },
    { name: "Electric Wave", settings: { background: "#11152a", fill: "#80ecff", shape: "square", corner: 24, scale: 67, warp: "wave", warpAmount: 38, effect: "glow", intensity: 48 } },
    { name: "Ink Ripple", settings: { background: "#f6f0ff", fill: "#3d1768", shape: "circle", scale: 64, warp: "ripple", warpAmount: 26, effect: "none", intensity: 0 } },
    { name: "Rough Proof", settings: { background: "#f2f0e8", fill: "#151515", shape: "square", corner: 2, scale: 69, warp: "rough", warpAmount: 17, effect: "pressed", intensity: 35 } },
    { name: "Outline", settings: { background: "#ffffff", fill: "#ffffff", stroke: "#0b1220", strokeWidth: 2.25, shape: "circle", scale: 65, warp: "tall", warpAmount: 18, effect: "none" } },
    { name: "Difference", settings: { background: "#ff725c", fill: "#27f2c2", shape: "square", corner: 32, scale: 76, rotation: -5, blend: "difference", warp: "italic", warpAmount: 44, effect: "hard", intensity: 23 } }
  ];

  function copy(value) {
    return JSON.parse(JSON.stringify(value));
  }

  function makeId() {
    return "project-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 8);
  }

  function clamp(value, min, max) {
    value = Number(value);
    return Math.max(min, Math.min(max, isFinite(value) ? value : min));
  }

  function sanitizeName(value) {
    value = String(value || "Untitled logo").trim();
    return (value || "Untitled logo").slice(0, 80);
  }

  function notify(message, error) {
    if (!toast) return;
    toast.textContent = message;
    toast.hidden = false;
    toast.setAttribute("data-state", error ? "error" : "ok");
    if (toastTimer) clearTimeout(toastTimer);
    toastTimer = setTimeout(function () { toast.hidden = true; }, 2600);
  }

  function markDirty() {
    dirty = true;
    if (saveState) {
      saveState.textContent = "Unsaved";
      saveState.setAttribute("data-state", "dirty");
    }
  }

  function markSaving() {
    if (saveState) {
      saveState.textContent = "Saving…";
      saveState.setAttribute("data-state", "saving");
    }
  }

  function markSaved() {
    dirty = false;
    if (saveState) {
      saveState.textContent = "Saved";
      saveState.setAttribute("data-state", "saved");
    }
  }

  function snapshot() {
    return {
      svg: state.svg,
      settings: copy(state.settings),
      name: projectTitle ? projectTitle.value : "Untitled logo"
    };
  }

  function sameSnapshot(a, b) {
    return a && b && a.svg === b.svg && a.name === b.name &&
      JSON.stringify(a.settings) === JSON.stringify(b.settings);
  }

  function checkpoint(before) {
    before = before || snapshot();
    var last = history[history.length - 1];
    if (!sameSnapshot(last, before)) history.push(before);
    if (history.length > 80) history.shift();
    future = [];
    updateUndoButtons();
  }

  function restore(snap) {
    if (!snap) return;
    state.svg = sanitizeSvg(snap.svg);
    state.settings = Object.assign(copy(defaults), snap.settings || {});
    if (projectTitle) projectTitle.value = snap.name || projectTitle.value;
    syncControls();
    renderSvg();
    applyView();
    markDirty();
  }

  function undo() {
    if (!history.length) return;
    var previous = history.pop();
    future.push(snapshot());
    restore(previous);
    updateUndoButtons();
  }

  function redo() {
    if (!future.length) return;
    var next = future.pop();
    history.push(snapshot());
    restore(next);
    updateUndoButtons();
  }

  function updateUndoButtons() {
    var undoButton = q('[data-action="undo"]');
    var redoButton = q('[data-action="redo"]');
    if (undoButton) undoButton.disabled = !history.length;
    if (redoButton) redoButton.disabled = !future.length;
  }

  function sanitizeSvg(markup) {
    markup = String(markup || "").trim();
    if (!markup || markup.length > 400000) throw new Error("SVG must be under 400 KB");
    var holder = document.createElement("div");
    holder.innerHTML = markup;
    var svg = holder.firstElementChild;
    if (!svg || String(svg.localName).toLowerCase() !== "svg" || holder.children.length !== 1) {
      throw new Error("Source must contain exactly one SVG element");
    }

    Array.prototype.slice.call(svg.querySelectorAll("script,foreignObject,iframe,object,embed,audio,video")).forEach(function (node) {
      if (node.parentNode) node.parentNode.removeChild(node);
    });

    Array.prototype.slice.call(svg.querySelectorAll("*")).concat([svg]).forEach(function (node) {
      Array.prototype.slice.call(node.attributes || []).forEach(function (attr) {
        var name = String(attr.name || "").toLowerCase();
        var value = String(attr.value || "");
        if (name.indexOf("on") === 0 ||
            /javascript\s*:/i.test(value) ||
            /url\s*\(\s*['"]?\s*(?:https?:|data:|javascript:)/i.test(value) ||
            ((name === "href" || name === "xlink:href" || name === "src") && !/^#/.test(value))) {
          node.removeAttribute(attr.name);
        }
      });
    });

    if (!svg.getAttribute("viewBox")) {
      var width = parseFloat(svg.getAttribute("width")) || 512;
      var height = parseFloat(svg.getAttribute("height")) || 512;
      svg.setAttribute("viewBox", "0 0 " + width + " " + height);
    }
    svg.removeAttribute("width");
    svg.removeAttribute("height");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
    return svg.outerHTML;
  }

  function parseSvg(markup) {
    var probe = document.createElement("div");
    probe.innerHTML = String(markup || "");
    if (!probe.firstElementChild) return null;
    var holder = document.createElement("div");
    holder.innerHTML = sanitizeSvg(markup);
    return holder.firstElementChild;
  }

  function splitDefaultWordmark(svg) {
    var path = svg && svg.querySelector("path#wordmark");
    if (!path || svg.querySelectorAll("path").length !== 1) return [];
    var d = String(path.getAttribute("d") || "");
    var markers = ["M22.5695", "M27.9124", "M41.2451", "M56.6696", "M65.7531", "M90.242"];
    var starts = [0];
    for (var i = 0; i < markers.length; i += 1) {
      var at = d.indexOf(markers[i]);
      if (at < 0) return [];
      starts.push(at);
    }
    starts.push(d.length);
    var ns = "http://www.w3.org/2000/svg";
    var group = document.createElementNS(ns, "g");
    Array.prototype.slice.call(path.attributes || []).forEach(function (attr) {
      if (attr.name !== "d") group.setAttribute(attr.name, attr.value);
    });
    group.setAttribute("data-luna-wordmark", "glyphs");
    for (var g = 0; g < starts.length - 1; g += 1) {
      var glyph = document.createElementNS(ns, "path");
      glyph.setAttribute("d", d.slice(starts[g], starts[g + 1]));
      glyph.setAttribute("data-luna-glyph", String(g));
      group.appendChild(glyph);
    }
    path.parentNode.replaceChild(group, path);
    return Array.prototype.slice.call(group.children);
  }

  function unwrapArch(svg) {
    var wrappers = Array.prototype.slice.call(svg.querySelectorAll('g[data-luna-arch="glyph"]'));
    wrappers.forEach(function (wrapper) {
      var parent = wrapper.parentNode;
      while (wrapper.firstChild) parent.insertBefore(wrapper.firstChild, wrapper);
      parent.removeChild(wrapper);
    });
    var baseViewBox = svg.getAttribute("data-luna-base-viewbox");
    if (baseViewBox) {
      svg.setAttribute("viewBox", baseViewBox);
      svg.removeAttribute("data-luna-base-viewbox");
    }
    svg.removeAttribute("data-luna-arch-strength");
    svg.removeAttribute("data-luna-arch-direction");
    return wrappers.length;
  }

  function normalizeLegacyArch(svg) {
    var wordmark = svg.querySelector("g#wordmark");
    if (!wordmark) return;
    var paths = Array.prototype.slice.call(wordmark.children || []).filter(function (node) {
      return String(node.localName || "").toLowerCase() === "path";
    });
    if (paths.length < 3) return;
    var rotated = paths.filter(function (node) {
      return /rotate\s*\(/i.test(String(node.getAttribute("transform") || ""));
    });
    if (rotated.length !== paths.length) return;
    paths.forEach(function (node) { node.removeAttribute("transform"); });
    if (/scale\s*\(\s*1[\s,]+0\.[5-9]/i.test(String(wordmark.getAttribute("transform") || ""))) {
      wordmark.removeAttribute("transform");
    }
  }

  function archCandidates(svg) {
    normalizeLegacyArch(svg);
    var split = splitDefaultWordmark(svg);
    if (split.length) return split;
    var wordmark = svg.querySelector("g#wordmark");
    if (wordmark) {
      var glyphs = Array.prototype.slice.call(wordmark.children || []).filter(function (node) {
        return /^(path|rect|circle|ellipse|polygon|polyline|text)$/i.test(String(node.localName || ""));
      });
      if (glyphs.length > 1) return glyphs;
    }
    return Array.prototype.slice.call(svg.querySelectorAll("path,rect,circle,ellipse,polygon,polyline,text")).filter(function (node) {
      return !(node.closest && node.closest("defs,clipPath,mask,filter,pattern"));
    });
  }

  function measureNodes(svg, nodes) {
    var measured = [];
    var scratch = null;
    try {
      nodes.forEach(function (node, index) { node.setAttribute("data-luna-measure", String(index)); });
      scratch = document.importNode(svg, true);
      scratch.setAttribute("width", "1200");
      scratch.setAttribute("height", "400");
      scratch.setAttribute("style", "position:fixed;left:-10000px;top:-10000px;visibility:hidden;overflow:visible");
      document.body.appendChild(scratch);
      nodes.forEach(function (_, index) {
        var clone = scratch.querySelector('[data-luna-measure="' + index + '"]');
        var box = clone && clone.getBBox ? clone.getBBox() : null;
        if (!box || !isFinite(box.x) || !isFinite(box.width)) throw new Error("bbox unavailable");
        measured.push({ x: box.x, y: box.y, width: box.width, height: box.height });
      });
    } catch (_) {
      measured = [];
    } finally {
      if (scratch && scratch.parentNode) scratch.parentNode.removeChild(scratch);
      nodes.forEach(function (node) { node.removeAttribute("data-luna-measure"); });
    }
    if (measured.length === nodes.length) return measured;
    var vb = viewBoxOf(svg);
    return nodes.map(function (_, index) {
      var width = vb[2] / Math.max(1, nodes.length);
      return { x: vb[0] + width * index, y: vb[1], width: width, height: vb[3] };
    });
  }

  function applyArch(svg, spec) {
    spec = spec || {};
    unwrapArch(svg);
    var nodes = archCandidates(svg);
    if (nodes.length < 2) throw new Error("This SVG needs at least two separate vector elements for a measured arch");
    var boxes = measureNodes(svg, nodes);
    var centers = boxes.map(function (box) { return box.x + box.width / 2; });
    var minX = Math.min.apply(Math, centers);
    var maxX = Math.max.apply(Math, centers);
    var span = Math.max(1, maxX - minX);
    var vb = viewBoxOf(svg);
    var strength = clamp(spec.amount == null ? .52 : spec.amount, .08, 1);
    var direction = spec.direction === "center-down" ? "center-down" : "center-up";
    var sign = direction === "center-up" ? -1 : 1;
    var amplitude = strength * Math.max(vb[3] * .68, span * .075);
    var centerScale = clamp(spec.centerScale == null ? .08 : spec.centerScale, 0, .24);
    var ns = "http://www.w3.org/2000/svg";
    var maxAngle = 0;

    nodes.forEach(function (node, index) {
      var box = boxes[index];
      var cx = box.x + box.width / 2;
      var cy = box.y + box.height / 2;
      var u = clamp(((cx - minX) / span) * 2 - 1, -1, 1);
      var bell = 1 - u * u;
      var dy = sign * amplitude * bell;
      var slope = -4 * sign * amplitude * u / span;
      var angle = spec.tangent === false ? 0 : clamp(Math.atan(slope) * 180 / Math.PI, -24, 24);
      var scaleAtCenter = 1 + centerScale * bell;
      maxAngle = Math.max(maxAngle, Math.abs(angle));
      var wrapper = document.createElementNS(ns, "g");
      wrapper.setAttribute("data-luna-arch", "glyph");
      wrapper.setAttribute("data-luna-arch-index", String(index));
      wrapper.setAttribute("transform", [
        "translate(0 " + dy.toFixed(3) + ")",
        "rotate(" + angle.toFixed(3) + " " + cx.toFixed(3) + " " + cy.toFixed(3) + ")",
        "translate(" + cx.toFixed(3) + " " + cy.toFixed(3) + ")",
        "scale(" + scaleAtCenter.toFixed(4) + ")",
        "translate(" + (-cx).toFixed(3) + " " + (-cy).toFixed(3) + ")"
      ].join(" "));
      node.parentNode.insertBefore(wrapper, node);
      wrapper.appendChild(node);
    });

    svg.setAttribute("data-luna-base-viewbox", vb.join(" "));
    svg.setAttribute("data-luna-arch-strength", strength.toFixed(3));
    svg.setAttribute("data-luna-arch-direction", direction);
    var padY = vb[3] * .1 + amplitude * .16;
    var padX = vb[2] * .025;
    var topExtra = direction === "center-up" ? amplitude : 0;
    var bottomExtra = direction === "center-down" ? amplitude : 0;
    svg.setAttribute("viewBox", [
      vb[0] - padX,
      vb[1] - topExtra - padY,
      vb[2] + padX * 2,
      vb[3] + topExtra + bottomExtra + padY * 2
    ].map(function (value) { return Number(value.toFixed(4)); }).join(" "));
    return {
      glyphs: nodes.length,
      amplitude: Number(amplitude.toFixed(2)),
      maxAngle: Number(maxAngle.toFixed(1)),
      strength: strength,
      direction: direction
    };
  }

  function directEditPlan(request) {
    var text = String(request || "").toLowerCase();
    var correction = /^(no\b|not\b|actually\b|instead\b)|\btry again\b|\bmore like\b/.test(text);
    if ((/\b(remove|reset|clear)\b/.test(text) && /\b(curve|arch|arc)\b/.test(text)) || /\bstraighten\b/.test(text)) {
      return { summary: "Straighten the wordmark", operations: [{ type: "reset_arch" }], correction: correction };
    }
    if (/\b(curve|curved|arch|arched|arc)\b/.test(text) || /universal\s+studios/.test(text)) {
      var amount = /\b(dramatic|strong|deep|big|much more|universal)\b/.test(text) ? .84 :
        (/\b(subtle|slight|gentle|small)\b/.test(text) ? .3 : .54);
      var centerUp = /universal\s+studios|\b(?:rises?|raised?|up|higher)\b[^.]{0,28}\b(?:center|middle)\b|\b(?:center|middle)\b[^.]{0,28}\b(?:rises?|raised?|up|higher)\b/.test(text);
      var centerDown = /\b(?:center|middle)\b[^.]{0,18}\b(?:dips?|drops?|lower(?:ed)?|down)\b|\b(?:dips?|drops?|lower(?:ed)?|down)\b[^.]{0,18}\b(?:center|middle)\b/.test(text);
      var direction = centerUp ? "center-up" : centerDown ? "center-down" : "center-up";
      return {
        summary: (amount > .7 ? "Dramatic " : amount < .4 ? "Subtle " : "") + (direction === "center-up" ? "center-up arch" : "center-down arch"),
        operations: [{ type: "arch", amount: amount, direction: direction, tangent: true, centerScale: amount > .7 ? .12 : .07 }],
        correction: correction
      };
    }
    var color = String(request || "").match(/#[0-9a-fA-F]{6}\b/);
    if (color && /\b(fill|color|colour|make it|turn it)\b/i.test(request)) {
      return { summary: "Set logo fill to " + color[0].toUpperCase(), operations: [{ type: "set_fill", value: color[0] }], correction: correction };
    }
    return null;
  }

  function applyPaint(svg) {
    var shapes = Array.prototype.slice.call(svg.querySelectorAll("path,rect,circle,ellipse,polygon,polyline,line,text"));
    shapes.forEach(function (node) {
      if (node.closest && node.closest("defs")) return;
      var existing = String(node.getAttribute("fill") || "").toLowerCase();
      if (existing !== "none") node.setAttribute("fill", state.settings.fill);
      if (Number(state.settings.strokeWidth) > 0) {
        node.setAttribute("stroke", state.settings.stroke);
        node.setAttribute("stroke-width", String(state.settings.strokeWidth));
        node.setAttribute("paint-order", "stroke fill");
        node.setAttribute("stroke-linejoin", "round");
      } else {
        node.removeAttribute("stroke");
        node.removeAttribute("stroke-width");
      }
    });
  }

  function installFilters(svg) {
    var ns = "http://www.w3.org/2000/svg";
    var defs = svg.querySelector("defs");
    if (!defs) {
      defs = document.createElementNS(ns, "defs");
      svg.insertBefore(defs, svg.firstChild);
    }
    Array.prototype.slice.call(defs.querySelectorAll('[data-editor-filter="true"]')).forEach(function (node) {
      node.parentNode.removeChild(node);
    });
    [
      { id: "editor-wave", freq: "0.012 0.075", oct: "1", seed: "4" },
      { id: "editor-ripple", freq: "0.045 0.035", oct: "2", seed: "7" },
      { id: "editor-rough", freq: "0.55", oct: "1", seed: "11" }
    ].forEach(function (spec) {
      var filter = document.createElementNS(ns, "filter");
      filter.setAttribute("id", spec.id);
      filter.setAttribute("data-editor-filter", "true");
      filter.setAttribute("x", "-20%");
      filter.setAttribute("y", "-20%");
      filter.setAttribute("width", "140%");
      filter.setAttribute("height", "140%");
      var turbulence = document.createElementNS(ns, "feTurbulence");
      turbulence.setAttribute("type", spec.id === "editor-ripple" ? "fractalNoise" : "turbulence");
      turbulence.setAttribute("baseFrequency", spec.freq);
      turbulence.setAttribute("numOctaves", spec.oct);
      turbulence.setAttribute("seed", spec.seed);
      turbulence.setAttribute("result", "noise");
      var displacement = document.createElementNS(ns, "feDisplacementMap");
      displacement.setAttribute("in", "SourceGraphic");
      displacement.setAttribute("in2", "noise");
      displacement.setAttribute("scale", String(Math.max(1, Number(state.settings.warpAmount) * (spec.id === "editor-rough" ? .22 : .38))));
      displacement.setAttribute("xChannelSelector", "R");
      displacement.setAttribute("yChannelSelector", spec.id === "editor-wave" ? "B" : "G");
      filter.appendChild(turbulence);
      filter.appendChild(displacement);
      defs.appendChild(filter);
    });
  }

  function renderSvg() {
    if (!svgHost) return;
    var svg = parseSvg(state.svg);
    if (!svg) {
      return;
    }
    svg.classList.add("rf-editor-node");
    Array.prototype.slice.call(svg.querySelectorAll("*")).forEach(function (node) {
      node.classList.add("rf-editor-node");
    });
    applyPaint(svg);
    installFilters(svg);
    svg.setAttribute("aria-label", "Editable logo");
    svg.setAttribute("role", "img");
    while (svgHost.firstChild) svgHost.removeChild(svgHost.firstChild);
    svgHost.appendChild(document.importNode(svg, true));
    if (codeEditor && !q('[data-panel="code"]').hidden) codeEditor.value = state.svg;
  }

  function effectFilter() {
    var amount = clamp(state.settings.intensity, 0, 100);
    var effect = state.settings.effect;
    var parts = [];
    if (effect === "soft") parts.push("drop-shadow(0 " + (2 + amount * .08) + "px " + (4 + amount * .16) + "px rgba(15,23,42," + (.08 + amount * .003) + "))");
    if (effect === "hard") parts.push("drop-shadow(" + (2 + amount * .08) + "px " + (2 + amount * .08) + "px 0 rgba(11,18,32," + (.35 + amount * .004) + "))");
    if (effect === "glow") parts.push("drop-shadow(0 0 " + (3 + amount * .18) + "px " + state.settings.fill + ")");
    if (effect === "bloom") parts.push("drop-shadow(0 0 " + (2 + amount * .12) + "px rgba(255,255,255,.85)) drop-shadow(0 0 " + (5 + amount * .18) + "px " + state.settings.fill + ")");
    if (effect === "pressed") parts.push("drop-shadow(1px 2px 0 rgba(255,255,255,.75)) drop-shadow(-1px -2px " + (1 + amount * .035) + "px rgba(11,18,32,.25))");
    if (state.settings.warp === "wave") parts.push("url(#editor-wave)");
    if (state.settings.warp === "ripple") parts.push("url(#editor-ripple)");
    if (state.settings.warp === "rough") parts.push("url(#editor-rough)");
    return parts.length ? parts.join(" ") : "none";
  }

  function warpValues() {
    var amount = clamp(state.settings.warpAmount, 0, 100) / 100;
    var warp = state.settings.warp;
    var out = { skew: 0, wide: 1, tall: 1 };
    if (warp === "arc") { out.skew = -7 * amount; out.wide = 1 + .09 * amount; out.tall = 1 - .08 * amount; }
    if (warp === "italic") out.skew = -24 * amount;
    if (warp === "wide") out.wide = 1 + .55 * amount;
    if (warp === "tall") out.tall = 1 + .72 * amount;
    if (warp === "squeeze") { out.wide = 1 - .42 * amount; out.tall = 1 + .34 * amount; }
    return out;
  }

  function applyView() {
    var s = state.settings;
    var warp = warpValues();
    ctx.setVar("editor-bg", s.transparent ? "transparent" : s.background);
    ctx.setVar("editor-radius", s.shape === "circle" ? "50%" : clamp(s.corner, 0, 48) + "%");
    ctx.setVar("logo-x", Number(s.x || 0) + "%");
    ctx.setVar("logo-y", Number(s.y || 0) + "%");
    ctx.setVar("logo-scale", clamp(s.scale, 10, 160) / 100);
    ctx.setVar("logo-rotate", Number(s.rotation || 0) + "deg");
    ctx.setVar("logo-opacity", clamp(s.opacity, 5, 100) / 100);
    ctx.setVar("logo-filter", effectFilter());
    ctx.setVar("logo-blend", s.blend || "normal");
    ctx.setVar("warp-skew", warp.skew + "deg");
    ctx.setVar("warp-wide", warp.wide);
    ctx.setVar("warp-tall", warp.tall);
    ctx.setVar("stage-zoom", zoom);
    if (artboard) {
      artboard.setAttribute("data-shape", s.shape);
      artboard.setAttribute("data-transparent", s.transparent ? "true" : "false");
    }
    ctx.setData("grid", gridOn ? "on" : "off");
    ctx.setData("snap", snapOn ? "on" : "off");
    var sizeLabel = q('[data-role="size-label"]');
    if (sizeLabel) sizeLabel.textContent = s.canvasSize + " × " + s.canvasSize;
    var zoomLabel = q('[data-role="zoom-label"]');
    if (zoomLabel) zoomLabel.textContent = Math.round(zoom * 100) + "%";
    updateOutputs();
  }

  function updateOutputs() {
    var s = state.settings;
    var values = {
      corner: Math.round(s.corner),
      scale: Math.round(s.scale) + "%",
      rotation: Math.round(s.rotation) + "°",
      strokeWidth: Number(s.strokeWidth).toFixed(Number(s.strokeWidth) % 1 ? 2 : 0),
      opacity: Math.round(s.opacity) + "%",
      curveAmount: Math.round(s.curveAmount),
      curveEmphasis: Math.round(s.curveEmphasis) + "%",
      warpAmount: Math.round(s.warpAmount),
      intensity: Math.round(s.intensity)
    };
    Object.keys(values).forEach(function (key) {
      var output = q('[data-output="' + key + '"]');
      if (output) output.textContent = values[key];
    });
  }

  function syncControls() {
    qa("[data-setting]").forEach(function (input) {
      var key = input.getAttribute("data-setting");
      if (!(key in state.settings)) return;
      if (input.type === "checkbox") input.checked = !!state.settings[key];
      else input.value = state.settings[key];
    });
    qa("[data-shape-action]").forEach(function (button) {
      button.setAttribute("aria-pressed", button.getAttribute("data-shape-action") === state.settings.shape ? "true" : "false");
    });
    renderSvg();
    applyView();
  }

  function setTab(name) {
    qa("[data-tab]").forEach(function (button) {
      button.setAttribute("aria-selected", button.getAttribute("data-tab") === name ? "true" : "false");
    });
    qa("[data-panel]").forEach(function (panel) {
      panel.hidden = panel.getAttribute("data-panel") !== name;
    });
    ctx.setData("mode", name);
    if (name === "code" && codeEditor) {
      codeEditor.value = state.svg;
      validateCode(false);
    }
  }

  function renderPresets() {
    var grid = q('[data-role="preset-grid"]');
    if (!grid) return;
    while (grid.firstChild) grid.removeChild(grid.firstChild);
    presets.forEach(function (preset, index) {
      var button = makeNode("button");
      button.setAttribute("type", "button");
      button.setAttribute("data-preset", String(index));
      var preview = makeNode("span");
      preview.setAttribute("data-preset-preview", "");
      preview.setAttribute("data-circle", preset.settings.shape === "circle" ? "true" : "false");
      preview.style.setProperty("--preset-bg", preset.settings.background || "#ffffff");
      preview.style.setProperty("--preset-fill", preset.settings.fill || "#0b1220");
      try {
        var svg = parseSvg(DEFAULT_SVG);
        preview.appendChild(document.importNode(svg, true));
      } catch (_) {}
      var name = makeNode("span");
      name.setAttribute("data-preset-name", "");
      name.textContent = preset.name;
      button.appendChild(preview);
      button.appendChild(name);
      grid.appendChild(button);
    });
  }

  function applyPreset(index) {
    var preset = presets[Number(index)];
    if (!preset) return;
    checkpoint();
    state.settings = Object.assign(copy(defaults), state.settings, preset.settings);
    if (preset.name === "Arc Lift") {
      var source = parseSvg(state.svg);
      var arch = applyArch(source, { amount: .56, direction: "center-up", tangent: true, centerScale: .08 });
      state.svg = sanitizeSvg(source.outerHTML);
      state.settings.curveDirection = "center-up";
      state.settings.curveAmount = 56;
      state.settings.curveEmphasis = 8;
      state.settings.curveTangent = true;
      state.settings.warp = "flat";
      lunaHistory.push({
        request: "Apply the Arc Lift preset",
        outcome: "Measured center-up arch across " + arch.glyphs + " glyphs",
        correction: false,
        source: "preset",
        at: new Date().toISOString()
      });
    }
    syncControls();
    markDirty();
    notify(preset.name + " applied");
  }

  function rowProject(row) {
    var meta = row && row.meta && typeof row.meta === "object" ? row.meta : {};
    var settings = {};
    var savedLunaHistory = [];
    try { settings = JSON.parse(meta.settings || "{}"); } catch (_) {}
    try { savedLunaHistory = JSON.parse(meta.luna_history || "[]"); } catch (_) {}
    return {
      code: row.code,
      name: meta.name || row.label || "Untitled logo",
      svg: meta.svg || DEFAULT_SVG,
      settings: Object.assign(copy(defaults), settings),
      lunaHistory: Array.isArray(savedLunaHistory) ? savedLunaHistory.slice(-12) : [],
      projectId: meta.project_id || row.code,
      updatedAt: meta.updated_at || "",
      row: row
    };
  }

  function formatTime(value) {
    if (!value) return "Saved project";
    var date = new Date(value);
    if (isNaN(date.getTime())) return "Saved project";
    return "Updated " + date.toLocaleDateString(undefined, { month: "short", day: "numeric" });
  }

  function renderProjects(rows) {
    rows = Array.isArray(rows) ? rows : [];
    projects = rows.filter(function (row) { return !(row.meta || {}).deleted; }).map(rowProject).sort(function (a, b) {
      return String(b.updatedAt).localeCompare(String(a.updatedAt));
    });
    if (!projectList) return;
    while (projectList.firstChild) projectList.removeChild(projectList.firstChild);
    if (!projects.length) {
      var empty = makeNode("p");
      empty.setAttribute("data-role", "empty-projects");
      empty.textContent = "No saved projects yet. Your first save will appear here.";
      projectList.appendChild(empty);
      return;
    }
    projects.forEach(function (project) {
      var button = makeNode("button");
      button.setAttribute("type", "button");
      button.setAttribute("data-project", project.code);
      button.setAttribute("aria-current", activeRow && activeRow.code === project.code ? "true" : "false");
      var thumb = makeNode("span");
      thumb.setAttribute("data-project-thumb", "");
      try {
        var svg = parseSvg(project.svg);
        Array.prototype.slice.call(svg.querySelectorAll("path,rect,circle,ellipse,polygon,polyline,line,text")).forEach(function (shape) {
          if (String(shape.getAttribute("fill") || "").toLowerCase() !== "none") shape.setAttribute("fill", project.settings.fill || "#0b1220");
        });
        thumb.appendChild(document.importNode(svg, true));
      } catch (_) {
        thumb.textContent = "A";
      }
      var copyNode = makeNode("span");
      copyNode.setAttribute("data-project-copy", "");
      var strong = makeNode("strong");
      strong.textContent = project.name;
      var small = makeNode("small");
      small.textContent = formatTime(project.updatedAt);
      copyNode.appendChild(strong);
      copyNode.appendChild(small);
      var del = makeNode("span");
      del.setAttribute("data-project-delete", project.code);
      del.setAttribute("title", "Delete project");
      del.textContent = "×";
      button.appendChild(thumb);
      button.appendChild(copyNode);
      button.appendChild(del);
      projectList.appendChild(button);
    });
  }

  function loadProject(code) {
    var project = projects.filter(function (item) { return item.code === code; })[0];
    if (!project) return;
    if (dirty && !window.confirm("Open this project and discard the unsaved canvas?")) return;
    state.svg = sanitizeSvg(project.svg);
    state.settings = Object.assign(copy(defaults), project.settings || {});
    lunaHistory = copy(project.lunaHistory || []);
    activeRow = project.row;
    activeProjectId = project.projectId;
    if (projectTitle) projectTitle.value = project.name;
    history = [];
    future = [];
    syncControls();
    markSaved();
    renderProjects(projects.map(function (item) { return item.row; }));
    updateUndoButtons();
  }

  function newProject(force) {
    if (!force && dirty && !window.confirm("Start a new project and discard the unsaved canvas?")) return;
    activeRow = null;
    activeProjectId = makeId();
    state.svg = DEFAULT_SVG;
    state.settings = copy(defaults);
    lunaHistory = [];
    if (projectTitle) projectTitle.value = "Untitled logo";
    history = [];
    future = [];
    syncControls();
    markDirty();
    renderProjects(projects.map(function (item) { return item.row; }));
    updateUndoButtons();
  }

  function saveProject() {
    if (busy) return;
    busy = true;
    markSaving();
    var payload = {
      action: activeRow ? "update" : "add",
      name: sanitizeName(projectTitle && projectTitle.value),
      svg: state.svg,
      settings: JSON.stringify(state.settings),
      luna_history: JSON.stringify(lunaHistory.slice(-12)),
      project_id: activeProjectId,
      updated_at: new Date().toISOString()
    };
    if (activeRow) payload.code = activeRow.code;
    ctx.run("logo_projects_commit", payload).then(function (launch) {
      busy = false;
      if (!launch || launch.status !== "done") {
        markDirty();
        notify("Project did not save", true);
        return;
      }
      markSaved();
      notify("Project saved to Runner");
      return ctx.readSet("logo_projects").then(function (rows) {
        renderProjects(rows);
        var found = projects.filter(function (project) { return project.projectId === activeProjectId; })[0];
        if (found) activeRow = found.row;
        renderProjects(rows);
      });
    }).catch(function (error) {
      busy = false;
      markDirty();
      notify("Save failed: " + ((error && error.message) || "offline"), true);
    });
  }

  function deleteProject(code) {
    var project = projects.filter(function (item) { return item.code === code; })[0];
    if (!project || !window.confirm("Delete “" + project.name + "”?")) return;
    ctx.run("logo_projects_commit", { action: "remove", code: code }).then(function (launch) {
      if (!launch || launch.status !== "done") {
        notify("Project could not be deleted", true);
        return;
      }
      if (activeRow && activeRow.code === code) newProject(true);
      notify("Project deleted");
      return ctx.readSet("logo_projects").then(renderProjects);
    }).catch(function () {
      notify("Project could not be deleted", true);
    });
  }

  function validateCode(showToast) {
    if (!codeEditor) return false;
    try {
      sanitizeSvg(codeEditor.value);
      if (codeStatus) {
        codeStatus.textContent = "Valid SVG";
        codeStatus.setAttribute("data-state", "valid");
      }
      return true;
    } catch (error) {
      if (codeStatus) {
        codeStatus.textContent = error.message;
        codeStatus.setAttribute("data-state", "error");
      }
      if (showToast) notify(error.message, true);
      return false;
    }
  }

  function applyCode() {
    if (!validateCode(true)) return;
    checkpoint();
    state.svg = sanitizeSvg(codeEditor.value);
    renderSvg();
    markDirty();
    notify("SVG source applied");
  }

  function formatCode() {
    if (!validateCode(true)) return;
    codeEditor.value = sanitizeSvg(codeEditor.value).replace(/></g, ">\n<");
    validateCode(false);
  }

  function extractSvg(value) {
    value = String(value || "").trim();
    value = value.replace(/^```(?:svg|xml)?\s*/i, "").replace(/\s*```$/i, "");
    var start = value.search(/<svg[\s>]/i);
    var end = value.toLowerCase().lastIndexOf("</svg>");
    if (start < 0 || end < start) throw new Error("Luna did not return a complete SVG");
    return value.slice(start, end + 6);
  }

  function addMessage(kind, text) {
    if (!transcript) return;
    var message = makeNode("p");
    message.setAttribute("data-message", kind);
    message.textContent = text;
    transcript.appendChild(message);
    transcript.scrollTop = transcript.scrollHeight;
  }

  function setLunaStatus(text, mode) {
    if (!lunaStatus) return;
    lunaStatus.textContent = text;
    lunaStatus.setAttribute("data-state", mode || "ready");
  }

  function parseLunaPlan(value) {
    if (value && typeof value === "object") return value;
    var text = String(value || "").trim().replace(/^```(?:json|svg|xml)?\s*/i, "").replace(/\s*```$/i, "");
    if (!text) throw new Error("Luna returned an empty plan");
    try {
      return JSON.parse(text);
    } catch (_) {
      if (/<svg[\s>]/i.test(text)) return { summary: "Custom vector edit", svg: extractSvg(text), operations: [] };
      throw new Error("Luna did not return a valid edit plan");
    }
  }

  function vectorInventory() {
    try {
      var svg = parseSvg(state.svg);
      var nodes = Array.prototype.slice.call(svg.querySelectorAll("path,rect,circle,ellipse,polygon,polyline,line,text,g")).slice(0, 48);
      return nodes.map(function (node, index) {
        return {
          index: index,
          tag: String(node.localName || ""),
          id: node.getAttribute("id") || null,
          role: node.getAttribute("data-luna-glyph") != null ? "glyph" : null,
          transform: node.getAttribute("transform") || null
        };
      });
    } catch (_) {
      return [];
    }
  }

  function applyLunaPlan(plan) {
    plan = plan && typeof plan === "object" ? plan : {};
    var nextSettings = copy(state.settings);
    var svg = parseSvg(plan.svg ? sanitizeSvg(plan.svg) : state.svg);
    if (!svg) throw new Error("The current SVG could not be parsed");
    var operations = Array.isArray(plan.operations) ? plan.operations : [];
    var changed = !!plan.svg;
    var metrics = null;
    var summaries = [];

    operations.forEach(function (operation) {
      operation = operation && typeof operation === "object" ? operation : {};
      var type = String(operation.type || "").toLowerCase();
      if (type === "arch") {
        metrics = applyArch(svg, operation);
        nextSettings.curveDirection = metrics.direction;
        nextSettings.curveAmount = Math.round(clamp(operation.amount == null ? .52 : operation.amount, .08, 1) * 100);
        nextSettings.curveEmphasis = Math.round(clamp(operation.centerScale == null ? .08 : operation.centerScale, 0, .24) * 100);
        nextSettings.curveTangent = operation.tangent !== false;
        summaries.push((metrics.direction === "center-up" ? "center-up" : "center-down") + " arch");
        changed = true;
      } else if (type === "reset_arch") {
        var removed = unwrapArch(svg);
        if (!removed) normalizeLegacyArch(svg);
        nextSettings.curveDirection = "straight";
        summaries.push("straightened wordmark");
        changed = true;
      } else if (type === "set_fill") {
        var fill = String(operation.value || "").trim();
        if (!/^#[0-9a-f]{6}$/i.test(fill)) throw new Error("Fill must be a six-digit hex color");
        nextSettings.fill = fill;
        Array.prototype.slice.call(svg.querySelectorAll("path,rect,circle,ellipse,polygon,polyline,line,text")).forEach(function (node) {
          if (!(node.closest && node.closest("defs")) && String(node.getAttribute("fill") || "").toLowerCase() !== "none") node.setAttribute("fill", fill);
        });
        summaries.push("fill " + fill.toUpperCase());
        changed = true;
      } else if (type === "set_stroke") {
        var stroke = String(operation.value || nextSettings.stroke).trim();
        if (!/^#[0-9a-f]{6}$/i.test(stroke)) throw new Error("Stroke must be a six-digit hex color");
        nextSettings.stroke = stroke;
        if (operation.width != null) nextSettings.strokeWidth = clamp(operation.width, 0, 24);
        summaries.push("stroke " + stroke.toUpperCase());
        changed = true;
      } else if (type === "set_opacity") {
        nextSettings.opacity = clamp(operation.value, 5, 100);
        summaries.push("opacity " + Math.round(nextSettings.opacity) + "%");
        changed = true;
      } else if (type === "rotate") {
        nextSettings.rotation = clamp(operation.degrees, -180, 180);
        summaries.push("rotation " + nextSettings.rotation + "°");
        changed = true;
      } else if (type === "scale") {
        nextSettings.scale = clamp(operation.percent, 10, 160);
        summaries.push("scale " + Math.round(nextSettings.scale) + "%");
        changed = true;
      } else if (type === "move") {
        if (operation.x != null) nextSettings.x = clamp(operation.x, -50, 50);
        if (operation.y != null) nextSettings.y = clamp(operation.y, -50, 50);
        summaries.push("position adjusted");
        changed = true;
      } else if (type === "set_attribute") {
        var target = String(operation.target || "").replace(/^#/, "");
        var name = String(operation.name || "").trim();
        var value = String(operation.value == null ? "" : operation.value);
        if (!target || !/^[a-zA-Z_:][\w:.-]*$/.test(name) || /^on/i.test(name)) throw new Error("Unsafe attribute operation");
        var targetNode = svg.querySelector("#" + target.replace(/([:.])/g, "\\$1"));
        if (!targetNode) throw new Error("No SVG element with id " + target);
        targetNode.setAttribute(name, value);
        summaries.push("updated #" + target + " " + name);
        changed = true;
      } else if (type === "replace_svg") {
        svg = parseSvg(sanitizeSvg(operation.svg || ""));
        if (!svg) throw new Error("Replacement SVG could not be parsed");
        summaries.push("custom vector geometry");
        changed = true;
      }
    });

    if (plan.settings && typeof plan.settings === "object") {
      ["shape", "background", "transparent", "corner", "x", "y", "scale", "rotation", "fill", "stroke", "strokeWidth", "opacity", "blend", "curveDirection", "curveAmount", "curveEmphasis", "curveTangent", "warp", "warpAmount", "effect", "intensity"].forEach(function (key) {
        if (Object.prototype.hasOwnProperty.call(plan.settings, key)) nextSettings[key] = plan.settings[key];
      });
      changed = true;
    }
    if (!changed) throw new Error("Luna’s plan did not contain an actionable SVG change");

    checkpoint();
    state.svg = sanitizeSvg(svg.outerHTML);
    state.settings = Object.assign(copy(defaults), nextSettings);
    syncControls();
    markDirty();
    return {
      summary: String(plan.summary || summaries.join(", ") || "Vector edit applied").slice(0, 180),
      metrics: metrics
    };
  }

  function recordLunaEdit(request, result, correction, source) {
    lunaHistory.push({
      request: String(request).slice(0, 500),
      outcome: String(result.summary || "Edit applied").slice(0, 240),
      correction: !!correction,
      source: source || "luna",
      at: new Date().toISOString()
    });
    if (lunaHistory.length > 12) lunaHistory = lunaHistory.slice(-12);
  }

  function appliedMessage(result) {
    if (result.metrics) {
      return result.summary + " · " + result.metrics.glyphs + " measured glyphs · " +
        result.metrics.amplitude + " SVG-unit rise · ±" + result.metrics.maxAngle + "° tangent · undo is ready.";
    }
    return result.summary + " · geometry validated · undo is ready.";
  }

  function runLuna() {
    var request = String(lunaPrompt && lunaPrompt.value || "").trim();
    if (!request || busy) return;
    addMessage("user", request);
    var direct = directEditPlan(request);
    if (direct) {
      try {
        var directResult = applyLunaPlan(direct);
        recordLunaEdit(request, directResult, direct.correction, "deterministic");
        setLunaStatus("Applied instantly", "ready");
        addMessage("luna", appliedMessage(directResult));
        if (lunaPrompt) lunaPrompt.value = "";
      } catch (directError) {
        setLunaStatus("Review source", "error");
        addMessage("luna", "I could not apply that measured edit safely: " + directError.message);
      }
      return;
    }

    busy = true;
    setLunaStatus("Planning…", "working");
    var recent = lunaHistory.slice(-6).map(function (item, index) {
      return (index + 1) + ". USER: " + item.request + "\n   APPLIED: " + item.outcome +
        (item.correction ? "\n   NOTE: This was a correction of the preceding intent." : "");
    }).join("\n");
    var correction = /^(no\b|not\b|actually\b|instead\b)|\btry again\b|\bmore like\b/i.test(request);
    var ask = [
      "LATEST CHANGE REQUEST" + (correction ? " — THIS CORRECTS THE PREVIOUS RESULT" : "") + ":",
      request,
      "",
      "COMPACT EDIT HISTORY (oldest to newest):",
      recent || "No prior accepted edits in this project.",
      "",
      "CURRENT SAFE SVG:",
      state.svg,
      "",
      "VECTOR INVENTORY:",
      JSON.stringify(vectorInventory()),
      "",
      "WORKING ARTBOARD CONTEXT:",
      JSON.stringify({
        shape: state.settings.shape,
        canvas: state.settings.canvasSize,
        fill: state.settings.fill,
        stroke: state.settings.stroke,
        scale: state.settings.scale,
        rotation: state.settings.rotation,
        position: { x: state.settings.x, y: state.settings.y }
      }),
      "",
      "Return only the JSON edit plan required by your system contract. Later corrective instructions override earlier edits. Use a structured operation whenever it can express the request; use svg replacement only for genuinely custom path geometry."
    ].join("\n");
    ctx.run("luna_svg_edit", { ask: ask }).then(function (launch) {
      busy = false;
      if (!launch || launch.status !== "done") {
        var reason = launch && (launch.error || launch.status);
        setLunaStatus("Needs attention", "error");
        addMessage("luna", "I could not apply that change" + (reason ? ": " + reason : "."));
        return;
      }
      try {
        var output = launch.output || {};
        if (typeof output === "string") {
          try { output = JSON.parse(output); } catch (_) { output = { result: output }; }
        }
        var raw = output.result || output.reply || output.plan || output.svg || "";
        var plan = parseLunaPlan(raw);
        var result = applyLunaPlan(plan);
        recordLunaEdit(request, result, correction, "luna");
        setLunaStatus("Applied", "ready");
        addMessage("luna", appliedMessage(result));
        if (lunaPrompt) lunaPrompt.value = "";
      } catch (error) {
        setLunaStatus("Review source", "error");
        addMessage("luna", "The returned markup was rejected safely: " + error.message);
      }
    }).catch(function (error) {
      busy = false;
      setLunaStatus("Unavailable", "error");
      addMessage("luna", "Luna could not run: " + ((error && error.message) || "unknown error"));
    });
  }

  function viewBoxOf(svg) {
    var raw = String(svg.getAttribute("viewBox") || "0 0 512 512").trim().split(/[\s,]+/).map(Number);
    if (raw.length !== 4 || raw.some(function (n) { return !isFinite(n); })) return [0, 0, 512, 512];
    return raw;
  }

  function xmlEscape(value) {
    return String(value).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  }

  function exportEffect(size) {
    var effect = state.settings.effect;
    if (!effect || effect === "none") return { defs: "", attr: "" };
    var amount = clamp(state.settings.intensity, 0, 100);
    var screenWidth = 512;
    if (artboard && artboard.getBoundingClientRect) {
      var rect = artboard.getBoundingClientRect();
      if (rect && rect.width) screenWidth = rect.width / Math.max(.01, zoom);
    }
    var px = size / Math.max(1, screenWidth);
    var color = xmlEscape(state.settings.fill);
    var primitives = "";
    if (effect === "soft") {
      primitives = '<feDropShadow dx="0" dy="' + ((2 + amount * .08) * px).toFixed(2) +
        '" stdDeviation="' + ((2 + amount * .08) * px).toFixed(2) +
        '" flood-color="#0f172a" flood-opacity="' + (.08 + amount * .003).toFixed(3) + '"/>';
    } else if (effect === "hard") {
      var hard = (2 + amount * .08) * px;
      primitives = '<feDropShadow dx="' + hard.toFixed(2) + '" dy="' + hard.toFixed(2) +
        '" stdDeviation="0.01" flood-color="#0b1220" flood-opacity="' + (.35 + amount * .004).toFixed(3) + '"/>';
    } else if (effect === "glow") {
      primitives = '<feDropShadow dx="0" dy="0" stdDeviation="' + ((1.5 + amount * .09) * px).toFixed(2) +
        '" flood-color="' + color + '" flood-opacity=".92"/>';
    } else if (effect === "bloom") {
      primitives = '<feDropShadow dx="0" dy="0" stdDeviation="' + ((1 + amount * .06) * px).toFixed(2) +
        '" flood-color="#ffffff" flood-opacity=".9"/>' +
        '<feDropShadow dx="0" dy="0" stdDeviation="' + ((2.5 + amount * .09) * px).toFixed(2) +
        '" flood-color="' + color + '" flood-opacity=".78"/>';
    } else if (effect === "pressed") {
      primitives = '<feDropShadow dx="' + px.toFixed(2) + '" dy="' + (2 * px).toFixed(2) +
        '" stdDeviation=".01" flood-color="#ffffff" flood-opacity=".75"/>' +
        '<feDropShadow dx="' + (-px).toFixed(2) + '" dy="' + (-2 * px).toFixed(2) +
        '" stdDeviation="' + ((.5 + amount * .0175) * px).toFixed(2) +
        '" flood-color="#0b1220" flood-opacity=".25"/>';
    }
    return {
      defs: '<filter id="editor-export-effect" x="-60%" y="-60%" width="220%" height="220%" color-interpolation-filters="sRGB">' +
        primitives + "</filter>",
      attr: primitives ? ' filter="url(#editor-export-effect)"' : ""
    };
  }

  function exportSvgMarkup() {
    var source = parseSvg(state.svg);
    if (!source) throw new Error("SVG source is unavailable");
    applyPaint(source);
    installFilters(source);
    var vb = viewBoxOf(source);
    var size = Number(state.settings.canvasSize) || 1024;
    var centerX = vb[0] + vb[2] / 2;
    var centerY = vb[1] + vb[3] / 2;
    var selectionSpan = .74 * size;
    var fit = selectionSpan / Math.max(vb[2], vb[3]);
    var scale = fit * clamp(state.settings.scale, 10, 160) / 100;
    var x = size / 2 + (Number(state.settings.x) / 100) * selectionSpan;
    var y = size / 2 + (Number(state.settings.y) / 100) * selectionSpan;
    var warp = warpValues();
    var content = source.innerHTML;
    var radius = state.settings.shape === "circle" ? size / 2 : size * clamp(state.settings.corner, 0, 48) / 100;
    var clipShape = state.settings.shape === "circle" ?
      '<circle cx="' + size / 2 + '" cy="' + size / 2 + '" r="' + size / 2 + '"/>' :
      '<rect width="' + size + '" height="' + size + '" rx="' + radius + '"/>';
    var bg = "";
    if (!state.settings.transparent) {
      if (state.settings.shape === "circle") {
        bg = '<circle cx="' + size / 2 + '" cy="' + size / 2 + '" r="' + size / 2 + '" fill="' + xmlEscape(state.settings.background) + '"/>';
      } else {
        bg = '<rect width="' + size + '" height="' + size + '" rx="' + radius + '" fill="' + xmlEscape(state.settings.background) + '"/>';
      }
    }
    var effect = exportEffect(size);
    var warpFilter = "";
    if (state.settings.warp === "wave") warpFilter = ' filter="url(#editor-wave)"';
    if (state.settings.warp === "ripple") warpFilter = ' filter="url(#editor-ripple)"';
    if (state.settings.warp === "rough") warpFilter = ' filter="url(#editor-rough)"';
    var transform = [
      "translate(" + x + " " + y + ")",
      "rotate(" + Number(state.settings.rotation || 0) + ")",
      "skewX(" + warp.skew + ")",
      "scale(" + (scale * warp.wide) + " " + (scale * warp.tall) + ")",
      "translate(" + (-centerX) + " " + (-centerY) + ")"
    ].join(" ");
    return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + size + " " + size + '" width="' + size + '" height="' + size + '">' +
      '<defs><clipPath id="editor-canvas-clip">' + clipShape + "</clipPath>" + effect.defs + "</defs>" +
      '<g clip-path="url(#editor-canvas-clip)">' + bg +
      '<g opacity="' + (clamp(state.settings.opacity, 5, 100) / 100) + '" style="mix-blend-mode:' +
      xmlEscape(state.settings.blend || "normal") + '"' + effect.attr + ">" +
      '<g transform="' + transform + '"' + warpFilter + ">" + content + "</g></g></g></svg>";
  }

  function downloadBlob(blob, extension) {
    var link = document.createElement("a");
    var url = URL.createObjectURL(blob);
    link.href = url;
    link.download = sanitizeName(projectTitle && projectTitle.value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") + "." + extension;
    document.body.appendChild(link);
    link.click();
    link.remove();
    setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
  }

  function exportSvg() {
    try {
      downloadBlob(new Blob([exportSvgMarkup()], { type: "image/svg+xml;charset=utf-8" }), "svg");
      notify("SVG exported");
    } catch (error) {
      notify("Export failed: " + error.message, true);
    }
  }

  function exportPng() {
    try {
      var size = Number(state.settings.canvasSize) || 1024;
      var blob = new Blob([exportSvgMarkup()], { type: "image/svg+xml;charset=utf-8" });
      var url = URL.createObjectURL(blob);
      var image = new window.Image();
      image.onload = function () {
        var canvas = document.createElement("canvas");
        canvas.width = size;
        canvas.height = size;
        var context = canvas.getContext("2d");
        context.drawImage(image, 0, 0, size, size);
        URL.revokeObjectURL(url);
        canvas.toBlob(function (png) {
          if (!png) {
            notify("PNG export failed", true);
            return;
          }
          downloadBlob(png, "png");
          notify("PNG exported");
        }, "image/png");
      };
      image.onerror = function () {
        URL.revokeObjectURL(url);
        notify("PNG export failed", true);
      };
      image.src = url;
    } catch (error) {
      notify("PNG export failed: " + error.message, true);
    }
  }

  function importFile(file) {
    if (!file) return;
    if (file.size > 400000) {
      notify("SVG must be under 400 KB", true);
      return;
    }
    var reader = new window.FileReader();
    reader.onload = function () {
      try {
        var safe = sanitizeSvg(reader.result);
        checkpoint();
        state.svg = safe;
        if (projectTitle && (!projectTitle.value || projectTitle.value === "Untitled logo")) {
          projectTitle.value = String(file.name || "Imported logo").replace(/\.svg$/i, "");
        }
        renderSvg();
        markDirty();
        notify("SVG imported");
      } catch (error) {
        notify(error.message, true);
      }
    };
    reader.readAsText(file);
  }

  function updateSetting(input, final) {
    var key = input.getAttribute("data-setting");
    if (!key || !(key in state.settings)) return;
    if (final && inputCheckpoint) {
      checkpoint(inputCheckpoint);
      inputCheckpoint = null;
    }
    var value = input.type === "checkbox" ? input.checked : input.value;
    if (["canvasSize", "corner", "x", "y", "scale", "rotation", "strokeWidth", "opacity", "curveAmount", "curveEmphasis", "warpAmount", "intensity"].indexOf(key) >= 0) {
      value = Number(value);
    }
    state.settings[key] = value;
    if (["curveDirection", "curveAmount", "curveEmphasis", "curveTangent"].indexOf(key) >= 0) {
      applyManualCurve();
    } else if (key === "fill" || key === "stroke" || key === "strokeWidth" || key === "warp" || key === "warpAmount") {
      renderSvg();
    }
    applyView();
    markDirty();
  }

  function applyManualCurve() {
    var svg = parseSvg(state.svg);
    if (!svg) return;
    if (state.settings.curveDirection === "straight") {
      var removed = unwrapArch(svg);
      if (!removed) normalizeLegacyArch(svg);
    } else {
      applyArch(svg, {
        amount: clamp(state.settings.curveAmount, 8, 100) / 100,
        direction: state.settings.curveDirection,
        tangent: !!state.settings.curveTangent,
        centerScale: clamp(state.settings.curveEmphasis, 0, 24) / 100
      });
    }
    state.svg = sanitizeSvg(svg.outerHTML);
    renderSvg();
  }

  function centerLogo() {
    checkpoint();
    state.settings.x = 0;
    state.settings.y = 0;
    state.settings.rotation = 0;
    syncControls();
    markDirty();
  }

  function setZoom(next) {
    zoom = clamp(next, .5, 1.6);
    applyView();
  }

  function beginDrag(event, resize) {
    if (!artboard || !selection || event.button !== 0) return;
    event.preventDefault();
    if (stage) stage.focus();
    var start = { x: event.clientX, y: event.clientY };
    var before = snapshot();
    var baseX = Number(state.settings.x);
    var baseY = Number(state.settings.y);
    var baseScale = Number(state.settings.scale);
    var rect = artboard.getBoundingClientRect();
    var center = { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
    var startDistance = Math.max(1, Math.hypot(event.clientX - center.x, event.clientY - center.y));
    var pointerId = event.pointerId;
    try { selection.setPointerCapture(pointerId); } catch (_) {}

    function move(moveEvent) {
      if (resize) {
        var distance = Math.max(1, Math.hypot(moveEvent.clientX - center.x, moveEvent.clientY - center.y));
        state.settings.scale = clamp(baseScale * distance / startDistance, 10, 160);
      } else {
        var nextX = clamp(baseX + (moveEvent.clientX - start.x) / rect.width * 100, -50, 50);
        var nextY = clamp(baseY + (moveEvent.clientY - start.y) / rect.height * 100, -50, 50);
        var guideX = false;
        var guideY = false;
        if (snapOn) {
          [-25, 0, 25].forEach(function (point) {
            if (Math.abs(nextX - point) < 1.3) { nextX = point; guideY = true; }
            if (Math.abs(nextY - point) < 1.3) { nextY = point; guideX = true; }
          });
        }
        state.settings.x = Math.round(nextX * 10) / 10;
        state.settings.y = Math.round(nextY * 10) / 10;
        ctx.setData("guide-x", guideX ? "true" : "false");
        ctx.setData("guide-y", guideY ? "true" : "false");
      }
      syncControls();
      markDirty();
    }

    function end() {
      checkpoint(before);
      ctx.setData("guide-x", "false");
      ctx.setData("guide-y", "false");
      window.removeEventListener("pointermove", move);
      window.removeEventListener("pointerup", end);
      window.removeEventListener("pointercancel", end);
      try { selection.releasePointerCapture(pointerId); } catch (_) {}
    }

    window.addEventListener("pointermove", move);
    window.addEventListener("pointerup", end);
    window.addEventListener("pointercancel", end);
  }

  function handleAction(action) {
    if (action === "undo") undo();
    if (action === "redo") redo();
    if (action === "save") saveProject();
    if (action === "new-project") newProject(false);
    if (action === "import" && fileInput) fileInput.click();
    if (action === "export-svg") exportSvg();
    if (action === "export-png") exportPng();
    if (action === "zoom-in") setZoom(zoom + .1);
    if (action === "zoom-out") setZoom(zoom - .1);
    if (action === "zoom-reset") setZoom(1);
    if (action === "center-logo") centerLogo();
    if (action === "toggle-grid") {
      gridOn = !gridOn;
      var gridButton = q('[data-action="toggle-grid"]');
      if (gridButton) gridButton.setAttribute("aria-pressed", gridOn ? "true" : "false");
      applyView();
    }
    if (action === "toggle-snap") {
      snapOn = !snapOn;
      var snapButton = q('[data-action="toggle-snap"]');
      if (snapButton) snapButton.setAttribute("aria-pressed", snapOn ? "true" : "false");
      applyView();
    }
    if (action === "apply-code") applyCode();
    if (action === "format-code") formatCode();
    if (action === "run-luna") runLuna();
  }

  ctx.on(root, "click", function (event) {
    var tab = event.target.closest && event.target.closest("[data-tab]");
    if (tab) {
      setTab(tab.getAttribute("data-tab"));
      return;
    }
    var shape = event.target.closest && event.target.closest("[data-shape-action]");
    if (shape) {
      checkpoint();
      state.settings.shape = shape.getAttribute("data-shape-action");
      syncControls();
      markDirty();
      return;
    }
    var preset = event.target.closest && event.target.closest("[data-preset]");
    if (preset) {
      applyPreset(preset.getAttribute("data-preset"));
      return;
    }
    var suggestion = event.target.closest && event.target.closest("[data-suggestion]");
    if (suggestion && lunaPrompt) {
      lunaPrompt.value = suggestion.getAttribute("data-suggestion");
      lunaPrompt.focus();
      return;
    }
    var projectDelete = event.target.closest && event.target.closest("[data-project-delete]");
    if (projectDelete) {
      event.preventDefault();
      event.stopPropagation();
      deleteProject(projectDelete.getAttribute("data-project-delete"));
      return;
    }
    var project = event.target.closest && event.target.closest("[data-project]");
    if (project) {
      loadProject(project.getAttribute("data-project"));
      return;
    }
    var action = event.target.closest && event.target.closest("[data-action]");
    if (action) handleAction(action.getAttribute("data-action"));
  });

  ctx.on(root, "focusin", function (event) {
    if (event.target && event.target.hasAttribute && event.target.hasAttribute("data-setting")) inputCheckpoint = snapshot();
  });

  ctx.on(root, "input", function (event) {
    if (event.target && event.target.hasAttribute && event.target.hasAttribute("data-setting")) updateSetting(event.target, false);
    if (event.target === codeEditor) validateCode(false);
    if (event.target === projectTitle) markDirty();
  });

  ctx.on(root, "change", function (event) {
    if (event.target && event.target.hasAttribute && event.target.hasAttribute("data-setting")) updateSetting(event.target, true);
    if (event.target === fileInput) {
      var file = fileInput.files && fileInput.files[0];
      fileInput.value = "";
      importFile(file);
    }
  });

  ctx.on(selection, "pointerdown", function (event) {
    var handle = event.target.closest && event.target.closest("[data-handle]");
    beginDrag(event, !!handle);
  });

  ctx.on(stage, "dragover", function (event) {
    event.preventDefault();
  });

  ctx.on(stage, "drop", function (event) {
    event.preventDefault();
    var file = event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0];
    importFile(file);
  });

  ctx.on(stage, "keydown", function (event) {
    if (["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].indexOf(event.key) < 0) return;
    event.preventDefault();
    checkpoint();
    var step = event.shiftKey ? 5 : 1;
    if (event.key === "ArrowLeft") state.settings.x = clamp(state.settings.x - step, -50, 50);
    if (event.key === "ArrowRight") state.settings.x = clamp(state.settings.x + step, -50, 50);
    if (event.key === "ArrowUp") state.settings.y = clamp(state.settings.y - step, -50, 50);
    if (event.key === "ArrowDown") state.settings.y = clamp(state.settings.y + step, -50, 50);
    syncControls();
    markDirty();
  });

  ctx.on(lunaPrompt, "keydown", function (event) {
    if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
      event.preventDefault();
      runLuna();
    }
  });

  ctx.on(window, "keydown", function (event) {
    var mod = event.metaKey || event.ctrlKey;
    if (!mod) return;
    if (event.key.toLowerCase() === "s") {
      event.preventDefault();
      saveProject();
    }
    if (event.key.toLowerCase() === "z" && event.shiftKey) {
      event.preventDefault();
      redo();
    } else if (event.key.toLowerCase() === "z") {
      event.preventDefault();
      undo();
    }
  });

  renderPresets();
  syncControls();
  updateUndoButtons();
  setTab("design");
  addMessage("luna", "I’m ready. Common logo edits apply instantly; custom edits use project history and a precise SVG operation plan.");

  ctx.readSet("logo_projects").then(renderProjects).catch(function () {
    renderProjects([]);
  });
  ctx.onSet("logo_projects", renderProjects);

  ctx.cleanup(function () {
    if (toastTimer) clearTimeout(toastTimer);
  });
}
