(function () {
  "use strict";

  var theme = window.NEBULA_THEME || {};
  var app = document.getElementById("app");
  var loader = document.getElementById("nebula-loader");
  var themeMediaQuery = getThemeMediaQuery();

  if (!app) {
    return;
  }

  var state = {
    mode: "login",
    loading: true,
    message: "",
    messageType: "",
    themePreference: getStoredThemePreference(),
    themeMode: "dark",
    uplinkMbps: null,
    currentRoute: getCurrentRoute(),
    nodePage: 1,
    localIp: null,
    reportedIps: [],
    authToken: getStoredToken(),
    user: null,
    subscribe: null,
    stats: null,
    knowledge: [],
    tickets: [],
    selectedTicketId: null,
    selectedTicket: null,
    servers: [],
    ipv6AuthToken: getStoredIpv6Token(),
    ipv6User: null,
    ipv6Subscribe: null,
    ipv6Eligibility: null,
    ipv6Servers: [],
    sessionOverview: null,
    ipv6SessionOverview: null,
    realNameVerification: null,
    appConfig: null,
    isSidebarOpen: false,
    serverSearch: "",
    cachedSubNodes: null,
    ipv6CachedSubNodes: null
  };

  init();

  async function init() {
    applyThemeMode();
    applyCustomBackground();
    loadUplinkMetric();
    window.setInterval(loadUplinkMetric, 5000);

    try {
      var verify = getVerifyToken();
      if (verify) {
        var verifyResponse = await fetchJson("/api/v1/passport/auth/token2Login?verify=" + encodeURIComponent(verify), {
          method: "GET",
          auth: false
        });
        var verifyPayload = unwrap(verifyResponse);
        if (verifyPayload && verifyPayload.auth_data) {
          saveToken(verifyPayload.auth_data);
          clearVerifyToken();
          state.authToken = getStoredToken();
        }
      }
    } catch (error) {
      showMessage(error.message || "Quick login failed", "error");
    }

    if (state.authToken) {
      var loaded = await loadDashboard();
      if (!loaded) {
        state.loading = false;
      }
    } else {
      state.loading = false;
    }

    render();
    app.addEventListener("click", onClick);
    app.addEventListener("input", onInput);
    app.addEventListener("submit", onSubmit);
    window.addEventListener("hashchange", onRouteChange);
    bindSystemThemeListener();
  }

  async function loadDashboard(silent) {
    if (!state.authToken) {
      state.loading = false;
      return false;
    }

    state.loading = !silent;
    if (!silent) {
      render();
    }

    try {
      var results = await Promise.all([
        fetchJson("/api/v1/user/info", { method: "GET" }),
        fetchJson("/api/v1/user/getSubscribe", { method: "GET" }),
        fetchJson("/api/v1/user/getStat", { method: "GET" }),
        fetchJson("/api/v1/user/server/fetch", { method: "GET" }),
        fetchJson("/api/v1/user/ticket/fetch", { method: "GET" }),
        fetchSessionOverview(),
        fetchJson("/api/v1/user/comm/config", { method: "GET" }),
        fetchRealNameVerificationStatus(),
        fetchJson("/api/v1/user/user-add-ipv6-subscription/check", { method: "GET" })
      ]);

      state.user = unwrap(results[0]);
      state.subscribe = unwrap(results[1]);
      state.stats = unwrap(results[2]);
      state.servers = unwrap(results[3]) || [];
      state.knowledge = [];
      state.tickets = unwrap(results[4]) || [];
      state.sessionOverview = results[5];
      state.appConfig = unwrap(results[6]) || {};
      state.realNameVerification = results[7] || null;
      state.ipv6Eligibility = unwrap(results[8]) || null;

      if (state.ipv6AuthToken) {
        try {
          var ipv6Results = await Promise.all([
            fetchJson("/api/v1/user/info", { method: "GET", ipv6: true }),
            fetchJson("/api/v1/user/getSubscribe", { method: "GET", ipv6: true }),
            fetchJson("/api/v1/user/server/fetch", { method: "GET", ipv6: true }),
            fetchSessionOverview(true)
          ]);
          state.ipv6User = unwrap(ipv6Results[0]);
          state.ipv6Subscribe = unwrap(ipv6Results[1]);
          state.ipv6Servers = unwrap(ipv6Results[2]) || [];
          state.ipv6SessionOverview = ipv6Results[3];
        } catch (e) {
          if (handleIpv6AuthFailure(e)) {
            console.warn("Nebula: cleared stale IPv6 session after unauthorized response");
          }
          console.error("Failed to load IPv6 dashboard data", e);
        }
      }

      state.nodePage = 1;
      state.loading = false;
      return true;
    } catch (error) {
      state.loading = false;
      if (error.status === 401 || error.status === 403) {
        var hadSession = Boolean(state.authToken);
        clearToken();
        resetDashboard();
        showMessage(hadSession ? "Session expired. Please sign in again." : "", hadSession ? "error" : "");
        return false;
      }
      showMessage(error.message || "Failed to load dashboard", "error");
      return false;
    }
  }

  async function fetchSessionOverview(isIpv6) {
    try {
      var response = await fetchJson("/api/v1/user/user-online-devices/get-ip", { method: "GET", ipv6: !!isIpv6 });
      var ipPayload = unwrap(response) || {};

      var reportedIps = ipPayload.ips || [];
      if (!isIpv6) {
        state.reportedIps = reportedIps;
      }
      
      var overview = ipPayload.session_overview || { sessions: [], online_ips: [] };
      if (!isIpv6) {
        overview.online_ips = state.reportedIps;
      }
      
      return buildSessionOverview(overview.sessions, overview, !!isIpv6);
    } catch (error) {
      if (isIpv6 && handleIpv6AuthFailure(error)) {
        return null;
      }
      console.error("Nebula: Failed to synchronize online status", error);
      // Fallback for sessions if plugin fails
      try {
        var sessionsFallback = unwrap(await fetchJson("/api/v1/user/getActiveSession", { method: "GET", ipv6: !!isIpv6 })) || [];
        return buildSessionOverview(sessionsFallback, {}, !!isIpv6);
      } catch (e) {
        if (isIpv6 && handleIpv6AuthFailure(e)) {
          return null;
        }
        return buildSessionOverview([], {}, !!isIpv6);
      }
    }
  }

  async function fetchRealNameVerificationStatus() {
    try {
      var response = await fetchJson("/api/v1/user/real-name-verification/status", { method: "GET" });
      var payload = unwrap(response) || {};
      return Object.assign({
        enabled: true,
        status: "unverified",
        status_label: "未认证",
        can_submit: true,
        real_name: "",
        identity_no_masked: "",
        notice: "",
        reject_reason: "",
        submitted_at: null,
        reviewed_at: null
      }, payload);
    } catch (error) {
      return {
        enabled: false,
        status: "unavailable",
        status_label: "Unavailable",
        can_submit: false,
        real_name: "",
        identity_no_masked: "",
        notice: "",
        reject_reason: "",
        submitted_at: null,
        reviewed_at: null
      };
    }
  }

  async function fetchJson(url, options) {
    options = options || {};
    var headers = {
      "Content-Type": "application/json",
      "X-Requested-With": "XMLHttpRequest"
    };

    if (options.auth !== false) {
      var token = options.ipv6 ? (state.ipv6AuthToken || getStoredIpv6Token()) : (state.authToken || getStoredToken());
      if (token) {
        headers.Authorization = normalizeAuthHeader(token);
      }
    }

    var response = await fetch(url, {
      method: options.method || "GET",
      headers: headers,
      credentials: "same-origin",
      body: options.body ? JSON.stringify(options.body) : undefined
    });

    var payload = null;
    try {
      payload = await response.json();
    } catch (error) {
      payload = null;
    }

    if (!response.ok) {
      var message = payload && (payload.message || payload.error) || "Request failed";
      var err = new Error(message);
      err.status = response.status;
      throw err;
    }

    return payload;
  }

  function unwrap(payload) {
    if (!payload) {
      return null;
    }
    if (typeof payload.data !== "undefined") {
      return payload.data;
    }
    if (typeof payload.total !== "undefined" && Array.isArray(payload.data)) {
      return payload.data;
    }
    return payload;
  }

  function onClick(event) {
    if (state.isSidebarOpen) {
      var sidebar = event.target.closest(".dashboard-sidebar");
      var toggleBtn = event.target.closest("[data-action='toggle-sidebar']");
      if (!sidebar && !toggleBtn) {
        state.isSidebarOpen = false;
        document.documentElement.dataset.sidebarOpen = "false";
        var sidebarEl = document.querySelector(".dashboard-sidebar");
        var overlayEl = document.querySelector(".sidebar-overlay");
        if (sidebarEl) sidebarEl.classList.remove("is-open");
        if (overlayEl) overlayEl.classList.remove("is-visible");
      }
    }

    var actionEl = event.target.closest("[data-action]");
    if (!actionEl) return;

    var action = actionEl.getAttribute("data-action");

    if (action === "switch-mode") {
      state.mode = actionEl.getAttribute("data-mode") || "login";
      showMessage("", "");
      render();
      return;
    }

    if (action === "navigate") {
      state.isSidebarOpen = false;
      document.documentElement.dataset.sidebarOpen = "false";
      setCurrentRoute(actionEl.getAttribute("data-route") || "overview");
      return;
    }

    if (action === "toggle-sidebar") {
      state.isSidebarOpen = !state.isSidebarOpen;
      document.documentElement.dataset.sidebarOpen = String(state.isSidebarOpen);
      var sidebar = document.querySelector(".dashboard-sidebar");
      var overlay = document.querySelector(".sidebar-overlay");
      if (sidebar) sidebar.classList.toggle("is-open", state.isSidebarOpen);
      if (overlay) overlay.classList.toggle("is-visible", state.isSidebarOpen);
      return;
    }

    if (action === "close-sidebar") {
      state.isSidebarOpen = false;
      document.documentElement.dataset.sidebarOpen = "false";
      var sidebar = document.querySelector(".dashboard-sidebar");
      var overlay = document.querySelector(".sidebar-overlay");
      if (sidebar) sidebar.classList.remove("is-open");
      if (overlay) overlay.classList.remove("is-visible");
      return;
    }

    if (action === "toggle-theme-mode") {
      toggleThemeMode();
      return;
    }

    if (action === "change-node-page") {
      setNodePage(Number(actionEl.getAttribute("data-page") || 1));
      return;
    }

    if (action === "logout") {
      clearToken();
      clearIpv6Token();
      resetDashboard();
      render();
      return;
    }

    if (action === "refresh-dashboard") {
      loadDashboard(true).then(render);
      return;
    }

    if (action === "copy-subscribe") {
      copyText((state.subscribe && state.subscribe.subscribe_url) || "");
      return;
    }

    if (action === "copy-ipv6-subscribe") {
      copyText((state.ipv6Subscribe && state.ipv6Subscribe.subscribe_url) || "");
      return;
    }

    if (action === "copy-text") {
      copyText(actionEl.getAttribute("data-value") || "");
      return;
    }

    if (action === "reset-security") {
      handleResetSecurity(actionEl);
      return;
    }

    if (action === "reset-ipv6-security") {
      handleResetSecurity(actionEl, true);
      return;
    }

    if (action === "remove-session") {
      handleRemoveSession(actionEl);
      return;
    }

    if (action === "remove-other-sessions") {
      handleRemoveOtherSessions(actionEl);
      return;
    }

    if (action === "open-ticket-detail") {
      handleOpenTicketDetail(actionEl);
      return;
    }

    if (action === "open-knowledge-article") {
      handleOpenKnowledgeArticle(actionEl);
      return;
    }

    if (action === "close-ticket-detail") {
      state.selectedTicketId = null;
      state.selectedTicket = null;
      render();
    }

    if (action === "copy-node-info") {
      handleCopyNodeInfo(actionEl.getAttribute("data-node-id"));
      return;
    }

    if (action === "set-mode") {
      state.mode = actionEl.getAttribute("data-mode") || "login";
      render();
      return;
    }

    if (action === "send-reset-code") {
      var form = actionEl.closest("form");
      var email = form.querySelector("[name='email']").value;
      if (!email) {
        showMessage("请输入邮箱地址", "warning");
        return;
      }
      actionEl.disabled = true;
      fetchJson("/api/v1/passport/comm/sendEmailVerify", {
        method: "POST",
        auth: false,
        body: { email: email }
      }).then(function () {
        showMessage("验证码已发送，请检查您的邮箱", "success");
        startCountdown(actionEl, 60);
      }).catch(function (error) {
        showMessage(error.message || "获取验证码失败", "error");
        actionEl.disabled = false;
      });
      return;
    }
  }

  function startCountdown(button, seconds) {
    var originalText = "获取验证码";
    button.disabled = true;
    var current = seconds;
    var timer = setInterval(function () {
      if (current <= 0) {
        clearInterval(timer);
        button.innerText = originalText;
        button.disabled = false;
        return;
      }
      button.innerText = current + "s";
      current--;
    }, 1000);
  }

  async function handleCopyNodeInfo(nodeId) {
    var server = (state.servers || []).concat(state.ipv6Servers || []).find(function (s) {
      return String(s.id) === String(nodeId);
    });
    if (!server) return;

    var isIpv6Node = (state.ipv6Servers || []).some(function (s) {
      return String(s.id) === String(nodeId);
    });
    
    var sub = isIpv6Node ? state.ipv6Subscribe : state.subscribe;
    var subUrl = (sub && sub.subscribe_url) || "";
    var targetName = (server.name || "").trim();
    var cacheKey = isIpv6Node ? "ipv6CachedSubNodes" : "cachedSubNodes";
    var storageKey = isIpv6Node ? "__nebula_ipv6_sub_content__" : "__nebula_sub_content__";

    // Try to get link from subscription if possible
    if (subUrl) {
      try {
        if (!state[cacheKey]) {
          // Check if we have a manually pasted sub content in sessionStorage
          var savedSub = window.sessionStorage.getItem(storageKey);
          if (savedSub) {
            console.log("Nebula: Using session-cached " + (isIpv6Node ? "IPv6 " : "") + "subscription content");
            state[cacheKey] = parseSubscriptionToLines(savedSub);
          } else {
            console.log("Nebula: Synchronizing " + (isIpv6Node ? "IPv6 " : "") + "subscription metadata...");
            state[cacheKey] = await fetchSubscriptionNodes(subUrl);
          }
        }
        
        if (state[cacheKey] && state[cacheKey].length) {
          var link = findNodeLinkByName(state[cacheKey], targetName);
          if (link) {
            var clashYaml = linkToClash(link);
            showNodeInfoModal(server, clashYaml, link);
            return;
          }
        }
      } catch (error) {
        console.error("Nebula: Failed to parse subscription", error);
        // If it looks like a CORS error (TypeError: Failed to fetch)
        if (error instanceof TypeError) {
          showCorsHelpModal(subUrl, nodeId, isIpv6Node);
          return;
        }
      }
    }

    // Fallback to text info if link not found or subscription failed
    var tags = normalizeServerTags(server.tags);
    var info = [
      "节点名称: " + (server.name || "未命名节点"),
      "节点类型: " + (server.type || "未知"),
      "节点倍率: " + (server.rate || 1) + "x",
      "节点状态: " + (server.is_online ? "在线" : "离线"),
      tags.length ? "节点标签: " + tags.join(", ") : ""
    ].filter(Boolean).join("\n");

    copyText(info, "已复制节点基本信息（订阅链接当前不可达）");
  }

  function showNodeInfoModal(server, clashYaml, standardLink) {
    var overlay = document.createElement("div");
    overlay.className = "nebula-modal-overlay";
    
    var modal = document.createElement("div");
    modal.className = "nebula-modal stack";
    modal.style.maxWidth = "600px";
    modal.style.width = "min(540px, 100%)";
    
    modal.innerHTML = [
        '<h3 class="nebula-modal-title" style="margin-bottom: 20px;">' + escapeHtml(server.name || "节点详情") + '</h3>',
        '<div class="node-specs" style="margin-bottom:24px; display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">',
        '<div class="node-spec" style="background: rgba(var(--accent-rgb), 0.05); border-color: rgba(var(--accent-rgb), 0.15); text-align: center; padding: 14px;">' + 
          '<span class="meta-label" style="margin-top:0; font-size:10px; opacity:0.7;">节点类型</span>' + 
          '<strong style="color: var(--accent); font-size: 18px; margin-top: 4px; display: block;">' + escapeHtml(server.type || "unknown").toUpperCase() + '</strong>' + 
        '</div>',
        '<div class="node-spec" style="background: rgba(var(--accent-rgb), 0.05); border-color: rgba(var(--accent-rgb), 0.15); text-align: center; padding: 14px;">' + 
          '<span class="meta-label" style="margin-top:0; font-size:10px; opacity:0.7;">结算倍率</span>' + 
          '<strong style="color: var(--accent); font-size: 18px; margin-top: 4px; display: block;">' + escapeHtml(String(server.rate || 1)) + 'x</strong>' + 
        '</div>',
        '</div>',
        '<div class="field" style="margin-top: 8px;"><label>Clash 配置预览 (已脱敏)</label>',
        '<pre class="node-info-pre" style="max-height: 180px; font-size: 11px;">' + escapeHtml(maskClashYaml(clashYaml)) + '</pre>',
        '</div>',
        '<div class="nebula-modal-footer" style="padding-top: 24px; display: flex; gap: 10px; flex-wrap: wrap;">',
        '<button class="btn btn-primary" id="node-info-copy-link" style="flex: 1; min-width: 100px; white-space: nowrap;">标准格式</button>',
        '<button class="btn btn-primary" id="node-info-copy-clash" style="flex: 1; min-width: 100px; white-space: nowrap;">Clash 格式</button>',
        '<button class="btn btn-ghost" id="node-info-close" style="flex: 1; min-width: 100px; white-space: nowrap;">关闭</button>',
        '</div>'
    ].join("");
    
    document.body.appendChild(overlay);
    overlay.appendChild(modal);
    
    modal.querySelector("#node-info-close").onclick = function() {
        document.body.removeChild(overlay);
    };
    
    modal.querySelector("#node-info-copy-link").onclick = function() {
        copyText(standardLink, "标准配置链接已复制");
        document.body.removeChild(overlay);
    };

    modal.querySelector("#node-info-copy-clash").onclick = function() {
        copyText(clashYaml, "Clash 配置已复制");
        document.body.removeChild(overlay);
    };
    
    overlay.onclick = function(e) {
      if (e.target === overlay) document.body.removeChild(overlay);
    };
  }

  function showCorsHelpModal(subUrl, nodeId, isIpv6) {
    var title = (isIpv6 ? "IPv6 " : "") + "同步订阅数据";
    showNebulaModal({
      title: title,
      content: "由于浏览器跨域 (CORS) 策略限制，无法直接从网页获取节点配置链接。请手动同步一次订阅：\n\n1. 点击下方按钮在浏览器中打开订阅链接\n2. 复制网页显示的全部内容\n3. 返回此处点击“去粘贴”并存入系统",
      confirmText: "打开订阅链接",
      cancelText: "我要手动粘贴",
      onConfirm: function() {
        window.open(subUrl, "_blank");
        showMessage("订阅已在新标签页打开，请复制其内容", "info");
      },
      onCancel: function() {
        showManualPasteModal(nodeId, isIpv6);
      }
    });
  }

  function showManualPasteModal(nodeId, isIpv6) {
    var overlay = document.createElement("div");
    overlay.className = "nebula-modal-overlay";
    
    var modal = document.createElement("div");
    modal.className = "nebula-modal stack";
    modal.style.maxWidth = "550px";
    
    var storageKey = isIpv6 ? "__nebula_ipv6_sub_content__" : "__nebula_sub_content__";
    var cacheKey = isIpv6 ? "ipv6CachedSubNodes" : "cachedSubNodes";
    
    modal.innerHTML = [
        '<h3 class="nebula-modal-title">粘贴 ' + (isIpv6 ? "IPv6 " : "") + '订阅内容</h3>',
        '<p class="hero-description" style="font-size:13px;margin-bottom:12px">请将你刚才复制的订阅源代码粘贴在下方。我们将为您解析并保存。</p>',
        '<textarea id="nebula-sub-paste" class="field" style="width:100%;min-height:220px;font-size:12px;background:rgba(255,255,255,0.03);border-radius:12px;padding:12px" placeholder="粘贴此处..."></textarea>',
        '<div class="nebula-modal-footer">',
        '<button class="btn btn-ghost" id="nebula-paste-cancel">取消</button>',
        '<button class="btn btn-primary" id="nebula-paste-save">解析并保存</button>',
        '</div>'
    ].join("");
    
    document.body.appendChild(overlay);
    overlay.appendChild(modal);
    
    modal.querySelector("#nebula-paste-cancel").onclick = function() {
        document.body.removeChild(overlay);
    };
    
    modal.querySelector("#nebula-paste-save").onclick = function() {
        var content = modal.querySelector("#nebula-sub-paste").value.trim();
        if (content) {
            window.sessionStorage.setItem(storageKey, content);
            state[cacheKey] = parseSubscriptionToLines(content);
            document.body.removeChild(overlay);
            showMessage("订阅数据同步成功！", "success");
            if (nodeId) handleCopyNodeInfo(nodeId);
        } else {
            showMessage("内容不能为空", "error");
        }
    };
  }

  async function fetchSubscriptionNodes(url) {
    try {
      var response = await fetch(url);
      if (!response.ok) throw new Error("Network response was not ok");
      var text = await response.text();
      return parseSubscriptionToLines(text);
    } catch (e) {
      throw e; // Reraise to be caught by handleCopyNodeInfo for CORS check
    }
  }

  function parseSubscriptionToLines(text) {
    var decoded = "";
    try {
      decoded = utf8Base64Decode(text.trim());
    } catch (e) {
      decoded = text; 
    }
    return decoded.split(/\r?\n/).map(function(s) { return s.trim(); }).filter(Boolean);
  }

  function findNodeLinkByName(links, name) {
    if (!name) return null;
    var target = name.trim();
    
    console.log("Nebula: Searching for node '" + name + "' among " + links.length + " links (Strict Match)");

    for (var i = 0; i < links.length; i++) {
      var link = links[i].trim();
      var remark = "";
      
      if (link.indexOf("#") !== -1) {
        var parts = link.split("#");
        var rawRemark = parts[parts.length - 1];
        try {
          remark = decodeURIComponent(rawRemark.replace(/\+/g, "%20"));
        } catch (e) {
          remark = rawRemark;
        }
      } else if (link.indexOf("vmess://") === 0) {
        try {
          var jsonStr = utf8Base64Decode(link.slice(8));
          var json = JSON.parse(jsonStr);
          remark = json.ps || "";
        } catch (e) {}
      } else if (link.indexOf("name:") !== -1) {
        var yamlMatch = link.match(/name:\s*["']?([^"']+)["']?/);
        if (yamlMatch) remark = yamlMatch[1];
      }
      
      // Strict exact match after trimming
      if ((remark || "").trim() === target) {
        return link;
      }
    }
    
    console.warn("Nebula: No exact match found for node '" + name + "'");
    return null;
  }

  function maskClashYaml(yaml) {
    if (!yaml) return "";
    return yaml
      .replace(/server:\s*.+/g, "server: **********")
      .replace(/password:\s*.+/g, "password: **********")
      .replace(/uuid:\s*.+/g, "uuid: **********")
      .replace(/public-key:\s*.+/g, "public-key: **********");
  }

  function linkToClash(link) {
    if (!link) return "";

    var remark = "";
    if (link.indexOf("#") !== -1) {
      var parts = link.split("#");
      try {
        remark = decodeURIComponent(parts[parts.length - 1].replace(/\+/g, "%20"));
      } catch (e) {
        remark = parts[parts.length - 1];
      }
    }

    // Shadowsocks
    if (link.indexOf("ss://") === 0) {
      var body = link.split("#")[0].slice(5);
      var parts = body.split("@");
      if (parts.length < 2) return link;

      var userinfo = parts[0];
      var serverinfo = parts[1];
      var decodedUser = "";
      try {
        decodedUser = atob(userinfo.replace(/-/g, "+").replace(/_/g, "/"));
      } catch (e) {
        decodedUser = userinfo;
      }

      var userParts = decodedUser.split(":");
      var serverParts = serverinfo.split(":");

      return [
        "- name: \"" + (remark || "SS Node") + "\"",
        "  type: ss",
        "  server: " + serverParts[0],
        "  port: " + (serverParts[1] || 443),
        "  cipher: " + (userParts[0] || "aes-256-gcm"),
        "  password: " + (userParts[1] || ""),
        "  udp: true"
      ].join("\n");
    }

    // VMess
    if (link.indexOf("vmess://") === 0) {
      try {
        var json = JSON.parse(utf8Base64Decode(link.slice(8)));
        var yaml = [
          "- name: \"" + (json.ps || remark || "VMess Node") + "\"",
          "  type: vmess",
          "  server: " + json.add,
          "  port: " + (json.port || 443),
          "  uuid: " + json.id,
          "  alterId: " + (json.aid || 0),
          "  cipher: auto",
          "  udp: true",
          "  tls: " + (json.tls === "tls" ? "true" : "false"),
          "  network: " + (json.net || "tcp"),
          "  servername: " + (json.sni || json.host || "")
        ];
        if (json.net === "ws") {
          yaml.push("  ws-opts:");
          yaml.push("    path: " + (json.path || "/"));
          if (json.host) yaml.push("    headers:");
          if (json.host) yaml.push("      Host: " + json.host);
        }
        if (json.net === "grpc") {
          yaml.push("  grpc-opts:");
          yaml.push("    grpc-service-name: " + (json.path || ""));
        }
        return yaml.join("\n");
      } catch (e) {
        return link;
      }
    }

    // VLESS
    if (link.indexOf("vless://") === 0) {
      var body = link.split("#")[0].slice(8);
      var main = body.split("?")[0];
      var query = body.split("?")[1] || "";
      var parts = main.split("@");
      var uuid = parts[0];
      var serverParts = (parts[1] || "").split(":");

      var params = {};
      query.split("&").forEach(function (pair) {
        var p = pair.split("=");
        params[p[0]] = decodeURIComponent(p[1] || "");
      });

      var yaml = [
        "- name: \"" + (remark || "VLESS Node") + "\"",
        "  type: vless",
        "  server: " + serverParts[0],
        "  port: " + (parseInt(serverParts[1]) || 443),
        "  uuid: " + uuid,
        "  udp: true",
        "  tls: " + (params.security !== "none" ? "true" : "false"),
        "  skip-cert-verify: true"
      ];

      if (params.sni) yaml.push("  servername: " + params.sni);
      if (params.type) yaml.push("  network: " + params.type);
      if (params.flow) yaml.push("  flow: " + params.flow);
      if (params.fp) yaml.push("  client-fingerprint: " + params.fp);
      if (params.security === "reality") {
        yaml.push("  reality-opts:");
        if (params.pbk) yaml.push("    public-key: " + params.pbk);
        if (params.sid) yaml.push("    short-id: " + params.sid);
      }
      if (params.type === "ws") {
        yaml.push("  ws-opts:");
        yaml.push("    path: " + (params.path || "/"));
        if (params.host) yaml.push("    headers:");
        if (params.host) yaml.push("      Host: " + params.host);
      }
      if (params.type === "grpc") {
        yaml.push("  grpc-opts:");
        yaml.push("    grpc-service-name: " + (params.serviceName || ""));
      }

      return yaml.join("\n");
    }

    // Trojan
    if (link.indexOf("trojan://") === 0) {
      var body = link.split("#")[0].slice(9);
      var main = body.split("?")[0];
      var query = body.split("?")[1] || "";
      var parts = main.split("@");
      var password = parts[0];
      var serverParts = (parts[1] || "").split(":");

      var params = {};
      query.split("&").forEach(function (pair) {
        var p = pair.split("=");
        params[p[0]] = decodeURIComponent(p[1] || "");
      });

      var yaml = [
        "- name: \"" + (remark || "Trojan Node") + "\"",
        "  type: trojan",
        "  server: " + serverParts[0],
        "  port: " + (parseInt(serverParts[1]) || 443),
        "  password: " + password,
        "  udp: true",
        "  sni: " + (params.sni || params.peer || ""),
        "  skip-cert-verify: true"
      ];

      if (params.type === "ws") {
        yaml.push("  ws-opts:");
        yaml.push("    path: " + (params.path || "/"));
        if (params.host) yaml.push("    headers:");
        if (params.host) yaml.push("      Host: " + params.host);
      }
      if (params.type === "grpc") {
        yaml.push("  grpc-opts:");
        yaml.push("    grpc-service-name: " + (params.serviceName || ""));
      }

      return yaml.join("\n");
    }

    return link;
  }

  function utf8Base64Decode(str) {
    try {
      var b64 = str.trim().replace(/-/g, "+").replace(/_/g, "/");
      while (b64.length % 4 !== 0) b64 += "=";
      
      var binary = atob(b64);
      try {
        if (typeof TextDecoder !== "undefined") {
          var bytes = new Uint8Array(binary.length);
          for (var i = 0; i < binary.length; i++) {
            bytes[i] = binary.charCodeAt(i);
          }
          return new TextDecoder("utf-8").decode(bytes);
        }
        
        return decodeURIComponent(binary.split("").map(function (c) {
          return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(""));
      } catch (e) {
        return binary;
      }
    } catch (e) {
      throw new Error("Invalid base64 content: " + e.message);
    }
  }

  function onInput(event) {
    var target = event.target;
    if (target.matches("[data-action='search-servers']")) {
      state.serverSearch = target.value;
      state.nodePage = 1;
      render();

      var input = document.querySelector("[data-action='search-servers']");
      if (input) {
        input.focus();
        input.setSelectionRange(target.value.length, target.value.length);
      }
    }
  }



  async function loadUplinkMetric() {
    if (state.authToken) {
      return;
    }
    var baseUrl = getMetricsBaseUrl();
    if (!baseUrl) {
      return;
    }
    try {
      var response = await fetch(baseUrl + "/api/metrics/overview", {
        method: "GET",
        headers: {
          "X-Requested-With": "XMLHttpRequest"
        }
      });
      var payload = await response.json();
      var network = (payload && payload.network) || {};
      var tx = Number(network.tx || 0);
      var total = tx;
      if (total > 0) {
        state.uplinkMbps = (total * 8) / 1000000;
        var el = document.getElementById("nebula-hero-metric-stack");
        if (el) {
          el.innerHTML = renderUplinkMetricStack();
        } else if (!state.authToken || !state.user) {
          render();
        }
      }
    } catch (error) {
      state.uplinkMbps = null;
    }
  }

  function onSubmit(event) {
    var form = event.target;
    if (!form.matches("[data-form]")) {
      return;
    }

    event.preventDefault();
    var formType = form.getAttribute("data-form");
    var data = Object.fromEntries(new FormData(form).entries());
    var submitButton = form.querySelector("[type='submit']");

    if (submitButton) {
      submitButton.disabled = true;
    }

    if (formType === "create-ticket") {
      fetchJson("/api/v1/user/ticket/save", {
        method: "POST",
        body: {
          subject: data.subject || "",
          level: data.level || "0",
          message: data.message || ""
        }
      }).then(function () {
        showMessage("工单已创建", "success");
        state.selectedTicketId = null;
        state.selectedTicket = null;
        return loadDashboard(true);
      }).then(render).catch(function (error) {
        showMessage(error.message || "工单创建失败", "error");
        render();
      }).finally(function () {
        if (submitButton) {
          submitButton.disabled = false;
        }
      });
      return;
    }

    if (formType === "real-name-verification") {
      fetchJson("/api/v1/user/real-name-verification/submit", {
        method: "POST",
        body: {
          real_name: data.real_name || "",
          identity_no: data.identity_no || ""
        }
      }).then(function (response) {
        var payload = unwrap(response) || {};
        var verification = payload.verification || payload;
        state.realNameVerification = verification;
        showMessage(
          verification && verification.status === "approved"
            ? "实名认证已通过。"
            : "认证信息已提交，请等待审核。",
          "success"
        );
        return loadDashboard(true);
      }).then(render).catch(function (error) {
        showMessage(error.message || "认证提交失败", "error");
        render();
      }).finally(function () {
        if (submitButton) {
          submitButton.disabled = false;
        }
      });
      return;
    }

    if (formType === "change-password") {
      if (data.new_password !== data.confirm_password) {
        showMessage("两次输入的新密码不一致", "error");
        if (submitButton) submitButton.disabled = false;
        return;
      }
      fetchJson("/api/v1/user/changePassword", {
        method: "POST",
        body: {
          old_password: data.old_password || "",
          new_password: data.new_password || ""
        }
      }).then(function () {
        showMessage("密码已修改，建议在 IPv6 节点同步新密码", "success");
        form.reset();
      }).catch(function (error) {
        showMessage(error.message || "密码修改失败", "error");
      }).finally(function () {
        if (submitButton) {
          submitButton.disabled = false;
        }
      });
      return;
    }

    if (formType === "forget-password") {
      fetchJson("/api/v1/passport/auth/forget", {
        method: "POST",
        auth: false,
        body: {
          email: data.email,
          email_code: data.email_code,
          password: data.password
        }
      }).then(function () {
        showMessage("密码已重置，请使用新密码登录", "success");
        state.mode = "login";
        render();
      }).catch(function (error) {
        showMessage(error.message || "重置失败", "error");
      }).finally(function () {
        if (submitButton) {
          submitButton.disabled = false;
        }
      });
      return;
    }

    fetchJson(formType === "register" ? "/api/v1/passport/auth/register" : "/api/v1/passport/auth/login", {
      method: "POST",
      auth: false,
      body: data
    }).then(function (response) {
      var payload = unwrap(response);
      if (!payload || !payload.auth_data) {
        throw new Error("Authentication succeeded but token payload is missing");
      }
      saveToken(payload.auth_data);
      state.authToken = getStoredToken();

      showMessage(formType === "register" ? "Account created" : "Signed in", "success");
      return loadDashboard(true).then(function () {
        if (formType !== "login" || !data.email || !data.password || state.ipv6AuthToken) {
          return null;
        }
        if (!(state.ipv6Eligibility && state.ipv6Eligibility.is_active)) {
          return null;
        }

        var ipv6Email = data.email.replace("@", "-ipv6@");
        return fetchJson("/api/v1/passport/auth/login", {
          method: "POST",
          auth: false,
          body: Object.assign({}, data, { email: ipv6Email })
        }).then(function (ipv6Response) {
          var ipv6Payload = unwrap(ipv6Response);
          if (ipv6Payload && ipv6Payload.auth_data) {
            saveIpv6Token(ipv6Payload.auth_data);
            state.ipv6AuthToken = getStoredIpv6Token();
          }
        }).catch(function () {
          return null;
        }).then(function () {
          return loadDashboard(true);
        });
      });
    }).then(render).catch(function (error) {
      showMessage(error.message || "Unable to continue", "error");
      render();
    }).finally(function () {
      if (submitButton) {
        submitButton.disabled = false;
      }
    });
  }

  function handleResetSecurity(actionEl, isIpv6) {
    showNebulaModal({
      title: "安全提醒",
      content: "确定要重置订阅令牌吗？重置令牌后，原本的订阅地址将失效，你需要重新获取并配置订阅。",
      confirmText: "确认重置",
      onConfirm: function () {
        actionEl.disabled = true;
        fetchJson("/api/v1/user/resetSecurity", { method: "GET", ipv6: !!isIpv6 }).then(function (response) {
          var subscribeUrl = unwrap(response);
          var sub = isIpv6 ? state.ipv6Subscribe : state.subscribe;
          if (sub && subscribeUrl) {
            sub.subscribe_url = subscribeUrl;
          }
          showMessage((isIpv6 ? "IPv6 " : "") + "订阅令牌已重置", "success");
          render();
        }).catch(function (error) {
          showMessage(error.message || "重置失败", "error");
          render();
        }).finally(function () {
          actionEl.disabled = false;
        });
      }
    });
  }

  function handleRemoveSession(actionEl) {
    var sessionId = actionEl.getAttribute("data-session-id");
    if (!sessionId) {
      return;
    }

    actionEl.disabled = true;
    fetchJson("/api/v1/user/removeActiveSession", {
      method: "POST",
      body: { session_id: sessionId }
    }).then(function () {
      showMessage("Session revoked", "success");
      return loadDashboard(true);
    }).then(render).catch(function (error) {
      showMessage(error.message || "Unable to revoke session", "error");
      render();
    }).finally(function () {
      actionEl.disabled = false;
    });
  }

  function handleRemoveOtherSessions(actionEl) {
    var sessions = ((state.sessionOverview && state.sessionOverview.sessions) || []).filter(function (session) {
      return !session.is_current;
    });

    if (!sessions.length) {
      showMessage("No other sessions to sign out.", "success");
      render();
      return;
    }

    actionEl.disabled = true;
    Promise.all(sessions.map(function (session) {
      return fetchJson("/api/v1/user/removeActiveSession", {
        method: "POST",
        body: { session_id: session.id }
      });
    })).then(function () {
      showMessage("Signed out all other sessions.", "success");
      return loadDashboard(true);
    }).then(render).catch(function (error) {
      showMessage(error.message || "Unable to sign out other sessions", "error");
      render();
    }).finally(function () {
      actionEl.disabled = false;
    });
  }

  function handleOpenTicketDetail(actionEl) {
    var ticketId = actionEl.getAttribute("data-ticket-id");
    if (!ticketId) {
      return;
    }
    actionEl.disabled = true;
    fetchJson("/api/v1/user/ticket/fetch?id=" + encodeURIComponent(ticketId), { method: "GET" }).then(function (response) {
      state.selectedTicketId = ticketId;
      state.selectedTicket = unwrap(response);
      render();
    }).catch(function (error) {
      showMessage(error.message || "工单详情加载失败", "error");
      render();
    }).finally(function () {
      actionEl.disabled = false;
    });
  }

  function handleOpenKnowledgeArticle(actionEl) {
    var articleId = actionEl.getAttribute("data-article-id");
    if (!articleId) {
      return;
    }

    var article = findKnowledgeArticleById(articleId);
    if (!article) {
      showMessage("Knowledge article not found", "error");
      return;
    }

    showKnowledgeArticleModal(article);
  }

  var currentLayout = null; // "loading", "login", "dashboard"

  function render() {
    if (state.loading) {
      return;
    }

    var isLoggedIn = !!(state.authToken && state.user);
    var targetLayout = isLoggedIn ? "dashboard" : "login";

    // If layout type changed (e.g. Login -> Dashboard), do a full shell render
    if (currentLayout !== targetLayout) {
      currentLayout = targetLayout;
      app.innerHTML = isLoggedIn ? renderDashboard() : renderAuth();
      hideLoader();
      return;
    }

    // If already in Dashboard, only update the content area and sidebar state
    if (isLoggedIn) {
      var usedTraffic = ((state.subscribe && state.subscribe.u) || 0) + ((state.subscribe && state.subscribe.d) || 0);
      var totalTraffic = (state.subscribe && state.subscribe.transfer_enable) || 0;
      var remainingTraffic = Math.max(totalTraffic - usedTraffic, 0);
      var percent = totalTraffic > 0 ? Math.min(100, Math.round((usedTraffic / totalTraffic) * 100)) : 0;
      var stats = Array.isArray(state.stats) ? state.stats : [0, 0, 0];
      var overview = state.sessionOverview || {};

      var contentArea = document.querySelector(".dashboard-main");
      if (contentArea) {
        contentArea.innerHTML = renderCurrentRoute(remainingTraffic, usedTraffic, totalTraffic, percent, stats, overview);
      }

      var sidebarContainer = document.querySelector(".dashboard-shell");
      if (sidebarContainer) {
        var sidebar = sidebarContainer.querySelector(".dashboard-sidebar");
        if (sidebar) {
          sidebar.innerHTML = renderSidebar(remainingTraffic, usedTraffic, totalTraffic, percent, overview, true);
        }
      }
    } else {
      // For auth page, simple re-render is fine
      app.innerHTML = renderAuth();
    }

    hideLoader();
  }

  function renderAuth() {
    return [
      '<div class="app-shell">',
      renderTopbar(false),
      '<div class="auth-grid">',
      '<section class="hero glass-card">',
      '<div class="hero-badge-row"><span class="nebula-pill">' + escapeHtml(theme.description || "") + "</span></div>",
      '<div id="nebula-hero-metric-stack" class="hero-metric-stack">' + renderUplinkMetricStack() + '</div>',
      (theme.config && theme.config.slogan) ? '<p class="hero-custom-line">' + escapeHtml(theme.config.slogan) + "</p>" : "",
      "</section>",
      '<section class="auth-panel glass-card">',
      (theme.config && (theme.config.isRegisterEnabled || state.mode === "forget-password")) ? [
        '<div class="form-tabs">',
        tabButton("login", "登录", state.mode === "login"),
        (theme.config && theme.config.isRegisterEnabled) ? tabButton("register", "注册", state.mode === "register") : "",
        tabButton("forget-password", "找回密码", state.mode === "forget-password"),
        "</div>"
      ].join("") : [
        '<div class="form-tabs">',
        tabButton("login", "登录", state.mode === "login"),
        tabButton("forget-password", "找回密码", state.mode === "forget-password"),
        "</div>"
      ].join(""),
      renderAuthPanelHeading(),
      renderAuthForm(),
      "</section>",
      "</div>",
      renderRecordFooter(),
      "</div>"
    ].join("");
  }

  function renderDashboard() {
    var usedTraffic = ((state.subscribe && state.subscribe.u) || 0) + ((state.subscribe && state.subscribe.d) || 0);
    var totalTraffic = (state.subscribe && state.subscribe.transfer_enable) || 0;
    var remainingTraffic = Math.max(totalTraffic - usedTraffic, 0);
    var percent = totalTraffic > 0 ? Math.min(100, Math.round((usedTraffic / totalTraffic) * 100)) : 0;
    var stats = Array.isArray(state.stats) ? state.stats : [0, 0, 0];
    var overview = state.sessionOverview || {};

    return renderDashboardByRoute(remainingTraffic, usedTraffic, totalTraffic, percent, stats, overview);
  }

  function renderTopbar(isLoggedIn) {
    return [
      '<header class="topbar">',
      '<div class="brand">',
      isLoggedIn ? '<button class="menu-toggle" data-action="toggle-sidebar" aria-label="Menu"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg></button>' : '',
      renderBrandMark(),
      '<div><h1>' + escapeHtml(theme.title || "Nebula") + "</h1></div>",
      "</div>",
      '<div class="topbar-actions">',
      renderThemeToggle(),
      isLoggedIn
        ? '<button class="btn btn-secondary" data-action="refresh-dashboard">刷新</button><button class="btn btn-ghost" data-action="logout">退出登录</button>'
        : '',
      "</div>",
      "</header>"
    ].join("");
  }

  function renderDashboardByRoute(remainingTraffic, usedTraffic, totalTraffic, percent, stats, overview) {
    return [
      '<div class="app-shell">',
      renderTopbar(true),
      '<div class="sidebar-overlay' + (state.isSidebarOpen ? ' is-visible' : '') + '" data-action="close-sidebar"></div>',
      '<section class="dashboard-shell">',
      renderSidebar(remainingTraffic, usedTraffic, totalTraffic, percent, overview),
      '<div class="dashboard-main">',
      renderCurrentRoute(remainingTraffic, usedTraffic, totalTraffic, percent, stats, overview),
      '</div>',
      '</section>',
      renderRecordFooter(),
      '</div>'
    ].join("");
  }

  function renderCurrentRoute(remainingTraffic, usedTraffic, totalTraffic, percent, stats, overview) {
    if (state.currentRoute === "access-history") {
      return [
        '<section class="dashboard-grid">',
        renderSessionSection(overview.sessions || []),
        renderIpSection(overview.online_ips || []),
        '</section>'
      ].join("");
    }
    if (state.currentRoute === "nodes") {
      return [
        '<section class="dashboard-grid">',
        renderSubscribeSection("span-12 section-card--overview"),
        renderServerSection(state.servers || []),
        '</section>'
      ].join("");
    }
    if (state.currentRoute === "ipv6-nodes") {
      return [
        '<section class="dashboard-grid">',
        renderIpv6SubscribeSection("span-12 section-card--overview"),
        renderServerSection(state.ipv6Servers || [], true),
        '</section>'
      ].join("");
    }
    if (state.currentRoute === "tickets") {
      return [
        '<section class="dashboard-grid">',
        renderTicketComposer(),
        renderTicketSection(state.tickets || []),
        renderTicketDetailSection(state.selectedTicket),
        '</section>'
      ].join("");
    }
    if (state.currentRoute === "real-name") {
      return [
        '<section class="dashboard-grid">',
        renderRealNameVerificationPanel(),
        '</section>'
      ].join("");
    }
    if (state.currentRoute === "security") {
      return [
        '<section class="dashboard-grid">',
        renderSecuritySection(),
        '</section>'
      ].join("");
    }

    var html = [
      '<section class="dashboard-grid dashboard-grid--overview">',
      renderTrafficOverviewCard(remainingTraffic, usedTraffic, totalTraffic, percent, "span-12 section-card--overview"),
      renderIpv6TrafficOverviewCard("span-12"),
      renderAccessSnapshotCard(overview, "span-12 section-card--overview"),
      renderRealNameVerificationOverviewCard("span-12 section-card--overview"),
      '</section>'
    ].join("");
    return html;
  }

  function renderLiveUserSurface(remainingTraffic, usedTraffic, totalTraffic, percent, overview) {
    return [
      '<section class="dashboard-hero">',
      '<div class="hero glass-card">',
      '<span class="nebula-pill">Subscription</span>',
      "<h2>" + escapeHtml(theme.title || "Nebula") + "</h2>",
      "<p>" + escapeHtml((state.subscribe && state.subscribe.plan && state.subscribe.plan.name) || "No active plan") + " · " + escapeHtml((state.user && state.user.email) || "") + "</p>",
      '<div class="dashboard-stat-grid">',
      dashboardStat(formatTraffic(remainingTraffic), "Remaining traffic"),
      dashboardStat(String(overview.online_ip_count || 0), "Current online IPs"),
      dashboardStat(String(overview.active_session_count || 0), "Stored sessions"),
      '</div>',
      '<div class="progress"><span style="width:' + percent + '%"></span></div>',
      '<p class="footer-note">Used ' + formatTraffic(usedTraffic) + ' of ' + formatTraffic(totalTraffic) + ' this cycle.</p>',
      '</div>',
      renderSubscribeSection(),
      '</section>'
    ].join("");
  }

  function renderAccessSnapshot(overview) {
    return [
      '<section class="dashboard-grid">',
      renderAccessSnapshotCard(overview),
      renderOpsSection(Array.isArray(state.stats) ? state.stats : [0, 0, 0], overview),
      '</section>'
    ].join("");
  }

  function renderAccessSnapshotCard(overview, extraClass) {
    extraClass = extraClass || "span-7";
    var ipv6Overview = state.ipv6SessionOverview;
    var limitDisplay = formatLimit(overview.device_limit) + (ipv6Overview ? " / " + formatLimit(ipv6Overview.device_limit) : "");
    return [
      '<div class="section-card glass-card ' + escapeHtml(extraClass) + '">',
      '<div class="section-head"><div><span class="tiny-pill">访问概览</span><h3>安全状态</h3></div></div>',
      '<div class="kpi-row">',
      kpiBox("设备数限制(IPv4/IPv6)", limitDisplay),
      kpiBox("余额", formatMoney((state.user && state.user.balance) || 0, state.appConfig && state.appConfig.currency_symbol)),
      kpiBox("到期时间", formatDate(state.subscribe && state.subscribe.expired_at)),
      '</div>',
      '</div>'
    ].join("");
  }

  function renderIpv6TrafficOverviewCard(extraClass) {
    if (!state.ipv6AuthToken || !state.ipv6Subscribe) {
      return [
        '<article class="section-card glass-card ' + escapeHtml(extraClass || "span-6") + ' section-card--overview">',
        '<div class="section-head"><div><span class="tiny-pill">IPv6 流量</span><h3>IPv6 流量概览</h3></div></div>',
        '<div class="empty-state" style="padding:24px">IPv6 账号未启用或加载失败。</div>',
        '</article>'
      ].join("");
    }

    var usedTraffic = (state.ipv6Subscribe.u || 0) + (state.ipv6Subscribe.d || 0);
    var totalTraffic = state.ipv6Subscribe.transfer_enable || 0;
    var remainingTraffic = Math.max(totalTraffic - usedTraffic, 0);
    var percent = totalTraffic > 0 ? Math.min(100, Math.round((usedTraffic / totalTraffic) * 100)) : 0;

    return renderTrafficOverviewCard(remainingTraffic, usedTraffic, totalTraffic, percent, extraClass || "span-6 section-card--overview", "IPv6 ");
  }

  function renderTrafficOverviewCard(remainingTraffic, usedTraffic, totalTraffic, percent, extraClass, prefix) {
    extraClass = extraClass || "span-5";
    prefix = prefix || "";
    return [
      '<article class="section-card glass-card ' + escapeHtml(extraClass) + '">',
      '<div class="section-head"><div><span class="tiny-pill">流量</span><h3>' + prefix + '用户流量概览</h3></div></div>',
      '<div class="kpi-row">',
      kpiBox("剩余", formatTraffic(remainingTraffic)),
      kpiBox("已用", formatTraffic(usedTraffic)),
      kpiBox("总量", formatTraffic(totalTraffic)),
      kpiBox("使用率", String(percent) + "%"),
      '</div>',
      '<div class="progress"><span style="width:' + percent + '%"></span></div>',
      '</article>'
    ].join("");
  }

  function renderSidebar(remainingTraffic, usedTraffic, totalTraffic, percent, overview, innerOnly) {
    var content = [
      '<div class="sidebar-nav">',
      sidebarLink("overview", "总览", "查看关键概览"),
      sidebarLink("access-history", "访问记录", "查看会话与当前 IP"),
      sidebarLink("nodes", "节点", "查看服务节点"),
      sidebarLink("ipv6-nodes", "IPv6 节点", "查看 IPv6 服务节点"),
      sidebarLink("notices", "知识库", "查看最新内容"),
      sidebarLink("tickets", "工单", "查看与创建工单"),
      sidebarLink("security", "账号安全", "修改密码与安全设置"),
      '</div>'
    ].join("");
    content = content.replace(/<button class="sidebar-link[^"]*" data-action="navigate" data-route="notices"[\s\S]*?<\/button>/, "");
    content = content.replace(
      '</div>',
      sidebarLink("real-name", "\u5b9e\u540d\u8ba4\u8bc1", "\u63d0\u4ea4\u4e0e\u67e5\u770b\u5b9e\u540d\u5ba1\u6838\u72b6\u6001") + '</div>'
    );

    if (innerOnly) return content;

    return [
      '<aside class="dashboard-sidebar glass-card' + (state.isSidebarOpen ? ' is-open' : '') + '">',
      content,
      '</aside>'
    ].join("");
  }

  function renderAuthForm() {
    var isRegister = state.mode === "register";
    var isForget = state.mode === "forget-password";

    if (isForget) {
      return [
        '<form class="stack" data-form="forget-password" style="gap:12px">',
        '<div class="field"><input name="email" type="email" placeholder="邮箱地址" required /></div>',
        '<div class="field">',
        '<div class="input-with-button">',
        '<input name="email_code" type="text" placeholder="邮箱验证码" required />',
        '<button type="button" class="btn btn-secondary btn-tiny send-code-btn" data-action="send-reset-code">发送验证码</button>',
        '</div></div>',
        '<div class="field"><input name="password" type="password" placeholder="设置新密码" minlength="8" required /></div>',
        '<div class="form-actions" style="margin-top:10px">',
        '<button class="btn btn-primary" type="submit">立即重置</button>',
        "</div>",
        "</form>"
      ].join("");
    }

    return [
      '<form class="stack" data-form="' + (isRegister ? "register" : "login") + '">',
      '<div class="field"><label>Email</label><input name="email" type="email" placeholder="name@example.com" required /></div>',
      '<div class="field"><label>Password</label><input name="password" type="password" placeholder="Minimum 8 characters" minlength="8" required /></div>',
      '<div class="form-actions">',
      '<button class="btn btn-primary" type="submit">' + (isRegister ? "创建账号" : "立即登录") + "</button>",
      "</div>",
      "</form>"
    ].join("");
  }

  function renderIpv6SubscribeSection(extraClass) {
    return renderSubscribeSection(extraClass, true);
  }

  function renderSubscribeSection(extraClass, isIpv6) {
    extraClass = extraClass || "span-7";
    var sub = isIpv6 ? state.ipv6Subscribe : state.subscribe;
    var user = isIpv6 ? state.ipv6User : state.user;
    var ipv6Eligibility = state.ipv6Eligibility || {};
    var prefix = isIpv6 ? "IPv6 " : "";
    var copyAction = isIpv6 ? "copy-ipv6-subscribe" : "copy-subscribe";
    var resetAction = isIpv6 ? "reset-ipv6-security" : "reset-security";

    if (isIpv6 && !state.ipv6AuthToken) {
      // IPv6 not enabled - show guidance to submit a ticket
    } else if (isIpv6 && !sub) {
      return "";
    }

    var ipv6FooterHtml = "";
    if (isIpv6 && !state.ipv6AuthToken) {
      var statusText = ipv6Eligibility.status_label || "未开通";
      var guidanceText = "IPv6 订阅需要通过工单由管理员开通，请提交工单申请。";
      if (ipv6Eligibility.is_active) {
        guidanceText = "您的 IPv6 已开通，如有问题请联系管理员。";
      }
      ipv6FooterHtml = '<div class="card-footer" style="margin-top:16px;border-top:1px solid var(--border-color);padding-top:16px">' +
        '<div class="empty-state" style="padding:0;text-align:left">' +
        '<strong>' + escapeHtml(statusText) + '</strong> — ' + escapeHtml(guidanceText) +
        '</div></div>';
    }

    var html = [
      '<article class="section-card glass-card ' + escapeHtml(extraClass) + '">',
      '<div class="section-head"><div><span class="tiny-pill">' + prefix + '订阅</span><h3>连接工具</h3></div><div class="toolbar">',
                  '<button class="btn btn-secondary" data-action="' + copyAction + '">复制链接</button>',
                  "</div></div>",
                  '<div class="kpi-row">',
                  !isIpv6 ? kpiBox("套餐", escapeHtml((sub && sub.plan && sub.plan.name) || "暂无套餐")) : "",
                  kpiBox("限速", formatSpeed(sub && sub.speed_limit)),
                  kpiBox("重置日", String((sub && sub.reset_day) || "-")),
                  !isIpv6 ? kpiBox("邮箱", '<span style="font-family:var(--font-mono)">' + escapeHtml((user && user.email) || "-") + "</span>") : "",
                  "</div>",
                  ipv6FooterHtml,
                  "</article>"
    ].join("");
    return html;
  }

  function renderOpsSection(stats, overview) {
    return [
      '<article class="section-card glass-card span-5">',
      '<div class="section-head"><div><span class="tiny-pill">运营</span><h3>当前工作台</h3></div></div>',
      '<div class="kpi-row">',
      kpiBox("待处理订单", String(stats[0] || 0)),
      kpiBox("工单数量", String(stats[1] || 0)),
      kpiBox("邀请用户", String(stats[2] || 0)),
      kpiBox("在线设备", String(overview.online_device_count || 0)),
      "</div>",
      "</article>"
    ].join("");
  }

  function renderIpSection(ips) {
    return [
      '<article class="section-card glass-card span-6">',
      '<div class="section-head"><div><span class="tiny-pill">当前 IP</span><h3>在线入口</h3></div></div>',
      renderIpList(ips),
      "</article>"
    ].join("");
  }

  function renderSessionSection(sessions) {
    return [
      '<article class="section-card glass-card span-6">',
      '<div class="section-head"><div><span class="tiny-pill">会话</span><h3>访问记录</h3></div><div class="toolbar"><button class="btn btn-ghost" data-action="remove-other-sessions">退出其他会话</button></div></div>',
      renderSessions(sessions),
      "</article>"
    ].join("");
  }

  function renderServerSection(servers, isIpv6) {
    return [
      '<article class="section-card glass-card span-12 section-card--nodes">',
      '<div class="section-head">',
      '<div><span class="tiny-pill">' + (isIpv6 ? "IPv6 " : "") + '节点</span><h3>' + (isIpv6 ? "IPv6 " : "") + '服务节点' + '</h3></div>',
      '<div class="toolbar">',
      '<input type="text" class="node-search-input" placeholder="搜索节点..." data-action="search-servers" value="' + escapeHtml(state.serverSearch || "") + '" />',
      '</div>',
      '</div>',
      renderServers(servers),
      "</article>"
    ].join("");
  }

  function renderKnowledgeSection(articles) {
    if (!articles.length) {
      return [
        '<article class="section-card glass-card span-6">',
        '<div class="section-head"><div><span class="tiny-pill">知识库</span><h3>知识内容</h3></div></div>',
        '<div class="empty-state">暂时没有内容。</div>',
        "</article>"
      ].join("");
    }

    // Group articles by category if possibly present, or just list them
    return [
      '<article class="section-card glass-card span-12">',
      '<div class="section-head"><div><span class="tiny-pill">知识库</span><h3>全部文章</h3></div></div>',
      '<div class="notice-list">' + articles.map(function (article) {
        return [
          '<div class="notice-item">',
          '<div class="notice-copy">',
          "<strong>" + escapeHtml(article.title || "未命名文章") + "</strong>",
          '<span class="notice-meta">' + escapeHtml(truncate(article.body || article.content || "", 160)) + "</span>",
          "</div>",
          article.category ? '<span class="tiny-pill">' + escapeHtml(article.category) + '</span>' : formatDate(article.updated_at || article.created_at),
          "</div>"
        ].join("");
      }).join("") + "</div>",
      "</article>"
    ].join("");
  }

  function renderTicketSection(tickets) {
    return [
      '<article class="section-card glass-card span-7">',
      '<div class="section-head"><div><span class="tiny-pill">工单</span><h3>工单列表</h3></div></div>',
      renderTickets(tickets),
      "</article>"
    ].join("");
  }

  function renderTicketComposer() {
    return [
      '<article class="section-card glass-card span-5">',
      '<div class="section-head"><div><span class="tiny-pill">新建</span><h3>创建工单</h3></div></div>',
      '<form class="stack" data-form="create-ticket">',
      '<div class="field"><label>主题</label><input name="subject" type="text" placeholder="请输入工单主题" required /></div>',
      '<div class="field"><label>等级</label><select name="level" class="ticket-select"><option value="0">低</option><option value="1">中</option><option value="2">高</option></select></div>',
      '<div class="field"><label>内容</label><textarea name="message" rows="6" placeholder="请描述你遇到的问题" required></textarea></div>',
      '<div class="form-actions"><button class="btn btn-primary" type="submit">提交工单</button></div>',
      '</form>',
      "</article>"
    ].join("");
  }

  function renderTicketDetailSection(ticket) {
    if (!ticket) {
      return "";
    }
    return [
      '<article class="section-card glass-card span-12">',
      '<div class="section-head"><div><span class="tiny-pill">详情</span><h3>' + escapeHtml(ticket.subject || ("工单 #" + ticket.id)) + '</h3></div><div class="toolbar"><button class="btn btn-ghost" data-action="close-ticket-detail">关闭详情</button></div></div>',
      '<div class="kpi-row">',
      kpiBox("状态", renderTicketStatus(ticket.status)),
      kpiBox("等级", getTicketLevelLabel(ticket.level)),
      kpiBox("创建时间", formatDate(ticket.created_at)),
      kpiBox("更新时间", formatDate(ticket.updated_at)),
      '</div>',
      renderTicketMessages(ticket.message || []),
      "</article>"
    ].join("");
  }


  function renderIpList(ips) {
    var list = ips.slice();
    if (!list.length) {
      return '<div class="empty-state">当前还没有检测到在线 IP 记录。</div>';
    }
    return '<div class="ip-list">' + list.map(function (ip) {
      return '<div class="ip-item"><span class="meta-label">在线设备 IP</span><code>' + escapeHtml(ip) + '</code></div>';
    }).join("") + "</div>";
  }

  function renderSessions(sessions) {
    if (!sessions.length) {
      return '<div class="empty-state">当前没有会话记录。</div>';
    }
    return '<div class="session-list">' + sessions.map(function (session) {
      var tag = session.is_current ? '<span class="status-chip online">当前</span>' : '<span class="status-chip">已保存</span>';
      var revoke = session.is_current ? "" : '<button class="btn btn-ghost" data-action="remove-session" data-session-id="' + escapeHtml(String(session.id)) + '">撤销</button>';
      return [
        '<div class="session-item">',
        '<div class="session-copy">',
        "<strong>" + escapeHtml(session.name || ("Session #" + session.id)) + "</strong>",
        '<span class="session-meta">创建于 ' + formatDate(session.created_at) + " · 最近使用 " + formatDate(session.last_used_at) + (session.ip ? " · IP " + escapeHtml(session.ip) : "") + "</span>",
        "</div>",
        '<div class="inline-actions">' + tag + revoke + "</div>",
        "</div>"
      ].join("");
    }).join("") + "</div>";
  }

  function renderServers(servers) {
    if (!servers.length) {
      return '<div class="empty-state">当前没有可用节点。</div>';
    }
    var sortedServers = servers.slice().sort(compareServers);

    var searchTerm = (state.serverSearch || "").toLowerCase();
    var filteredServers = sortedServers;
    if (searchTerm) {
      filteredServers = sortedServers.filter(function (server) {
        var nameMatch = (server.name || "").toLowerCase().indexOf(searchTerm) !== -1;
        var tags = normalizeServerTags(server.tags);
        var tagMatch = tags.some(function (tag) {
          return String(tag).toLowerCase().indexOf(searchTerm) !== -1;
        });
        return nameMatch || tagMatch;
      });
    }

    if (!filteredServers.length && servers.length > 0) {
      return '<div class="empty-state">没有匹配的节点。</div>';
    }

    var pageSize = getNodePageSize();
    var totalPages = Math.max(1, Math.ceil(filteredServers.length / pageSize));
    if (state.nodePage > totalPages) {
      state.nodePage = totalPages;
    }
    if (state.nodePage < 1) {
      state.nodePage = 1;
    }
    var startIndex = (state.nodePage - 1) * pageSize;
    var pagedServers = filteredServers.slice(startIndex, startIndex + pageSize);

    return '<div class="node-list-wrap"><div class="node-list">' + pagedServers.map(function (server) {
      var tags = normalizeServerTags(server.tags);
      return [
        '<div class="node-item">',
        '<div class="node-head">',
        '<div class="node-copy">',
        "<strong>" + escapeHtml(server.name || "未命名节点") + "</strong>",
        '<span class="node-meta">' + escapeHtml(server.type || "node") + "</span>",
        "</div>",
        '<button class="node-action-btn" data-action="copy-node-info" data-node-id="' + server.id + '">查看配置</button>',
        "</div>",
        '<div class="node-specs">',
        nodeSpec("倍率", escapeHtml(String(server.rate || 1)) + "x"),
        nodeSpec("状态", '<span class="status-chip ' + (server.is_online ? "online" : "offline") + '">' + (server.is_online ? "在线" : "离线") + "</span>"),
        "</div>",
        tags.length ? '<div class="node-tags">' + tags.map(function (tag) {
          return '<span class="tiny-pill">' + escapeHtml(String(tag)) + "</span>";
        }).join("") + "</div>" : "",
        "</div>"
      ].join("");
    }).join("") + '</div>' + renderNodePagination(totalPages) + "</div>";
  }

  function renderNotices(articles) {
    if (!articles.length) {
      return '<div class="empty-state">暂时没有内容。</div>';
    }
    return '<div class="notice-list">' + articles.map(function (article) {
      return [
        '<div class="notice-item">',
        '<div class="notice-copy">',
        "<strong>" + escapeHtml(article.title || "公告") + "</strong>",
        '<span class="notice-meta">' + escapeHtml(truncate(article.body || article.content || "", 160)) + "</span>",
        "</div>",
        '<span class="tiny-pill">' + formatDate(article.updated_at || article.created_at) + "</span>",
        "</div>"
      ].join("");
    }).join("") + "</div>";
  }

  function renderTickets(tickets) {
    if (!tickets.length) {
      return '<div class="empty-state">当前没有工单记录。</div>';
    }
    return '<div class="notice-list">' + tickets.map(function (ticket) {
      return [
        '<div class="notice-item">',
        '<div class="notice-copy">',
        "<strong>" + escapeHtml(ticket.subject || ("工单 #" + ticket.id)) + "</strong>",
        '<span class="notice-meta">等级 ' + escapeHtml(getTicketLevelLabel(ticket.level)) + ' · 更新时间 ' + formatDate(ticket.updated_at || ticket.created_at) + "</span>",
        "</div>",
        '<div class="inline-actions">' + renderTicketStatus(ticket.status) + '<button class="btn btn-ghost" data-action="open-ticket-detail" data-ticket-id="' + escapeHtml(String(ticket.id)) + '">查看详情</button></div>',
        "</div>"
      ].join("");
    }).join("") + "</div>";
  }

  function renderTicketMessages(messages) {
    if (!messages.length) {
      return '<div class="empty-state">当前工单还没有更多消息。</div>';
    }
    return '<div class="session-list">' + messages.map(function (message) {
      return [
        '<div class="session-item">',
        '<div class="session-copy">',
        '<strong>' + (message.is_me ? "我" : "客服") + "</strong>",
        '<span class="session-meta">' + formatDate(message.created_at) + "</span>",
        '<span class="notice-meta">' + escapeHtml(message.message || "") + "</span>",
        "</div>",
        "</div>"
      ].join("");
    }).join("") + "</div>";
  }

  function renderTicketStatus(status) {
    var isOpen = Number(status) === 0;
    return '<span class="status-chip ' + (isOpen ? "online" : "offline") + '">' + (isOpen ? "处理中" : "已关闭") + "</span>";
  }

  function getTicketLevelLabel(level) {
    if (String(level) === "2") {
      return "高";
    }
    if (String(level) === "1") {
      return "中";
    }
    return "低";
  }

  function metricBox(value, label) {
    return '<div class="metric-box"><span class="value">' + escapeHtml(value) + '</span><span class="label">' + escapeHtml(label) + "</span></div>";
  }

  function nodeSpec(label, value) {
    return '<div class="node-spec"><span class="meta-label">' + escapeHtml(label) + '</span><strong>' + value + "</strong></div>";
  }

  function renderNodePagination(totalPages) {
    if (totalPages <= 1) {
      return "";
    }
    return [
      '<div class="node-pagination">',
      '<button class="btn btn-ghost" data-action="change-node-page" data-page="' + String(Math.max(1, state.nodePage - 1)) + '"' + (state.nodePage <= 1 ? " disabled" : "") + '>上一页</button>',
      '<span class="tiny-pill">第 ' + String(state.nodePage) + " / " + String(totalPages) + " 页</span>",
      '<button class="btn btn-ghost" data-action="change-node-page" data-page="' + String(Math.min(totalPages, state.nodePage + 1)) + '"' + (state.nodePage >= totalPages ? " disabled" : "") + '>下一页</button>',
      "</div>"
    ].join("");
  }

  function getNodePageSize() {
    var width = window.innerWidth || document.documentElement.clientWidth || 1280;
    if (width <= 860) {
      return 2;
    }
    if (width <= 1180) {
      return 4;
    }
    return 6;
  }

  function compareServers(left, right) {
    var leftOnline = left && left.is_online ? 1 : 0;
    var rightOnline = right && right.is_online ? 1 : 0;
    if (leftOnline !== rightOnline) {
      return rightOnline - leftOnline;
    }

    var leftRate = toSortableNumber(left && left.rate, Number.POSITIVE_INFINITY);
    var rightRate = toSortableNumber(right && right.rate, Number.POSITIVE_INFINITY);
    if (leftRate !== rightRate) {
      return leftRate - rightRate;
    }

    var leftCheckedAt = toTimestamp(left && left.last_check_at) || 0;
    var rightCheckedAt = toTimestamp(right && right.last_check_at) || 0;
    if (leftCheckedAt !== rightCheckedAt) {
      return rightCheckedAt - leftCheckedAt;
    }

    return String((left && left.name) || "").localeCompare(String((right && right.name) || ""));
  }

  function normalizeServerTags(tags) {
    if (!tags) {
      return [];
    }
    if (Array.isArray(tags)) {
      return tags.filter(Boolean);
    }
    if (typeof tags === "string") {
      var normalized = tags.trim();
      if (!normalized || normalized === "[]" || normalized === "null" || normalized === '""') {
        return [];
      }
      if ((normalized.charAt(0) === "[" && normalized.charAt(normalized.length - 1) === "]") ||
        (normalized.charAt(0) === "{" && normalized.charAt(normalized.length - 1) === "}")) {
        try {
          var parsed = JSON.parse(normalized);
          if (Array.isArray(parsed)) {
            return parsed.map(function (tag) {
              return String(tag).trim();
            }).filter(Boolean);
          }
        } catch (error) {
          normalized = normalized.replace(/^\[/, "").replace(/\]$/, "");
        }
      }
      return normalized.split(/[|,\/]/).map(function (tag) {
        return tag.trim().replace(/^["'\[]+|["'\]]+$/g, "");
      }).filter(Boolean);
    }
    return [];
  }

  function toSortableNumber(value, fallback) {
    var numeric = Number(value);
    return Number.isFinite(numeric) ? numeric : fallback;
  }

  function buildSessionOverview(list, extra, isIpv6) {
    var ipMap = {};
    var deviceMap = {};
    var lastOnlineAt = null;

    var normalizedSessions = list.map(function (session) {
      var lastUsedAt = toTimestamp(session.last_used_at);
      var createdAt = toTimestamp(session.created_at);
      var expiresAt = toTimestamp(session.expires_at);
      var sessionName = session.name || session.device_name || session.user_agent || ("Session #" + (session.id || ""));
      var sessionIp = firstNonEmpty([
        session.ip,
        session.ip_address,
        session.last_used_ip,
        session.login_ip,
        session.current_ip
      ]);
      var deviceKey = firstNonEmpty([
        session.device_name,
        session.user_agent,
        session.client_type,
        session.os,
        sessionName
      ]) || ("device-" + Math.random().toString(36).slice(2));

      if (sessionIp) {
        ipMap[sessionIp] = true;
      }
      if (deviceKey) {
        deviceMap[deviceKey] = true;
      }
      if (lastUsedAt && (!lastOnlineAt || lastUsedAt > lastOnlineAt)) {
        lastOnlineAt = lastUsedAt;
      }

      return {
        id: session.id,
        name: sessionName,
        abilities: session.abilities,
        ip: sessionIp,
        user_agent: session.user_agent || "",
        last_used_at: lastUsedAt,
        created_at: createdAt,
        expires_at: expiresAt,
        is_current: Boolean(session.is_current || session.current || session.is_login || session.this_device)
      };
    });

    var sub = isIpv6 ? state.ipv6Subscribe : state.subscribe;

    return {
      online_ip_count: Object.keys(ipMap).length,
      online_ips: Object.keys(ipMap),
      online_device_count: Object.keys(deviceMap).length,
      device_limit: extra.device_limit || (sub ? (sub.device_limit || (sub.plan && sub.plan.device_limit) || null) : null),
      last_online_at: toTimestamp(extra.last_online_at) || lastOnlineAt,
      active_session_count: normalizedSessions.length,
      sessions: normalizedSessions
    };
  }

  function dashboardStat(value, label) {
    return '<div class="dashboard-stat"><span class="value">' + escapeHtml(value) + '</span><span class="label">' + escapeHtml(label) + "</span></div>";
  }

  function kpiBox(label, value) {
    return '<div class="kpi-box"><span class="kpi-label">' + label + '</span><span class="kpi-value">' + value + "</span></div>";
  }

  function sidebarLink(route, title, copy) {
    return '<button class="sidebar-link' + (state.currentRoute === route ? ' is-active' : '') + '" data-action="navigate" data-route="' + escapeHtml(route) + '"><strong>' + escapeHtml(title) + '</strong><span>' + escapeHtml(copy) + "</span></button>";
  }

  function renderBrandMark() {
    var logoUrl = getThemeLogo();
    if (logoUrl) {
      return '<span class="brand-mark brand-mark--image"><img src="' + escapeHtml(logoUrl) + '" alt="' + escapeHtml(theme.title || "Nebula") + ' logo" /></span>';
    }
    return '<span class="brand-mark" aria-hidden="true"></span>';
  }

  function renderThemeToggle() {
    var isLight = state.themeMode === "light";
    return [
      '<button class="theme-switch' + (isLight ? ' is-light' : ' is-dark') + '" data-action="toggle-theme-mode" type="button" aria-label="Toggle Theme">',
      '<span class="theme-switch__track">',
      '<span class="theme-switch__icon theme-switch__icon--moon"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></span>',
      '<span class="theme-switch__icon theme-switch__icon--sun"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></span>',
      '<span class="theme-switch__thumb"></span>',
      '</span>',
      '</button>'
    ].join("");
  }

  function renderRecordFooter() {
    var records = [];
    if (theme.config && theme.config.icpNo) {
      records.push('<span>' + escapeHtml(theme.config.icpNo) + '</span>');
    }
    if (theme.config && theme.config.psbNo) {
      records.push('<span>' + escapeHtml(theme.config.psbNo) + '</span>');
    }
    if (!records.length) {
      return "";
    }
    return '<footer class="record-footer">' + records.join('<span class="record-divider">|</span>') + '</footer>';
  }

  function renderAuthPanelHeading() {
    return [
      '<div class="auth-panel-heading">',
      '<p class="auth-panel-eyebrow">Welcome To</p>',
      '<h2>' + escapeHtml(getAuthPanelTitle()) + "</h2>",
      '</div>'
    ].join("");
  }

  function getAuthPanelTitle() {
    if (state.mode === "register") {
      return (theme.config && theme.config.registerTitle) || "Create your access.";
    }
    return (theme.config && theme.config.welcomeTarget) || (theme && theme.title) || "Your Space";
  }

  function onRouteChange() {
    state.currentRoute = getCurrentRoute();
    if (state.authToken && state.user) {
      render();
    }
  }

  function setCurrentRoute(route) {
    var nextRoute = isValidRoute(route) ? route : "overview";
    state.currentRoute = nextRoute;
    window.location.hash = "#/" + nextRoute;
    render();
  }

  function setNodePage(page) {
    state.nodePage = page > 0 ? page : 1;
    render();
  }

  function toggleThemeMode() {
    state.themePreference = state.themeMode === "light" ? "dark" : "light";
    persistThemePreference(state.themePreference);
    applyThemeMode();
    render();
  }

  function getCurrentRoute() {
    var hash = String(window.location.hash || "");
    var route = hash.indexOf("#/") === 0 ? hash.slice(2) : "";
    return isValidRoute(route) ? route : "overview";
  }

  function getStoredThemePreference() {
    var stored = "";
    try {
      stored = window.localStorage.getItem("nebula_theme_preference") || "";
    } catch (error) {
      stored = "";
    }
    if (stored === "light" || stored === "dark" || stored === "system") {
      return stored;
    }
    return getDefaultThemePreference();
  }

  function persistThemePreference(mode) {
    try {
      window.localStorage.setItem("nebula_theme_preference", mode);
    } catch (error) {
      return;
    }
  }

  function applyThemeMode() {
    state.themeMode = resolveThemeMode(state.themePreference);
    document.documentElement.dataset.themeMode = state.themeMode;
    document.documentElement.dataset.themePreference = state.themePreference;
    updateThemeColorMeta();
  }

  function getThemeToggleLabel() {
    return state.themeMode === "light" ? "切换至黑夜模式" : "切换至白天模式";
  }

  function getRealNameVerificationState() {
    return state.realNameVerification || {
      enabled: false,
      status: "unavailable",
      status_label: "Unavailable",
      can_submit: false,
      real_name: "",
      identity_no_masked: "",
      reject_reason: "",
      notice: "",
      submitted_at: null,
      reviewed_at: null
    };
  }

  function getRealNameStatusTone(status) {
    if (status === "approved") {
      return "online";
    }
    if (status === "rejected") {
      return "offline";
    }
    if (status === "pending") {
      return "pending";
    }
    return "neutral";
  }

  function renderRealNameStatusChip(status, label) {
    return '<span class="status-chip ' + getRealNameStatusTone(status) + '">' + escapeHtml(label || "未知状态") + '</span>';
  }

  function renderRealNameVerificationOverviewCard(extraClass) {
    var verification = getRealNameVerificationState();
    if (!verification.enabled) {
      return "";
    }

    var submitted = verification.submitted_at ? formatDate(verification.submitted_at) : "-";
    var reviewed = verification.reviewed_at ? formatDate(verification.reviewed_at) : "-";

    var html = [
      '<article class="section-card glass-card ' + escapeHtml(extraClass || "span-12") + '">',
      '<div class="section-head"><div><span class="tiny-pill">实名认证</span><h3>认证信息</h3></div></div>',
      '<div class="kpi-row">',
      kpiBox("认证状态", escapeHtml(verification.status_label || "未认证")),
      kpiBox("真实姓名", escapeHtml(verification.real_name || "-")),
      kpiBox("证件号码", escapeHtml(verification.identity_no_masked || "-")),
      '</div>',
      verification.reviewed_at ? '<div class="footer-note" style="margin-top:16px;">审核于 ' + escapeHtml(reviewed) + '</div>' : "",
      verification.reject_reason ? '<div class="real-name-alert real-name-alert--rejected">审核备注: ' + escapeHtml(verification.reject_reason) + '</div>' : "",
      '</article>'
    ].join("");
  }

  function renderRealNameVerificationPanel() {
    var verification = getRealNameVerificationState();
    if (!verification.enabled) {
      return "";
    }

    var detailBlocks = [
      '<div class="kpi-row">',
      '<div class="kpi-box"><span class="kpi-label">当前状态</span><span class="kpi-value">' + renderRealNameStatusChip(verification.status, verification.status_label) + '</span></div>',
      '<div class="kpi-box"><span class="kpi-label">真实姓名</span><span class="kpi-value">' + escapeHtml(verification.real_name || "-") + '</span></div>',
      '<div class="kpi-box"><span class="kpi-label">身份证号</span><span class="kpi-value">' + escapeHtml(verification.identity_no_masked || "-") + '</span></div>',
      '<div class="kpi-box"><span class="kpi-label">提交时间</span><span class="kpi-value">' + escapeHtml(verification.submitted_at ? formatDate(verification.submitted_at) : "-") + '</span></div>',
      '</div>'
    ];

    if (verification.reviewed_at) {
      detailBlocks.push('<div class="footer-note" style="margin-top:16px;">审核于 ' + escapeHtml(formatDate(verification.reviewed_at)) + '</div>');
    }

    if (verification.reject_reason) {
      detailBlocks.push('<div class="real-name-alert real-name-alert--rejected">驳回原因: ' + escapeHtml(verification.reject_reason) + '</div>');
    } else if (verification.notice) {
      detailBlocks.push('<div class="real-name-alert">提示: ' + escapeHtml(verification.notice) + '</div>');
    }

    if (!verification.can_submit) {
      return [
        '<article class="section-card glass-card span-12">',
        '<div class="section-head"><div><span class="tiny-pill">实名认证</span><h3>认证信息</h3></div></div>',
        detailBlocks.join(""),
        '</article>'
      ].join("");
    }

    return [
      '<article class="section-card glass-card span-12">',
      '<div class="section-head"><div><span class="tiny-pill">实名认证</span><h3>提交认证</h3></div></div>',
      detailBlocks.join(""),
      '<form class="stack" data-form="real-name-verification" style="margin-top:24px; max-width: 560px;">',
      '<div class="field"><label>真实姓名</label><input name="real_name" type="text" placeholder="请输入您的法律姓名" value="' + escapeHtml(verification.real_name || "") + '" required /></div>',
      '<div class="field"><label>身份证号</label><input name="identity_no" type="text" placeholder="请输入 18 位身份证号码" maxlength="18" required /></div>',
      '<div class="field-help">您的身份证号码将以加密形式存储，并在控制台中脱敏显示。</div>',
      '<div class="form-actions" style="margin-top:12px;"><button class="btn btn-primary" type="submit">' + (verification.status === "rejected" ? "重新提交认证" : "提交认证") + '</button></div>',
      '</form>',
      '</article>'
    ].join("");
  }

  function renderSecuritySection() {
    return [
      '<article class="section-card glass-card span-12">',
      '<div class="section-head"><div><span class="tiny-pill">账户设置</span><h3>修改密码</h3></div></div>',
      '<form class="stack" data-form="change-password" style="margin-top:24px; max-width: 480px;">',
      '<div class="field"><label>旧密码</label><input name="old_password" type="password" placeholder="请输入当前使用的密码" required /></div>',
      '<div class="field"><label>新密码</label><input name="new_password" type="password" placeholder="请输入 8 位以上新密码" minlength="8" required /></div>',
      '<div class="field"><label>确认新密码</label><input name="confirm_password" type="password" placeholder="请再次输入新密码" minlength="8" required /></div>',
      '<div class="form-actions" style="margin-top:12px;">',
      '<button class="btn btn-primary" type="submit">立即修改</button>',
      '</div>',
      '</form>',
      "</article>",
      '<article class="section-card glass-card span-12" style="margin-top:24px;">',
      '<div class="section-head"><div><span class="tiny-pill">安全操作</span><h3>订阅令牌管理</h3></div></div>',
      '<div class="kpi-row" style="margin-top:20px;">',
      '<div class="kpi-box"><span class="kpi-label">IPv4 订阅</span><div class="toolbar" style="margin-top:12px;"><button class="btn btn-secondary" data-action="reset-security">重置令牌</button></div></div>',
      '<div class="kpi-box"><span class="kpi-label">IPv6 订阅</span><div class="toolbar" style="margin-top:12px;"><button class="btn btn-secondary" data-action="reset-ipv6-security">重置令牌</button></div></div>',
      '</div>',
      '<div class="footer-note" style="margin-top:16px;">提示：重置令牌后原本的订阅链接将立即失效，您需要重新在连接工具卡片中复制新链接。</div>',
      '</article>'
    ].join("");
  }

  function getThemeLogo() {
    var config = theme.config || {};
    if (state.themeMode === "light") {
      return config.lightLogoUrl || config.darkLogoUrl || theme.logo || "";
    }
    return config.darkLogoUrl || theme.logo || config.lightLogoUrl || "";
  }

  function updateThemeColorMeta() {
    var meta = document.querySelector('meta[name="theme-color"]');
    if (meta) {
      meta.setAttribute("content", state.themeMode === "light" ? "#eef4fb" : "#08101c");
    }
  }

  function getDefaultThemePreference() {
    var configMode = theme && theme.config ? theme.config.defaultThemeMode : "";
    if (configMode === "light" || configMode === "dark" || configMode === "system") {
      return configMode;
    }
    return "system";
  }

  function resolveThemeMode(preference) {
    if (preference === "light" || preference === "dark") {
      return preference;
    }
    return isSystemDarkMode() ? "dark" : "light";
  }

  function bindSystemThemeListener() {
    if (!themeMediaQuery) {
      return;
    }
    var onThemeChange = function () {
      if (state.themePreference === "system") {
        applyThemeMode();
        render();
      }
    };
    if (typeof themeMediaQuery.addEventListener === "function") {
      themeMediaQuery.addEventListener("change", onThemeChange);
      return;
    }
    if (typeof themeMediaQuery.addListener === "function") {
      themeMediaQuery.addListener(onThemeChange);
    }
  }

  function getThemeMediaQuery() {
    if (!window.matchMedia) {
      return null;
    }
    return window.matchMedia("(prefers-color-scheme: dark)");
  }

  function isSystemDarkMode() {
    return Boolean(themeMediaQuery && themeMediaQuery.matches);
  }

  function getMetricsBaseUrl() {
    var baseUrl = theme && theme.config ? theme.config.metricsBaseUrl : "";
    return String(baseUrl || "").replace(/\/+$/, "");
  }

  function getUplinkHeadline() {
    if (!Number.isFinite(state.uplinkMbps)) {
      return "Delivering -- Mbps";
    }
    return "Delivering " + formatUplinkMbps(state.uplinkMbps) + " Mbps";
  }

  function renderUplinkMetricStack() {
    return [
      '<div class="hero-metric-line">Delivering</div>',
      '<div class="hero-metric-value"><em>' + escapeHtml(getUplinkValueText()) + '</em></div>',
      '<div class="hero-metric-line">Uplink Bandwidth</div>'
    ].join("");
  }

  function getUplinkValueText() {
    if (!Number.isFinite(state.uplinkMbps)) {
      return "-- Mbps";
    }
    return formatUplinkMbps(state.uplinkMbps) + " Mbps";
  }

  function formatUplinkMbps(value) {
    if (value >= 100) {
      return String(Math.round(value));
    }
    if (value >= 10) {
      return value.toFixed(1);
    }
    return value.toFixed(2);
  }

  function isValidRoute(route) {
    return [
      "overview",
      "access-history",
      "nodes",
      "ipv6-nodes",
      "tickets",
      "real-name",
      "security"
    ].indexOf(route) !== -1;
  }

  function getRouteTitle(route) {
    if (route === "access-history") {
      return "访问记录";
    }
    if (route === "nodes") {
      return "节点";
    }
    if (route === "ipv6-nodes") {
      return "IPv6 节点";
    }
    if (route === "notices") {
      return "知识库";
    }
    if (route === "tickets") {
      return "工单";
    }
    if (route === "real-name") {
      return "\u5b9e\u540d\u8ba4\u8bc1";
    }
    if (route === "security") {
      return "账号安全";
    }
    return "总览";
  }

  function tabButton(mode, label, active) {
    return '<button class="form-tab ' + (active ? "active" : "") + '" data-action="switch-mode" data-mode="' + mode + '">' + label + "</button>";
  }

  function getStoredToken() {
    var candidates = [];
    collectStorageValues(window.localStorage, ["access_token", "auth_data", "__nebula_auth_data__", "token", "auth_token"], candidates);
    collectStorageValues(window.sessionStorage, ["access_token", "auth_data", "__nebula_auth_data__", "token", "auth_token"], candidates);
    for (var i = 0; i < candidates.length; i += 1) {
      var token = extractToken(candidates[i]);
      if (token) {
        return token;
      }
    }
    return "";
  }

  function saveToken(token) {
    var normalized = normalizeAuthHeader(token);
    window.localStorage.setItem("access_token", normalized);
    window.localStorage.setItem("auth_data", normalized);
    window.localStorage.setItem("__nebula_auth_data__", normalized);
    window.sessionStorage.setItem("access_token", normalized);
    window.sessionStorage.setItem("auth_data", normalized);
    window.sessionStorage.setItem("__nebula_auth_data__", normalized);
  }

  function clearToken() {
    window.localStorage.removeItem("access_token");
    window.localStorage.removeItem("auth_data");
    window.localStorage.removeItem("__nebula_auth_data__");
    window.sessionStorage.removeItem("access_token");
    window.sessionStorage.removeItem("auth_data");
    window.sessionStorage.removeItem("__nebula_auth_data__");
    state.authToken = "";
  }

  function getStoredIpv6Token() {
    var stored = "";
    try {
      stored = window.localStorage.getItem("__nebula_ipv6_auth_data__") || "";
    } catch (e) {
      stored = "";
    }
    return stored;
  }

  function saveIpv6Token(token) {
    window.localStorage.setItem("__nebula_ipv6_auth_data__", normalizeAuthHeader(token));
  }

  function clearIpv6Token() {
    window.localStorage.removeItem("__nebula_ipv6_auth_data__");
    state.ipv6AuthToken = "";
  }

  function resetIpv6DashboardState() {
    clearIpv6Token();
    state.ipv6User = null;
    state.ipv6Subscribe = null;
    state.ipv6Eligibility = null;
    state.ipv6Servers = [];
    state.ipv6SessionOverview = null;
    state.ipv6CachedSubNodes = null;
    window.sessionStorage.removeItem("__nebula_ipv6_sub_content__");
  }

  function handleIpv6AuthFailure(error) {
    if (!error || (error.status !== 401 && error.status !== 403)) {
      return false;
    }
    resetIpv6DashboardState();
    return true;
  }

  function resetDashboard() {
    state.user = null;
    state.subscribe = null;
    state.stats = null;
    state.knowledge = [];
    state.tickets = [];
    state.selectedTicketId = null;
    state.selectedTicket = null;
    state.servers = [];
    state.ipv6AuthToken = "";
    state.ipv6User = null;
    state.ipv6Subscribe = null;
    state.ipv6Eligibility = null;
    state.ipv6Servers = [];
    state.sessionOverview = null;
    state.ipv6SessionOverview = null;
    state.realNameVerification = null;
    state.appConfig = null;
    state.cachedSubNodes = null;
    state.ipv6CachedSubNodes = null;
    state.loading = false;
  }

  function applyCustomBackground() {
    if (!theme.config || !theme.config.backgroundUrl) {
      document.documentElement.style.removeProperty("--nebula-bg-image");
      return;
    }
    document.documentElement.style.setProperty("--nebula-bg-image", 'url("' + String(theme.config.backgroundUrl).replace(/"/g, '\\"') + '")');
  }

  function getVerifyToken() {
    var url = new URL(window.location.href);
    var directVerify = url.searchParams.get("verify");
    if (directVerify) {
      return directVerify;
    }
    if (window.location.hash.indexOf("?") !== -1) {
      return new URLSearchParams(window.location.hash.split("?")[1] || "").get("verify");
    }
    return "";
  }

  function clearVerifyToken() {
    var url = new URL(window.location.href);
    url.searchParams.delete("verify");
    if (window.location.hash.indexOf("?") !== -1) {
      window.history.replaceState({}, "", url.pathname + url.search + window.location.hash.split("?")[0]);
      return;
    }
    window.history.replaceState({}, "", url.pathname + url.search);
  }

  function showMessage(message, type) {
    var container = document.getElementById("nebula-toasts");
    if (!container) return;
    
    if (!message) {
      container.innerHTML = "";
      return;
    }

    container.innerHTML = '<div class="nebula-toast-container"><div class="message-banner ' + escapeHtml(type || "") + '">' + escapeHtml(message) + "</div></div>";
    
    // Auto clear after 4s
    if (state.messageTimeout) clearTimeout(state.messageTimeout);
    state.messageTimeout = window.setTimeout(function() {
      container.innerHTML = "";
    }, 4000);
  }

  function hideLoader() {
    if (!loader) {
      return;
    }
    loader.classList.add("is-hidden");
    window.setTimeout(function () {
      if (loader && loader.parentNode) {
        loader.parentNode.removeChild(loader);
        loader = null;
      }
    }, 350);
  }

  function collectStorageValues(storage, keys, target) {
    if (!storage) {
      return;
    }
    for (var i = 0; i < keys.length; i += 1) {
      pushCandidate(storage.getItem(keys[i]), target);
    }
  }

  function pushCandidate(value, target) {
    if (value === null || typeof value === "undefined" || value === "") {
      return;
    }
    target.push(value);
  }

  function extractToken(value, depth) {
    depth = depth || 0;
    if (depth > 4 || value === null || typeof value === "undefined") {
      return "";
    }
    if (typeof value === "string") {
      var trimmed = value.trim();
      if (!trimmed) {
        return "";
      }
      if (trimmed.indexOf("Bearer ") === 0) {
        return trimmed;
      }
      if (/^[A-Za-z0-9\-_=]+\.[A-Za-z0-9\-_=]+(\.[A-Za-z0-9\-_.+/=]+)?$/.test(trimmed)) {
        return "Bearer " + trimmed;
      }
      if (/^[A-Za-z0-9\-_.+/=]{24,}$/.test(trimmed) && trimmed.indexOf("{") === -1) {
        return "Bearer " + trimmed;
      }
      if ((trimmed.charAt(0) === "{" && trimmed.charAt(trimmed.length - 1) === "}") ||
        (trimmed.charAt(0) === "[" && trimmed.charAt(trimmed.length - 1) === "]")) {
        try {
          return extractToken(JSON.parse(trimmed), depth + 1);
        } catch (error) {
          return "";
        }
      }
      return "";
    }
    if (Array.isArray(value)) {
      for (var i = 0; i < value.length; i += 1) {
        var arrayToken = extractToken(value[i], depth + 1);
        if (arrayToken) {
          return arrayToken;
        }
      }
      return "";
    }
    if (typeof value === "object") {
      var directKeys = ["access_token", "auth_data", "token", "Authorization", "authorization"];
      for (var j = 0; j < directKeys.length; j += 1) {
        if (Object.prototype.hasOwnProperty.call(value, directKeys[j])) {
          var directToken = extractToken(value[directKeys[j]], depth + 1);
          if (directToken) {
            return directToken;
          }
        }
      }
    }
    return "";
  }

  function normalizeAuthHeader(token) {
    var trimmed = String(token || "").trim();
    if (!trimmed) {
      return "";
    }
    if (trimmed.indexOf("Bearer ") === 0) {
      return trimmed;
    }
    return "Bearer " + trimmed;
  }

  function copyText(value, successMessage) {
    if (!value) {
      return;
    }
    navigator.clipboard.writeText(value).then(function () {
      showMessage(successMessage || "内容已复制到剪贴板", "success");
      render();
    }).catch(function () {
      showMessage("复制失败，请尝试手动复制", "error");
      render();
    });
  }

  function formatTraffic(value) {
    var units = ["B", "KB", "MB", "GB", "TB"];
    var size = Number(value || 0);
    var index = 0;
    while (size >= 1024 && index < units.length - 1) {
      size /= 1024;
      index += 1;
    }
    return size.toFixed(size >= 10 || index === 0 ? 0 : 1) + " " + units[index];
  }

  function formatMoney(value, symbol) {
    return (symbol || "$") + (Number(value || 0) / 100).toFixed(2);
  }

  function formatSpeed(value) {
    if (!value) {
      return "不限";
    }
    return value + " Mbps";
  }

  function formatLimit(value) {
    if (value === null || typeof value === "undefined" || value === "") {
      return "不限";
    }
    return String(value);
  }

  function formatDate(value) {
    var timestamp = toTimestamp(value);
    if (!timestamp) {
      return "-";
    }
    return new Date(timestamp * 1000).toLocaleString();
  }

  function toTimestamp(value) {
    if (!value) {
      return null;
    }
    if (typeof value === "number") {
      return value;
    }
    var parsed = Date.parse(value);
    return Number.isNaN(parsed) ? null : Math.floor(parsed / 1000);
  }

  function truncate(text, maxLength) {
    var str = String(text || "");
    if (str.length <= maxLength) {
      return str;
    }
    return str.slice(0, maxLength - 1) + "...";
  }

  function firstNonEmpty(values) {
    for (var i = 0; i < values.length; i += 1) {
      if (values[i]) {
        return values[i];
      }
    }
    return "";
  }

  function escapeHtml(value) {
    return String(value || "")
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#39;");
  }

  function renderKnowledgeSection(articles) {
    if (!articles.length) {
      return [
        '<article class="section-card glass-card span-6">',
        '<div class="section-head"><div><span class="tiny-pill">Knowledge</span><h3>Knowledge Base</h3></div></div>',
        '<div class="empty-state">No knowledge articles yet.</div>',
        "</article>"
      ].join("");
    }

    return [
      '<article class="section-card glass-card span-12">',
      '<div class="section-head"><div><span class="tiny-pill">Knowledge</span><h3>Knowledge Base</h3></div></div>',
      '<div class="notice-list">' + articles.map(function (article) {
        return [
          '<button class="notice-item notice-item--interactive" type="button" data-action="open-knowledge-article" data-article-id="' + escapeHtml(String(article.id || "")) + '">',
          '<div class="notice-copy">',
          '<div class="notice-title-row"><span class="tiny-pill">Knowledge</span><strong>' + escapeHtml(article.title || "Untitled article") + '</strong></div>',
          '<span class="notice-meta">' + escapeHtml(truncate(article.body || article.content || "", 160)) + "</span>",
          "</div>",
          '<div class="inline-actions">' + (article.category ? '<span class="tiny-pill">' + escapeHtml(article.category) + '</span>' : '<span class="tiny-pill">' + escapeHtml(formatDate(article.updated_at || article.created_at)) + '</span>') + '<span class="notice-open-indicator">Open</span></div>',
          "</button>"
        ].join("");
      }).join("") + "</div>",
      "</article>"
    ].join("");
  }

  function getMetricsBaseUrl() {
    var baseUrl = theme && theme.config ? theme.config.metricsBaseUrl : "";
    baseUrl = normalizeUrlValue(baseUrl);
    return String(baseUrl || "").replace(/\/+$/, "");
  }

  function applyCustomBackground() {
    var backgroundUrl = theme.config ? normalizeUrlValue(theme.config.backgroundUrl) : "";
    if (!backgroundUrl) {
      document.documentElement.style.removeProperty("--nebula-bg-image");
      return;
    }
    document.documentElement.style.setProperty("--nebula-bg-image", 'url("' + String(backgroundUrl).replace(/"/g, '\\"') + '")');
  }

  function getThemeLogo() {
    var config = theme.config || {};
    var lightLogo = normalizeUrlValue(config.lightLogoUrl);
    var darkLogo = normalizeUrlValue(config.darkLogoUrl);
    var themeLogo = normalizeUrlValue(theme.logo);
    if (state.themeMode === "light") {
      return lightLogo || darkLogo || themeLogo || "";
    }
    return darkLogo || themeLogo || lightLogo || "";
  }

  function normalizeUrlValue(value) {
    var normalized = String(value == null ? "" : value).trim();
    if (!normalized) {
      return "";
    }
    if (normalized === '""' || normalized === "''" || normalized === "null" || normalized === "undefined") {
      return "";
    }
    if ((normalized.charAt(0) === '"' && normalized.charAt(normalized.length - 1) === '"') ||
      (normalized.charAt(0) === "'" && normalized.charAt(normalized.length - 1) === "'")) {
      normalized = normalized.slice(1, -1).trim();
    }
    return normalized;
  }

  function findKnowledgeArticleById(articleId) {
    return (state.knowledge || []).find(function (article) {
      return String(article.id) === String(articleId);
    }) || null;
  }

  function renderKnowledgeArticleBody(article) {
    var content = String((article && (article.body || article.content)) || "").trim();
    if (!content) {
      return '<p class="knowledge-article-empty">No article body provided.</p>';
    }

    return '<div class="knowledge-article-body">' + escapeHtml(content).replace(/\r?\n/g, "<br>") + '</div>';
  }

  function showKnowledgeArticleModal(article) {
    var overlay = document.createElement("div");
    overlay.className = "nebula-modal-overlay";

    var modal = document.createElement("div");
    modal.className = "nebula-modal nebula-modal--article";
    modal.innerHTML = [
      '<div class="knowledge-article-head">',
      '<span class="tiny-pill">Knowledge</span>',
      '<h3 class="nebula-modal-title">' + escapeHtml(article.title || "Untitled article") + '</h3>',
      '</div>',
      '<div class="knowledge-article-meta">',
      article.category ? '<span class="tiny-pill">' + escapeHtml(article.category) + '</span>' : "",
      '<span>' + escapeHtml(formatDate(article.updated_at || article.created_at)) + '</span>',
      '</div>',
      renderKnowledgeArticleBody(article),
      '<div class="nebula-modal-footer">',
      '<button class="btn btn-ghost" type="button" data-role="close-knowledge-article">Close</button>',
      '</div>'
    ].join("");

    document.body.appendChild(overlay);
    overlay.appendChild(modal);

    var close = function () {
      if (overlay.parentNode) {
        overlay.parentNode.removeChild(overlay);
      }
    };

    var closeButton = modal.querySelector('[data-role="close-knowledge-article"]');
    if (closeButton) {
      closeButton.onclick = close;
    }

    overlay.onclick = function (event) {
      if (event.target === overlay) {
        close();
      }
    };
  }

  function showNebulaModal(options) {
    var overlay = document.createElement("div");
    overlay.className = "nebula-modal-overlay";
    
    var modal = document.createElement("div");
    modal.className = "nebula-modal";
    
    var title = document.createElement("h3");
    title.className = "nebula-modal-title";
    title.innerText = options.title || "操作确认";
    
    var body = document.createElement("div");
    body.className = "nebula-modal-body";
    body.innerText = options.content || "";
    
    var footer = document.createElement("div");
    footer.className = "nebula-modal-footer";
    
    var cancelBtn = document.createElement("button");
    cancelBtn.className = "btn btn-ghost";
    cancelBtn.innerText = options.cancelText || "取消";
    cancelBtn.onclick = function() {
      document.body.removeChild(overlay);
      if (options.onCancel) options.onCancel();
    };
    
    var confirmBtn = document.createElement("button");
    confirmBtn.className = "btn btn-primary";
    confirmBtn.innerText = options.confirmText || "确定提交";
    confirmBtn.onclick = function() {
      document.body.removeChild(overlay);
      if (options.onConfirm) options.onConfirm();
    };
    
    footer.appendChild(cancelBtn);
    footer.appendChild(confirmBtn);
    
    modal.appendChild(title);
    modal.appendChild(body);
    modal.appendChild(footer);
    overlay.appendChild(modal);
    
    document.body.appendChild(overlay);
    
    // Auto-close on overlay click
    overlay.onclick = function(e) {
      if (e.target === overlay) {
        document.body.removeChild(overlay);
        if (options.onCancel) options.onCancel();
      }
    };
  }
})();
