(() => {
  const instances = new WeakMap();
  const HISTORY_LIMIT = 40;
  const REQUEST_HISTORY_LIMIT = 12;
  const MIN_HEIGHT = 180;

  function mount(root) {
    if (!root || instances.has(root)) return;
    const endpoint = root.dataset.endpoint;
    const userId = root.dataset.userId || "anonymous";
    const panel = root.querySelector("[data-lumi-ai-panel]");
    const toggle = root.querySelector("[data-lumi-ai-toggle]");
    const close = root.querySelector("[data-lumi-ai-close]");
    const clear = root.querySelector("[data-lumi-ai-clear]");
    const resizeHandle = root.querySelector("[data-lumi-ai-resize]");
    const state = root.querySelector("[data-lumi-ai-state]");
    const status = root.querySelector("[data-lumi-ai-status]");
    const messages = root.querySelector("[data-lumi-ai-messages]");
    const form = root.querySelector("[data-lumi-ai-form]");
    const input = form?.querySelector("textarea");
    const submit = form?.querySelector("[data-lumi-ai-submit]");
    const cooldown = form?.querySelector("[data-lumi-ai-cooldown]");
    if (!endpoint || !panel || !toggle || !close || !clear || !resizeHandle || !state || !status || !messages || !form || !input || !submit) return;

    const listeners = new AbortController();
    const requests = new Set();
    const overlayRoot = document.createElement("div");
    overlayRoot.className = "lumi-ai-overlay-root";
    overlayRoot.dataset.lumiAiOverlayRoot = "";
    document.body.append(overlayRoot);
    overlayRoot.append(panel);
    const storageKey = `lumi_ai.chat.${userId}`;
    const stateKey = `lumi_ai.panel.${userId}`;
    let conversation = loadJson(storageKey, []);
    let panelState = loadJson(stateKey, {});
    let statusTimer = null;
    let cooldownTimer = null;
    let cooldownUntil = 0;
    let requestInFlight = false;

    const trackedFetch = async (url, options = {}) => {
      const controller = new AbortController();
      requests.add(controller);
      try {
        return await fetch(url, { ...options, signal: controller.signal });
      } finally {
        requests.delete(controller);
      }
    };

    const persistConversation = () => {
      conversation = conversation.slice(-HISTORY_LIMIT);
      saveJson(storageKey, conversation);
    };

    const persistPanelState = (next = {}) => {
      panelState = { ...panelState, ...next };
      saveJson(stateKey, panelState);
    };

    const panelHeight = () => {
      const fallback = Math.max(MIN_HEIGHT, Math.round(window.innerHeight / 6));
      const stored = Number(panelState.height);
      return Number.isFinite(stored) && stored >= MIN_HEIGHT ? stored : fallback;
    };

    const positionPanel = (height = panelHeight()) => {
      const maximum = Math.max(MIN_HEIGHT, window.innerHeight - 96);
      const clampedHeight = Math.min(maximum, Math.max(MIN_HEIGHT, height));
      panel.style.height = `${clampedHeight}px`;
      return clampedHeight;
    };

    const setOpen = (open, persist = true) => {
      if (open) positionPanel();
      panel.classList.toggle("open", open);
      panel.setAttribute("aria-hidden", String(!open));
      toggle.setAttribute("aria-expanded", String(open));
      if (persist) persistPanelState({ open });
      if (open) {
        input.focus();
        messages.scrollTop = messages.scrollHeight;
      }
    };

    const addMessage = (text, type, confirmation = null, links = [], persist = true, feedbackContext = null) => {
      const item = document.createElement("div");
      item.className = `lumi-ai-message ${type}`;
      if (type === "assistant") renderMarkdown(item, text);
      else {
        const body = document.createElement("div");
        body.textContent = text;
        body.style.whiteSpace = "pre-wrap";
        item.append(body);
      }
      appendLinks(item, links);
      if (confirmation) appendConfirmation(item, confirmation);
      if (type === "assistant" && feedbackContext) appendFeedback(item, feedbackContext);
      messages.append(item);
      messages.scrollTop = messages.scrollHeight;
      if (persist && ["user", "assistant"].includes(type)) {
        conversation.push({ role: type, content: String(text || ""), links: safeStoredLinks(links) });
        persistConversation();
      }
    };

    const addPending = () => {
      const item = document.createElement("div");
      item.className = "lumi-ai-message assistant pending";
      item.setAttribute("role", "status");
      const spinner = document.createElement("span");
      spinner.className = "lumi-ai-spinner";
      spinner.setAttribute("aria-hidden", "true");
      const label = document.createElement("span");
      label.textContent = "Queued for Lumi Assistant...";
      const progress = document.createElement("div");
      progress.className = "lumi-ai-pending-progress";
      progress.append(label);
      const controls = document.createElement("div");
      controls.className = "lumi-ai-timeout-controls";
      controls.hidden = true;
      const details = document.createElement("div");
      details.className = "lumi-ai-timeout-details";
      details.hidden = true;
      const buttons = {};
      for (const [key, text] of [
        ["continue", "Continue waiting"],
        ["cancel", "Cancel"],
        ["details", "Details"]
      ]) {
        const button = document.createElement("button");
        button.type = "button";
        button.textContent = text;
        button.dataset.timeoutAction = key;
        buttons[key] = button;
        controls.append(button);
      }
      progress.append(controls, details);
      item.append(spinner, progress);
      messages.append(item);
      messages.scrollTop = messages.scrollHeight;
      let latestJob = null;
      return {
        setStage(stage, job = null) {
          latestJob = job || latestJob;
          const labels = {
            queued: "Queued for Lumi Assistant...",
            deterministic: "Checking verified answers...",
            gating: "Routing with the lightweight gate...",
            gate: "Routing with the lightweight gate...",
            main_model_loading: "Loading the main model...",
            prompt_eval: "Evaluating the prompt...",
            generating: "Main model is generating...",
            formatting: "Formatting the reply...",
            done: "Reply complete.",
            cancelled: "Request cancelled."
          };
          const baseLabel = labels[stage] || "Lumi Assistant is processing...";
          const elapsed = latestJob?.elapsed_ms ? ` · ${formatElapsed(latestJob.elapsed_ms)}` : "";
          const mode = latestJob?.details?.controller_complexity;
          const retrieval = latestJob?.details?.okf_retrieval_depth;
          const routingText = mode && ["prompt_eval", "generating"].includes(stage)
            ? ` · ${mode}${retrieval ? `/${retrieval}` : ""}`
            : "";
          label.textContent = `${baseLabel}${elapsed}${routingText}`;
          if (!details.hidden && latestJob) this.updateDetails(latestJob);
        },
        showSoftTimeout(actions) {
          controls.hidden = false;
          buttons.continue.onclick = actions.continueWaiting;
          buttons.cancel.onclick = actions.cancel;
          buttons.details.onclick = () => {
            details.hidden = !details.hidden;
            if (!details.hidden && latestJob) this.updateDetails(latestJob);
          };
          updateCooldown();
        },
        hideSoftTimeout() {
          controls.hidden = true;
          details.hidden = true;
        },
        updateDetails(job) {
          latestJob = job;
          const jobDetails = job.details || {};
          const generated = Number(jobDetails.generated_tokens) || 0;
          details.textContent = [
            `Stage: ${job.stage || "unknown"}`,
            `Elapsed: ${formatElapsed(job.elapsed_ms)}`,
            `Generated tokens: ${generated || "not reported"}`,
            `Job alive: ${job.still_running ? "yes" : "no"}`
          ].join(" | ");
        },
        remove() {
          item.remove();
        }
      };
    };

    const cooldownSeconds = () => Math.max(0, Math.ceil((cooldownUntil - Date.now()) / 1000));
    const updateCooldown = () => {
      const seconds = cooldownSeconds();
      const active = seconds > 0;
      submit.disabled = requestInFlight || active;
      submit.title = active ? `Retry available in ${seconds}s` : "Send";
      if (cooldown) {
        cooldown.hidden = !active;
        cooldown.textContent = active ? `Retry available in ${seconds}s` : "";
      }
      for (const button of messages.querySelectorAll(".lumi-ai-retry[data-cooldown]")) {
        button.disabled = active;
        button.textContent = active ? `Retry in ${seconds}s` : "Retry";
      }
      if (!active && cooldownTimer) {
        window.clearInterval(cooldownTimer);
        cooldownTimer = null;
      }
    };
    const beginCooldown = (seconds) => {
      const duration = Math.max(1, Number(seconds) || 1);
      cooldownUntil = Math.max(cooldownUntil, Date.now() + duration * 1000);
      if (!cooldownTimer) cooldownTimer = window.setInterval(updateCooldown, 1000);
      updateCooldown();
    };

    const addError = (text, retry = null, retryAfterSeconds = 0) => {
      const item = document.createElement("div");
      item.className = "lumi-ai-message assistant error";
      item.setAttribute("role", "alert");
      const body = document.createElement("div");
      body.textContent = text || "Lumi Assistant could not complete the request.";
      item.append(body);
      if (typeof retry === "function") {
        const button = document.createElement("button");
        button.type = "button";
        button.className = "lumi-ai-retry";
        button.textContent = "Retry";
        if (retryAfterSeconds > 0) button.dataset.cooldown = "true";
        button.addEventListener("click", () => {
          if (cooldownSeconds() > 0) return;
          item.remove();
          retry();
        }, { once: true, signal: listeners.signal });
        item.append(button);
        updateCooldown();
      }
      messages.append(item);
      messages.scrollTop = messages.scrollHeight;
    };

    const appendConfirmation = (item, confirmation) => {
      const actions = document.createElement("div");
      actions.className = "lumi-ai-confirm";
      for (const [label, route] of [["Confirm", "confirm"], ["Cancel", "cancel"]]) {
        const button = document.createElement("button");
        button.type = "button";
        button.textContent = label;
        button.addEventListener("click", async () => {
          button.disabled = true;
          try {
            const response = await trackedFetch(`${endpoint}/assistant/${route}`, {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({ id: confirmation.id })
            });
            const data = await response.json();
            if (!response.ok) throw new Error(data.error || "Action failed.");
            addMessage(route === "confirm" ? "Confirmed and completed." : "Cancelled.", "assistant");
            actions.remove();
          } catch (error) {
            if (error.name !== "AbortError") addMessage(error.message, "assistant error", null, [], false);
          }
        }, { signal: listeners.signal });
        actions.append(button);
      }
      item.append(actions);
    };

    const appendFeedback = (item, context) => {
      const controls = document.createElement("form");
      controls.className = "lumi-ai-feedback";
      controls.setAttribute("aria-label", "Rate this Lumi Assistant reply");
      const quickActions = document.createElement("div");
      quickActions.className = "lumi-ai-feedback-quick-actions";
      const helpful = document.createElement("button");
      helpful.type = "button";
      helpful.className = "button subtle";
      helpful.textContent = "Helpful";
      helpful.setAttribute("aria-label", "Mark this reply as helpful");
      const needsWork = document.createElement("button");
      needsWork.type = "button";
      needsWork.className = "button subtle";
      needsWork.textContent = "Needs work";
      needsWork.setAttribute("aria-label", "Tell Lumi what needs work");
      quickActions.append(helpful, needsWork);
      const details = document.createElement("div");
      details.className = "lumi-ai-feedback-details";
      details.hidden = true;
      const select = document.createElement("select");
      select.setAttribute("aria-label", "Feedback tag");
      for (const tag of [
        "bad",
        "bad_retrieval",
        "bad_response",
        "missing_knowledge",
        "wrong_link",
        "hallucinated",
        "too_generic",
        "unsafe",
        "should_clarify",
        "bad_code",
        "wrong_scope",
        "wrong_tool_usage"
      ]) {
        const option = document.createElement("option");
        option.value = tag;
        option.textContent = tag.replaceAll("_", " ");
        select.append(option);
      }
      const kind = document.createElement("select");
      kind.setAttribute("aria-label", "Feedback type");
      for (const [value, label] of [
        ["instruction_based", "Instruction-based guidance"],
        ["strict_correction", "Strict correction"]
      ]) {
        const option = document.createElement("option");
        option.value = value;
        option.textContent = label;
        kind.append(option);
      }
      const correction = document.createElement("input");
      correction.maxLength = 16000;
      correction.placeholder = "Correction or instruction for future replies";
      correction.setAttribute("aria-label", "Correction or instruction");
      const submitFeedback = document.createElement("button");
      submitFeedback.type = "submit";
      submitFeedback.textContent = "Send feedback";
      details.append(select, kind, correction, submitFeedback);
      controls.append(quickActions, details);
      const sendFeedback = async ({ tag, rating, correctionText = "", feedbackKind = "instruction_based" }) => {
        helpful.disabled = true;
        needsWork.disabled = true;
        submitFeedback.disabled = true;
        try {
          const response = await trackedFetch(`${endpoint}/assistant/feedback`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
              ...context,
              rating,
              feedback_tag: tag,
              feedback_kind: feedbackKind,
              optional_correction: correctionText
            })
          });
          const data = await readResponseJson(response);
          if (!response.ok) throw new Error(data.error || "Feedback could not be saved.");
          controls.replaceChildren(document.createTextNode("Thanks—feedback saved."));
        } catch (error) {
          helpful.disabled = false;
          needsWork.disabled = false;
          submitFeedback.disabled = false;
          submitFeedback.textContent = error.message || "Try again";
        }
      };
      helpful.addEventListener("click", () => sendFeedback({ tag: "good", rating: "up" }), { signal: listeners.signal });
      needsWork.addEventListener("click", () => {
        details.hidden = false;
        needsWork.setAttribute("aria-expanded", "true");
        select.focus();
      }, { signal: listeners.signal });
      controls.addEventListener("submit", async (event) => {
        event.preventDefault();
        await sendFeedback({
          tag: select.value,
          rating: "down",
          correctionText: correction.value.trim(),
          feedbackKind: kind.value
        });
      }, { signal: listeners.signal });
      item.append(controls);
    };

    const restoreConversation = () => {
      messages.replaceChildren();
      for (const entry of conversation.slice(-HISTORY_LIMIT)) {
        if (!["user", "assistant"].includes(entry?.role) || !entry.content) continue;
        addMessage(entry.content, entry.role, null, entry.links || [], false);
      }
      if (!messages.childElementCount) {
        addMessage(
          "Ask about Lumi Bot, its WebUI, plugins, settings, streams, moderation, or community systems.",
          "assistant",
          null,
          [],
          false
        );
      }
    };

    const refreshStatus = async () => {
      try {
        const response = await trackedFetch(`${endpoint}/api/status`);
        const data = await response.json();
        const ready = response.ok && data.enabled && data.runtime?.healthy;
        const cold = response.ok && data.enabled && data.runtime?.runtime_installed &&
          data.runtime?.model_downloaded && data.runtime?.state === "stopped";
        state.className = `lumi-ai-state ${ready ? "ready" : cold ? "warming" : "error"}`;
        status.textContent = ready
          ? `Main ready · gate ${data.gate?.healthy ? "ready" : "fallback"}`
          : cold
            ? `Main loads on request · gate ${data.gate?.healthy ? "ready" : "starting"}`
          : "Runtime unavailable";
      } catch (error) {
        if (error.name !== "AbortError") {
          state.className = "lumi-ai-state error";
          status.textContent = "Status unavailable";
        }
      }
    };

    toggle.addEventListener("click", () => setOpen(!panel.classList.contains("open")), { signal: listeners.signal });
    close.addEventListener("click", () => setOpen(false), { signal: listeners.signal });
    clear.addEventListener("click", () => {
      conversation = [];
      persistConversation();
      restoreConversation();
    }, { signal: listeners.signal });
    resizeHandle.addEventListener("pointerdown", (event) => {
      event.preventDefault();
      resizeHandle.setPointerCapture(event.pointerId);
      const startY = event.clientY;
      const startHeight = panel.getBoundingClientRect().height;
      const move = (moveEvent) => {
        const nextHeight = positionPanel(startHeight + startY - moveEvent.clientY);
        persistPanelState({ height: Math.round(nextHeight) });
      };
      const stop = () => {
        resizeHandle.removeEventListener("pointermove", move);
        resizeHandle.removeEventListener("pointerup", stop);
        resizeHandle.removeEventListener("pointercancel", stop);
      };
      resizeHandle.addEventListener("pointermove", move);
      resizeHandle.addEventListener("pointerup", stop);
      resizeHandle.addEventListener("pointercancel", stop);
    }, { signal: listeners.signal });

    const sendMessage = async (message, history, addUserMessage = true) => {
      if (addUserMessage) addMessage(message, "user");
      requestInFlight = true;
      input.disabled = true;
      submit.disabled = true;
      messages.setAttribute("aria-busy", "true");
      const pending = addPending();
      try {
        const response = await trackedFetch(`${endpoint}/assistant/message`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ message, history })
        });
        const data = await readResponseJson(response);
        if (!response.ok) {
          const error = new Error(data.error || `Request failed (${response.status}).`);
          error.status = response.status;
          error.retryAfterSeconds = Number(data.retry_after_seconds) || 0;
          throw error;
        }
        const result = response.status === 202 && data.status_url
          ? await pollAssistantJob(data, pending)
          : data;
        addMessage(result.text, "assistant", result.confirmation, result.links, true, result.feedback_context);
      } catch (error) {
        if (error.name === "AbortError") {
          if (root.isConnected) {
            addError("Assistant request was cancelled.", () => sendMessage(message, history, false));
          }
        } else {
          const retrySafe = ![400, 401, 403].includes(error.status);
          if (error.status === 429 && error.retryAfterSeconds > 0) {
            beginCooldown(error.retryAfterSeconds);
          }
          addError(
            error.message,
            retrySafe ? () => sendMessage(message, history, false) : null,
            error.retryAfterSeconds
          );
        }
      } finally {
        pending.remove();
        requestInFlight = false;
        if (root.isConnected) {
          input.disabled = false;
          updateCooldown();
          messages.setAttribute("aria-busy", "false");
          input.focus();
        }
      }
    };

    const pollAssistantJob = async (jobRequest, pending) => {
      const softTimeoutMs = Math.max(5000, Number(jobRequest.ui_soft_timeout_ms) || 45000);
      let nextSoftTimeoutAt = Date.now() + softTimeoutMs;
      let softTimeoutReported = false;
      let requestedAction = null;
      while (root.isConnected) {
        if (requestedAction === "continue") {
          pending.hideSoftTimeout();
          nextSoftTimeoutAt = Date.now() + softTimeoutMs;
          requestedAction = null;
        } else if (requestedAction === "cancel") {
          requestedAction = null;
          const cancelResponse = await trackedFetch(jobRequest.cancel_url, { method: "POST" });
          const cancelled = await readResponseJson(cancelResponse);
          if (!cancelResponse.ok) {
            const error = new Error(cancelled.error || "Could not cancel the running request.");
            error.status = cancelResponse.status;
            throw error;
          }
        }
        const response = await trackedFetch(jobRequest.status_url, { cache: "no-store" });
        const job = await readResponseJson(response);
        if (!response.ok) {
          const error = new Error(job.error || `Request status failed (${response.status}).`);
          error.status = response.status;
          throw error;
        }
        pending.setStage(job.stage, job);
        pending.updateDetails(job);
        if (job.state === "complete") return job.result || {};
        if (["error", "cancelled"].includes(job.state)) {
          const error = new Error(job.error || "Lumi Assistant could not complete the request.");
          if (job.state === "cancelled") error.name = "AbortError";
          error.status = job.retry_after_seconds ? 429 : 503;
          error.retryAfterSeconds = Number(job.retry_after_seconds) || 0;
          throw error;
        }
        if (Date.now() >= nextSoftTimeoutAt) {
          pending.showSoftTimeout({
            continueWaiting: () => { requestedAction = "continue"; },
            cancel: () => { requestedAction = "cancel"; }
          });
          if (!softTimeoutReported && jobRequest.soft_timeout_url) {
            softTimeoutReported = true;
            trackedFetch(jobRequest.soft_timeout_url, { method: "POST" }).catch(() => {});
          }
          nextSoftTimeoutAt = Number.POSITIVE_INFINITY;
        }
        await new Promise((resolve) => window.setTimeout(resolve, 750));
      }
      throw Object.assign(new Error("Assistant panel closed."), { name: "AbortError" });
    };

    form.addEventListener("submit", async (event) => {
      event.preventDefault();
      const message = input.value.trim();
      if (!message || input.disabled || cooldownSeconds() > 0) return;
      const history = conversation.slice(-REQUEST_HISTORY_LIMIT).map((entry) => ({
        role: entry.role,
        content: entry.content
      }));
      input.value = "";
      await sendMessage(message, history);
    }, { signal: listeners.signal });
    input.addEventListener("keydown", (event) => {
      if (event.key === "Enter" && !event.shiftKey) {
        event.preventDefault();
        form.requestSubmit();
      }
    }, { signal: listeners.signal });
    messages.addEventListener("wheel", (event) => {
      event.stopPropagation();
      const atTop = messages.scrollTop <= 0;
      const atBottom = Math.ceil(messages.scrollTop + messages.clientHeight) >= messages.scrollHeight;
      if ((event.deltaY < 0 && atTop) || (event.deltaY > 0 && atBottom)) event.preventDefault();
    }, { passive: false, signal: listeners.signal });
    window.addEventListener("resize", () => {
      if (panel.classList.contains("open")) {
        const height = positionPanel();
        if (height !== panelState.height) persistPanelState({ height: Math.round(height) });
      }
    }, { signal: listeners.signal });

    restoreConversation();
    positionPanel();
    setOpen(panelState.open === true, false);
    refreshStatus();
    statusTimer = window.setInterval(refreshStatus, 15000);
    instances.set(root, {
      destroy() {
        listeners.abort();
        for (const request of requests) request.abort();
        requests.clear();
        if (statusTimer) window.clearInterval(statusTimer);
        if (cooldownTimer) window.clearInterval(cooldownTimer);
        overlayRoot.remove();
        instances.delete(root);
      }
    });
  }

  function renderMarkdown(container, value) {
    const lines = String(value || "").replace(/\r\n/g, "\n").split("\n");
    let paragraph = [];
    let list = null;
    let listType = null;
    const flushParagraph = () => {
      if (!paragraph.length) return;
      const p = document.createElement("p");
      appendInlineMarkdown(p, paragraph.join("\n"));
      container.append(p);
      paragraph = [];
    };
    const flushList = () => {
      if (list) container.append(list);
      list = null;
      listType = null;
    };
    for (let index = 0; index < lines.length; index += 1) {
      const fence = lines[index].match(/^```([a-z0-9_+-]*)\s*$/i);
      if (fence) {
        flushParagraph();
        flushList();
        const code = [];
        index += 1;
        while (index < lines.length && !/^```\s*$/.test(lines[index])) {
          code.push(lines[index]);
          index += 1;
        }
        container.append(createCodeBlock(code.join("\n"), fence[1] || "text"));
        continue;
      }
      const item = lines[index].match(/^\s*[-*]\s+(.+)$/);
      const orderedItem = lines[index].match(/^\s*\d+\.\s+(.+)$/);
      if (item || orderedItem) {
        flushParagraph();
        const nextListType = orderedItem ? "ol" : "ul";
        if (list && listType !== nextListType) flushList();
        if (!list) {
          list = document.createElement(nextListType);
          listType = nextListType;
        }
        const li = document.createElement("li");
        appendInlineMarkdown(li, (item || orderedItem)[1]);
        list.append(li);
        continue;
      }
      const heading = lines[index].match(/^(#{1,3})\s+(.+)$/);
      if (heading) {
        flushParagraph();
        flushList();
        const element = document.createElement(`h${heading[1].length}`);
        appendInlineMarkdown(element, heading[2]);
        container.append(element);
        continue;
      }
      const quote = lines[index].match(/^>\s?(.+)$/);
      if (quote) {
        flushParagraph();
        flushList();
        const blockquote = document.createElement("blockquote");
        appendInlineMarkdown(blockquote, quote[1]);
        container.append(blockquote);
        continue;
      }
      if (!lines[index].trim()) {
        flushParagraph();
        flushList();
        continue;
      }
      flushList();
      paragraph.push(lines[index]);
    }
    flushParagraph();
    flushList();
  }

  function formatElapsed(milliseconds) {
    const seconds = Math.max(0, Math.floor((Number(milliseconds) || 0) / 1000));
    const minutes = Math.floor(seconds / 60);
    return minutes ? `${minutes}m ${seconds % 60}s` : `${seconds}s`;
  }

  function appendInlineMarkdown(parent, value) {
    const pattern = /(<a\b[^>]*\bhref\s*=\s*(["'])(.*?)\2[^>]*>(.*?)<\/a>|`[^`\n]+`|\*\*[^*]+\*\*|_[^_\n]+_|\[[^\]]+\]\([^)]+\))/gi;
    let offset = 0;
    for (const match of String(value).matchAll(pattern)) {
      parent.append(document.createTextNode(value.slice(offset, match.index)));
      const token = match[0];
      if (token.startsWith("`")) {
        const code = document.createElement("code");
        code.textContent = token.slice(1, -1);
        parent.append(code);
      } else if (token.startsWith("**")) {
        const strong = document.createElement("strong");
        strong.textContent = token.slice(2, -2);
        parent.append(strong);
      } else if (token.startsWith("_")) {
        const emphasis = document.createElement("em");
        emphasis.textContent = token.slice(1, -1);
        parent.append(emphasis);
      } else if (/^<a\b/i.test(token)) {
        const parts = token.match(/^<a\b[^>]*\bhref\s*=\s*(["'])(.*?)\1[^>]*>(.*?)<\/a>$/i);
        const anchor = safeAnchor(parts?.[2], stripHtml(parts?.[3]));
        parent.append(anchor || document.createTextNode(stripHtml(parts?.[3]) || token));
      } else {
        const parts = token.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
        const anchor = safeAnchor(parts?.[2], parts?.[1]);
        parent.append(anchor || document.createTextNode(parts?.[1] || token));
      }
      offset = match.index + token.length;
    }
    parent.append(document.createTextNode(value.slice(offset)));
  }

  function createCodeBlock(codeValue, language) {
    const wrapper = document.createElement("div");
    wrapper.className = "lumi-ai-code";
    const header = document.createElement("div");
    header.className = "lumi-ai-code-header";
    const label = document.createElement("span");
    label.textContent = language;
    const copy = document.createElement("button");
    copy.type = "button";
    copy.className = "lumi-ai-code-copy";
    copy.textContent = "Copy";
    copy.title = "Copy code";
    copy.addEventListener("click", async () => {
      try {
        await copyText(codeValue);
        copy.textContent = "Copied";
      } catch {
        copy.textContent = "Copy failed";
      }
      window.setTimeout(() => { copy.textContent = "Copy"; }, 1200);
    });
    const pre = document.createElement("pre");
    const code = document.createElement("code");
    code.className = `language-${language}`;
    code.textContent = codeValue;
    pre.append(code);
    header.append(label, copy);
    wrapper.append(header, pre);
    return wrapper;
  }

  function appendLinks(item, links) {
    if (!Array.isArray(links) || !links.length) return;
    const linkList = document.createElement("div");
    linkList.className = "lumi-ai-links";
    for (const link of links) {
      const anchor = safeAnchor(link?.href, link?.label);
      if (anchor && ![...item.querySelectorAll("a")].some((existing) => existing.href === anchor.href)) {
        linkList.append(anchor);
      }
    }
    if (linkList.childElementCount) item.append(linkList);
  }

  function safeAnchor(href, label) {
    if (!href || !label) return null;
    try {
      const parsed = new URL(href, window.location.origin);
      if (!["http:", "https:"].includes(parsed.protocol)) return null;
      const anchor = document.createElement("a");
      anchor.href = parsed.href;
      anchor.textContent = label;
      if (parsed.origin !== window.location.origin) {
        anchor.target = "_blank";
        anchor.rel = "noopener noreferrer";
      }
      return anchor;
    } catch {
      return null;
    }
  }

  function safeStoredLinks(links) {
    return (Array.isArray(links) ? links : []).slice(0, 8).map((link) => ({
      href: String(link?.href || "").slice(0, 2000),
      label: String(link?.label || "").slice(0, 200)
    })).filter((link) => link.href && link.label);
  }

  function loadJson(key, fallback) {
    try {
      const value = JSON.parse(window.localStorage.getItem(key));
      return value && typeof value === "object" ? value : fallback;
    } catch {
      return fallback;
    }
  }

  function saveJson(key, value) {
    try { window.localStorage.setItem(key, JSON.stringify(value)); } catch {}
  }

  async function readResponseJson(response) {
    try {
      return await response.json();
    } catch {
      return {};
    }
  }

  async function copyText(value) {
    if (navigator.clipboard?.writeText) {
      await navigator.clipboard.writeText(value);
      return;
    }
    const textarea = document.createElement("textarea");
    textarea.value = value;
    textarea.setAttribute("readonly", "");
    textarea.style.position = "fixed";
    textarea.style.opacity = "0";
    document.body.append(textarea);
    textarea.select();
    const copied = document.execCommand?.("copy");
    textarea.remove();
    if (!copied) throw new Error("Clipboard access is unavailable.");
  }

  function stripHtml(value) {
    return String(value || "").replace(/<[^>]*>/g, "");
  }

  function unmount(root) {
    instances.get(root)?.destroy();
  }

  window.LumiAssistantPanels?.register("lumi_ai", { mount, unmount });
})();
