(function () {
  "use strict";

  const TOKEN_KEY = "airshelf_token";
  const LIVE_KEY = "airshelf_live";
  const USER_KEY = "airshelf_user";
  const TEAM_KEY = "airshelf_team";
  const BRIDGE_VERSION = "20260601-live-api-bridge-notifications";
  const CACHE_VERSION = "20260601";
  const CACHE_PREFIX = "airshelf:live-cache:";
  const requestMemo = {};
  const context = window.__AIR_SHELF_EXACT_CONTEXT__ || {};
  const sourceMeta = document.querySelector('meta[name="x-airshelf-exact-source"]');
  const page = (context.page || (sourceMeta && sourceMeta.content) || location.pathname.split("/").pop() || "index.html").toLowerCase();
  const query = new URLSearchParams(context.search || location.search);
  const liveDataPages = {
    "index.html": true,
    "products.html": true,
    "product-detail.html": true,
    "projects.html": true,
    "projects-new.html": true,
    "pipeline.html": true,
    "library.html": true,
    "account.html": true,
    "settings.html": true,
    "team.html": true,
    "messages.html": true,
  };
  const needsLiveHydration = Boolean(context.liveHydrate || liveDataPages[page]);

  function token() {
    return localStorage.getItem(TOKEN_KEY) || "";
  }

  function isLive() {
    return query.get("live") === "1" || localStorage.getItem(LIVE_KEY) === "1";
  }

  function canUseApi() {
    return !!token();
  }

  function cacheScope() {
    try {
      const team = JSON.parse(localStorage.getItem(TEAM_KEY) || "null");
      if (team?.id) return "team:" + team.id;
      const user = JSON.parse(localStorage.getItem(USER_KEY) || "null");
      if (user?.id) return "user:" + user.id;
    } catch (error) {
      // Fall through to anonymous scope.
    }
    return "anonymous";
  }

  function cacheKey(name) {
    return CACHE_PREFIX + CACHE_VERSION + ":" + cacheScope() + ":" + name;
  }

  function readCache(name) {
    try {
      const raw = localStorage.getItem(cacheKey(name));
      if (!raw) return null;
      const parsed = JSON.parse(raw);
      return parsed && parsed.data !== undefined ? parsed.data : null;
    } catch (error) {
      return null;
    }
  }

  function writeCache(name, data) {
    try {
      localStorage.setItem(cacheKey(name), JSON.stringify({ ts: Date.now(), data: data }));
      renderShellFromCache();
    } catch (error) {
      // localStorage may be full or unavailable; live API rendering still works.
    }
  }

  function forgetCache(name) {
    try {
      localStorage.removeItem(cacheKey(name));
      delete requestMemo[name];
      renderShellFromCache();
    } catch (error) {
      // Best effort only.
    }
  }

  function apiGet(cacheName, path) {
    if (!requestMemo[cacheName]) {
      requestMemo[cacheName] = api(path)
        .then(function (data) {
          writeCache(cacheName, data);
          return data;
        })
        .catch(function (error) {
          delete requestMemo[cacheName];
          throw error;
        });
    }
    return requestMemo[cacheName];
  }

  function rememberAuthShape(meData) {
    if (!meData) return;
    try {
      if (meData.user) localStorage.setItem(USER_KEY, JSON.stringify(meData.user));
      if (meData.team) localStorage.setItem(TEAM_KEY, JSON.stringify(meData.team));
    } catch (error) {
      // Best effort only.
    }
  }

  function cachedAuthShape() {
    const cached = readCache("auth:me");
    if (cached) return cached;
    try {
      const user = JSON.parse(localStorage.getItem(USER_KEY) || "null");
      const team = JSON.parse(localStorage.getItem(TEAM_KEY) || "null");
      return user || team ? { user: user || {}, team: team || {} } : null;
    } catch (error) {
      return null;
    }
  }

  async function loadWithCache(cacheName, fetcher, render, failTitle) {
    const cached = readCache(cacheName);
    if (cached) {
      render(cached, true);
      markHydrationDone();
    }
    try {
      const fresh = await fetcher();
      writeCache(cacheName, fresh);
      render(fresh, false);
      return fresh;
    } catch (error) {
      if (cached) {
        toast(failTitle || "数据刷新失败", (error && error.message ? error.message : "接口暂不可用") + " · 已显示本地缓存");
        return cached;
      }
      toast(failTitle || "数据加载失败", error.message);
      throw error;
    }
  }

  function markHydrationLoading() {
    if (!needsLiveHydration || !canUseApi()) return;
    document.documentElement.removeAttribute("data-live-error");
    document.documentElement.setAttribute("data-live-hydrating", "1");
  }

  function markHydrationDone() {
    if (!needsLiveHydration) return;
    document.documentElement.removeAttribute("data-live-hydrating");
    document.documentElement.removeAttribute("data-live-error");
    document.documentElement.setAttribute("data-live-ready", "1");
  }

  function markHydrationError(message) {
    if (!needsLiveHydration) return;
    document.documentElement.removeAttribute("data-live-hydrating");
    document.documentElement.setAttribute("data-live-error", "1");
    document.documentElement.dataset.liveErrorMessage = message || "真实数据加载失败";
  }

  function applyContextHash() {
    const cleanHash = String(context.hash || location.hash || "").replace(/^#/, "");
    if (!cleanHash) return;
    if (page === "settings.html" && cleanHash.indexOf("sec-") === 0 && typeof window.showSection === "function") {
      window.showSection(cleanHash);
      return;
    }
    const stageMatch = cleanHash.match(/^stage-(\d+)$/);
    if (page === "pipeline.html" && stageMatch && typeof window.activateStage === "function") {
      window.activateStage(Number(stageMatch[1]));
    }
  }

  function go(href) {
    if (typeof window.__AIR_SHELF_HOST_NAVIGATE__ === "function") {
      window.__AIR_SHELF_HOST_NAVIGATE__(href);
      return;
    }
    location.href = href;
  }

  function ready(fn) {
    if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", fn);
    else fn();
  }

  function esc(value) {
    return String(value == null ? "" : value).replace(/[&<>"']/g, function (char) {
      return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[char];
    });
  }

  function dateOnly(value) {
    if (!value) return new Date().toISOString().slice(0, 10);
    return String(value).slice(0, 10);
  }

  function shortLabel(value) {
    const text = String(value || "商品").replace(/[·｜|]/g, " ").trim();
    return text.length > 9 ? text.slice(0, 9) : text;
  }

  function parseError(text) {
    try {
      const data = JSON.parse(text);
      if (typeof data.detail === "string") return data.detail;
      if (Array.isArray(data.non_field_errors)) return data.non_field_errors.join(" / ");
      const parts = [];
      Object.keys(data).forEach(function (key) {
        const value = data[key];
        if (Array.isArray(value)) parts.push(key + ": " + value.join(" / "));
        else if (typeof value === "string") parts.push(key + ": " + value);
      });
      return parts.join("；") || text;
    } catch (error) {
      return text || "请求失败";
    }
  }

  async function api(path, options) {
    const opts = options || {};
    const headers = new Headers(opts.headers || {});
    if (!(opts.body instanceof FormData)) headers.set("Content-Type", "application/json");
    if (token()) headers.set("Authorization", "Token " + token());
    const response = await fetch(path, Object.assign({}, opts, { headers: headers }));
    if (!response.ok) {
      const text = await response.text();
      throw new Error(parseError(text));
    }
    if (response.status === 204) return null;
    return response.json();
  }

  function toast(title, sub) {
    if (window.Shell && typeof window.Shell.toast === "function") {
      window.Shell.toast(title, sub || "");
      return;
    }
    if (typeof window._loginToast === "function") {
      window._loginToast(title, sub || "");
      return;
    }
    if (typeof window._regToast === "function") {
      window._regToast(title, sub || "");
      return;
    }
    let el = document.getElementById("__api_bridge_toast");
    if (!el) {
      el = document.createElement("div");
      el.id = "__api_bridge_toast";
      el.style.cssText =
        "position:fixed;left:50%;bottom:36px;transform:translateX(-50%) translateY(20px);background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:12px 18px;box-shadow:0 8px 24px rgba(0,0,0,.12);display:flex;flex-direction:column;gap:2px;opacity:0;transition:opacity .2s,transform .2s;z-index:9999;font-family:inherit;max-width:380px;";
      document.body.appendChild(el);
    }
    el.innerHTML =
      '<div style="font-size:13.5px;font-weight:600;color:#262626;">' +
      esc(title) +
      "</div>" +
      (sub ? '<div style="font-size:11.5px;color:rgba(0,0,0,.56);">// ' + esc(sub) + "</div>" : "");
    requestAnimationFrame(function () {
      el.style.opacity = "1";
      el.style.transform = "translateX(-50%) translateY(0)";
    });
    clearTimeout(el._timer);
    el._timer = setTimeout(function () {
      el.style.opacity = "0";
      el.style.transform = "translateX(-50%) translateY(20px)";
    }, 3000);
  }

  function setAuthPayload(payload) {
    localStorage.setItem(TOKEN_KEY, payload.token);
    localStorage.setItem(LIVE_KEY, "1");
    localStorage.setItem(USER_KEY, JSON.stringify(payload.user || {}));
    localStorage.setItem(TEAM_KEY, JSON.stringify(payload.team || {}));
  }

  function renderShellIdentity(meData, billingData) {
    if (meData) rememberAuthShape(meData);
    const teamName = meData?.team?.name || "团队";
    const userName = meData?.user ? displayName(meData.user) : "成员";
    const email = meData?.user?.email || "";
    const avatar = initial(userName);

    document.querySelectorAll(".aside-foot .user .em, .shell-account-head .nm").forEach(function (el) {
      el.textContent = teamName;
    });
    document.querySelectorAll(".aside-foot .user .av, .topbar-avatar span, .shell-account-head .av").forEach(function (el) {
      el.textContent = avatar;
    });
    document.querySelectorAll(".shell-account-head .mail").forEach(function (el) {
      el.textContent = email;
    });
    syncShellBalance(billingData);
  }

  function syncShellBalance(billingData) {
    const balance = billingData?.account?.balance;
    document.querySelectorAll(".balance-chip strong").forEach(function (el) {
      el.textContent = balance === undefined || balance === null ? "--" : money(balance);
    });
  }

  function firstPresent(values) {
    for (let i = 0; i < values.length; i += 1) {
      if (values[i] !== null && values[i] !== undefined) return values[i];
    }
    return null;
  }

  function collectionCount(data) {
    if (!data) return null;
    if (typeof data.count === "number") return data.count;
    if (typeof data.count === "string" && data.count.trim() !== "" && !Number.isNaN(Number(data.count))) return Number(data.count);
    if (Array.isArray(data.results)) return data.results.length;
    if (Array.isArray(data)) return data.length;
    return null;
  }

  function listResults(data) {
    if (Array.isArray(data)) return data;
    if (data && Array.isArray(data.results)) return data.results;
    return [];
  }

  function unreadNotificationCount(data) {
    if (!data) return null;
    if (typeof data.unread_count === "number") return data.unread_count;
    if (typeof data.unread_count === "string" && data.unread_count.trim() !== "" && !Number.isNaN(Number(data.unread_count))) {
      return Number(data.unread_count);
    }
    const rows = listResults(data);
    if (!rows.length && collectionCount(data) === 0) return 0;
    return rows.filter(function (item) {
      if (item.unread !== undefined) return Boolean(item.unread);
      return item.is_read === false;
    }).length;
  }

  function emptyList() {
    return { count: 0, next: null, previous: null, results: [] };
  }

  function listCacheOrEmpty(name) {
    return readCache(name) || emptyList();
  }

  function cacheNested(name, key) {
    const data = readCache(name);
    return data && data[key] ? data[key] : null;
  }

  function setShellBadge(href, count) {
    document.querySelectorAll('aside.sidebar a[href="' + href + '"] .pill-mini').forEach(function (badge) {
      if (count === null || count === undefined) {
        badge.textContent = "";
        badge.hidden = true;
        return;
      }
      badge.hidden = false;
      badge.textContent = String(count);
    });
  }

  function syncNotificationBadge() {
    document.querySelectorAll(".count-noti").forEach(function (badge) {
      const count = firstPresent([
        readCache("notifications:summary")?.unread_count,
        unreadNotificationCount(readCache("notifications:list")),
      ]);
      if (count === null || count === undefined || Number(count) <= 0) {
        badge.textContent = "";
        badge.hidden = true;
        return;
      }
      badge.hidden = false;
      badge.textContent = String(count);
    });
  }

  function shellSummaryFromCache() {
    return firstPresent([
      readCache("billing:summary"),
      cacheNested("dashboard", "summary"),
      cacheNested("account:bundle", "summary"),
      cacheNested("team:bundle", "summary"),
      cacheNested("settings:profile", "summary"),
    ]);
  }

  function shellProductDataFromCache() {
    return firstPresent([
      readCache("products:list"),
      cacheNested("dashboard", "products"),
      cacheNested("account:bundle", "products"),
      cacheNested("projects:bundle", "products"),
      cacheNested("pipeline:latest", "products"),
    ]);
  }

  function shellProjectDataFromCache() {
    return firstPresent([
      readCache("projects:list"),
      cacheNested("dashboard", "projects"),
      cacheNested("account:bundle", "projects"),
      cacheNested("projects:bundle", "projects"),
    ]);
  }

  function renderShellFromCache() {
    if (!document.body) return;
    const meData = cachedAuthShape();
    const summaryData = shellSummaryFromCache();
    if (canUseApi() || meData || summaryData) renderShellIdentity(meData, summaryData);
    setShellBadge("products.html", collectionCount(shellProductDataFromCache()));
    setShellBadge("projects.html", collectionCount(shellProjectDataFromCache()));
    syncNotificationBadge();
  }

  function seedProjectsBundleFromCache() {
    if (readCache("projects:bundle")) return;
    const products = shellProductDataFromCache();
    const projects = shellProjectDataFromCache();
    if (!products && !projects) return;
    writeCache("projects:bundle", {
      products: products || emptyList(),
      projects: projects || emptyList(),
    });
  }

  function seedAccountBundleFromCache() {
    if (readCache("account:bundle")) return;
    const summary = shellSummaryFromCache();
    const ledgers = readCache("billing:ledgers");
    const members = readCache("team:members");
    const products = shellProductDataFromCache();
    const projects = shellProjectDataFromCache();
    if (!summary && !ledgers && !members && !products && !projects) return;
    writeCache("account:bundle", {
      summary: summary || { account: { balance: 0 }, charged_total: 0 },
      ledgers: ledgers || [],
      members: members || [],
      products: products || emptyList(),
      projects: projects || emptyList(),
    });
  }

  function seedTeamBundleFromCache() {
    if (readCache("team:bundle")) return;
    const me = cachedAuthShape();
    const summary = readCache("billing:summary");
    const members = readCache("team:members");
    if (!me && !summary && !members) return;
    writeCache("team:bundle", {
      me: me || {},
      summary: summary || { account: { balance: 0 }, charged_total: 0 },
      members: members || [],
    });
  }

  async function hydrateShellIdentity() {
    if (!canUseApi()) return;
    const cachedMe = cachedAuthShape();
    const cachedBilling = readCache("billing:summary");
    renderShellFromCache();
    if (cachedMe) renderShellIdentity(cachedMe, cachedBilling);
    try {
      const meData = await apiGet("auth:me", "/api/auth/me/");
      writeCache("auth:me", meData);
      const billingData = await apiGet("billing:summary", "/api/billing/summary/").catch(function () {
        return null;
      });
      if (billingData) writeCache("billing:summary", billingData);
      renderShellIdentity(meData, billingData || cachedBilling);
      renderShellFromCache();
    } catch (error) {
      // Shell identity is a convenience layer; page-specific API errors are handled below.
    }
  }

  function setButtonBusy(button, text) {
    if (!button) return function () {};
    const html = button.innerHTML;
    button.disabled = true;
    button.innerHTML =
      '<span style="font-family:var(--font-mono);font-size:12px;letter-spacing:.04em;">// ' +
      esc(text) +
      "</span>";
    return function () {
      button.disabled = false;
      button.innerHTML = html;
    };
  }

  function wireAuth() {
    if (page === "login.html") {
      window.doLogin = async function () {
        const email = document.getElementById("auth-email").value.trim();
        const password = document.getElementById("auth-pwd").value;
        const restore = setButtonBusy(document.querySelector(".btn-cta"), "验证中...");
        try {
          const payload = await api("/api/auth/login/", {
            method: "POST",
            body: JSON.stringify({ username: email, password: password }),
          });
          setAuthPayload(payload);
          toast("登录成功", "已接入 Django 真实会话");
          go("index.html");
        } catch (error) {
          restore();
          toast("登录失败", error.message || "请检查账号密码");
        }
      };
    }

    if (page === "register.html") {
      window.doRegister = async function () {
        const team = document.getElementById("reg-team").value.trim();
        const email = document.getElementById("reg-email").value.trim();
        const password = document.getElementById("reg-pwd").value;
        const confirm = document.getElementById("reg-pwd2").value;
        const agree = document.getElementById("reg-agree").checked;
        if (!team || !email) return alert("请补全团队名 + 邮箱");
        if (password.length < 8) return alert("密码至少 8 位");
        if (password !== confirm) return alert("两次密码不一致");
        if (!agree) return alert("请同意用户协议");

        const restore = setButtonBusy(document.getElementById("reg-submit"), "创建团队中...");
        try {
          const payload = await api("/api/auth/register/", {
            method: "POST",
            body: JSON.stringify({
              username: email,
              email: email,
              password: password,
              team_name: team,
            }),
          });
          setAuthPayload(payload);
          toast("注册成功", "团队与试用额度已创建");
          go("index.html");
        } catch (error) {
          restore();
          toast("注册失败", error.message || "请稍后重试");
        }
      };
    }
  }

  function sellingPointsFromDrawer() {
    const points = Array.from(document.querySelectorAll("#pf-bullets .bl-item .bl-text"))
      .map(function (el) {
        return el.textContent.trim();
      })
      .filter(Boolean);
    const pending = document.querySelector("#pf-bullets .bl-add .bl-input");
    if (pending && pending.value.trim()) points.push(pending.value.trim());
    return points;
  }

  let productCreateInFlight = false;

  async function submitLiveProduct(saveBtn) {
    if (!canUseApi()) return false;
    if (productCreateInFlight) return true;

    const nameEl = document.getElementById("pf-name");
    const catEl = document.getElementById("pf-cat");
    const targetEl = document.getElementById("pf-target");
    const name = (nameEl && nameEl.value ? nameEl.value : "").trim();
    const category = catEl ? catEl.value : "";
    const target = targetEl ? targetEl.value.trim() : "";
    const points = sellingPointsFromDrawer();

    if (!name) {
      toast("请填写商品名称", "必填项");
      if (nameEl) nameEl.focus();
      return true;
    }
    if (points.length === 0) {
      toast("请填写核心卖点", "至少 1 条");
      return true;
    }

    productCreateInFlight = true;
    const restore = setButtonBusy(saveBtn, "保存商品中...");
    try {
      const product = await api("/api/products/", {
        method: "POST",
        body: JSON.stringify({
          title: name,
          category: category,
          target_audience: target,
          selling_points: points.map(function (title, index) {
            return { title: title, detail: "", sort_order: index };
          }),
        }),
      });
      rememberPrototypeProduct(product, {
        title: name,
        category: category,
        target: target,
        points: points,
      });
      forgetCache("products:list");
      forgetCache("projects:bundle");
      forgetCache("dashboard");
      localStorage.setItem("airshelf_current_product_id", product.id);
      toast("商品已创建", "已写入 Django 数据库");
      go("product-detail.html?product_id=" + encodeURIComponent(product.id) + "&product=" + encodeURIComponent(product.title || name));
    } catch (error) {
      productCreateInFlight = false;
      restore();
      toast("创建失败", error.message);
    }
    return true;
  }

  function rememberPrototypeProduct(product, draft) {
    try {
      const key = "fs-extra-products";
      const list = JSON.parse(sessionStorage.getItem(key) || "[]");
      list.push({
        id: product.id,
        name: product.title || draft.title,
        cat: product.category || draft.category || "未分类",
        target: product.target_audience || draft.target || "",
        assets: 0,
        videos: 0,
        bullets: draft.points || [],
        date: dateOnly(product.created_at),
        createdAt: Date.now(),
      });
      sessionStorage.setItem(key, JSON.stringify(list));
    } catch (error) {
      // Prototype compatibility only; API persistence is already complete.
    }
  }

  function productCardHTML(product, index) {
    const title = product.title || "未命名商品";
    const cat = product.category || "未分类";
    const date = dateOnly(product.created_at);
    const assets = (product.images && product.images.length) || 0;
    const videos = product.metadata && product.metadata.videos_count ? product.metadata.videos_count : 0;
    return (
      '<div class="product-card" data-live="1" data-product-id="' +
      esc(product.id) +
      '" data-cat="' +
      esc(cat) +
      '" data-name="' +
      esc(title) +
      '" data-tags="" data-added="' +
      esc(index + 1) +
      '" data-assets="' +
      esc(assets) +
      '" data-videos="' +
      esc(videos) +
      '" data-date="' +
      esc(date) +
      '">' +
      '<span class="card-check" aria-hidden="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="3 8 7 12 13 4"/></svg></span>' +
      '<button class="card-del-btn" type="button" title="删除商品" data-action="delete-product" data-product-id="' +
      esc(product.id) +
      '"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/><path d="M19 6l-1.5 14a2 2 0 01-2 1.8H8.5a2 2 0 01-2-1.8L5 6"/></svg></button>' +
      '<div class="placeholder product-thumb"><span class="ph-frame">' +
      esc(shortLabel(title)) +
      " · 1200×800</span></div>" +
      '<div class="product-body"><div class="product-name">' +
      esc(title) +
      '</div><div class="product-cat">' +
      esc(cat) +
      '</div><div class="product-date">' +
      esc(date) +
      " 创建</div></div>" +
      '<div class="product-footer"><span class="stat"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="10" r="2"/><path d="M21 17l-5-5-9 9"/></svg>素材 <b>' +
      esc(assets) +
      '</b></span><span class="sep">·</span><span class="stat"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="6" width="14" height="12" rx="2"/><path d="M16 10l6-3v10l-6-3z"/></svg>视频 <b>' +
      esc(videos) +
      "</b></span></div></div>"
    );
  }

  function syncProductCount() {
    const cards = Array.from(document.querySelectorAll("#product-grid .product-card"));
    const visible = cards.filter(function (card) {
      return card.style.display !== "none";
    }).length;
    const total = cards.length;
    const sku = document.getElementById("sku-count");
    if (sku) sku.textContent = String(total);
    const meta = document.getElementById("result-meta");
    if (meta) meta.innerHTML = '// 显示 <span class="count">' + visible + "</span> / " + total + " 个商品";
    setShellBadge("products.html", total);
    const empty = document.getElementById("empty");
    if (empty) empty.hidden = visible !== 0;
  }

  function applyLiveProductFilter() {
    const q = (document.getElementById("search-input") || {}).value || "";
    const needle = q.trim().toLowerCase();
    document.querySelectorAll("#product-grid .product-card").forEach(function (card) {
      const hay = ((card.dataset.name || "") + " " + (card.dataset.cat || "") + " " + (card.dataset.tags || "")).toLowerCase();
      card.style.display = !needle || hay.indexOf(needle) >= 0 ? "" : "none";
    });
    syncProductCount();
  }

  function bindLiveProductCards() {
    const grid = document.getElementById("product-grid");
    if (!grid) return;
    grid.querySelectorAll(".product-card").forEach(function (card) {
      card.addEventListener("click", function (event) {
        if (document.body.classList.contains("edit-mode")) {
          event.preventDefault();
          event.stopImmediatePropagation();
          card.classList.toggle("selected");
          return;
        }
        const id = card.dataset.productId;
        const name = card.dataset.name || "";
        if (id) go("product-detail.html?product_id=" + encodeURIComponent(id) + "&product=" + encodeURIComponent(name));
      });
    });

    grid.querySelectorAll('[data-action="delete-product"][data-product-id]').forEach(function (button) {
      button.addEventListener("click", async function (event) {
        event.preventDefault();
        event.stopImmediatePropagation();
        const card = button.closest(".product-card");
        const id = button.dataset.productId;
        if (!id || !confirm("确认删除商品「" + (card && card.dataset.name ? card.dataset.name : "") + "」?")) return;
        try {
          await api("/api/products/" + encodeURIComponent(id) + "/", { method: "DELETE" });
          if (card) card.remove();
          forgetCache("products:list");
          forgetCache("projects:bundle");
          forgetCache("dashboard");
          syncProductCount();
          toast("已删除", "商品已从 Django 数据库移除");
        } catch (error) {
          toast("删除失败", error.message);
        }
      });
    });
  }

  function renderLiveProductsPayload(data) {
    const grid = document.getElementById("product-grid");
    if (!grid) return;
    const products = data.results || [];
    grid.innerHTML = products.length
      ? products.map(productCardHTML).join("")
      : '<div class="empty-filter">// 当前团队还没有真实商品</div>';
    bindLiveProductCards();
    applyLiveProductFilter();
  }

  async function loadLiveProducts() {
    if (!canUseApi()) return;
    const grid = document.getElementById("product-grid");
    if (!grid) return;
    return loadWithCache(
      "products:list",
      function () {
        return apiGet("products:list", "/api/products/");
      },
      renderLiveProductsPayload,
      "商品加载失败"
    );
  }

  function wireProductCreate() {
    const saveBtn = document.getElementById("pc-save-btn");
    if (!saveBtn) return;

    const submitHandler = function (event) {
      const button = event.target && event.target.closest ? event.target.closest("#pc-save-btn") : null;
      if (!button || !canUseApi()) return;
      event.preventDefault();
      event.stopPropagation();
      event.stopImmediatePropagation();
      submitLiveProduct(button);
    };

    document.addEventListener("click", submitHandler, true);
    saveBtn.addEventListener("click", submitHandler, true);

    const search = document.getElementById("search-input");
    if (search) {
      search.addEventListener(
        "input",
        function (event) {
          if (!canUseApi()) return;
          event.stopImmediatePropagation();
          applyLiveProductFilter();
        },
        true
      );
    }
  }

  function renderProductDetail(product) {
    if (!product) return;
      const title = product.title || "";
      const category = product.category || "";
      const target = product.target_audience || "";
      const h1 = document.getElementById("pd-name");
      if (h1 && title) h1.textContent = title;
      setField("name", title);
      setField("cat", category);
      setField("target", target);
      const bulletBox = document.querySelector('[data-field="bullets"] .v-static');
      if (bulletBox && product.selling_points && product.selling_points.length) {
        bulletBox.innerHTML = product.selling_points
          .map(function (point) {
            return '<span class="bullet">' + esc(point.title) + "</span>";
          })
          .join("");
      }
  }

  async function hydrateProductDetail() {
    if (!canUseApi()) return;
    if (page !== "product-detail.html") return;
    const id = query.get("product_id") || localStorage.getItem("airshelf_current_product_id");
    if (!id) return;
    return loadWithCache(
      "product:" + id,
      function () {
        return apiGet("product:" + id, "/api/products/" + encodeURIComponent(id) + "/");
      },
      renderProductDetail,
      "商品详情加载失败"
    );
  }

  function setField(field, value) {
    const row = document.querySelector('[data-field="' + field + '"]');
    if (!row) return;
    const stat = row.querySelector(".v-static");
    const input = row.querySelector(".v-input, .v-select");
    if (stat && value) stat.textContent = value;
    if (input && value) {
      if (input.tagName === "SELECT" && !Array.from(input.options).some(function (option) { return option.value === value; })) {
        const option = document.createElement("option");
        option.textContent = value;
        option.value = value;
        input.insertBefore(option, input.firstChild);
      }
      input.value = value;
    }
  }

  function stageNo(project) {
    const map = { script: 1, base_assets: 2, storyboard: 3, video: 4, export: 5 };
    if (project.status === "completed") return 5;
    return map[project.current_stage] || 1;
  }

  function statusBucket(project) {
    if (project.status === "completed") return "done";
    if (project.status === "failed") return "fail";
    return "wip";
  }

  function projectStatusLabel(project) {
    return {
      draft: "脚本待生成",
      scripting: "脚本生成中",
      asseting: "基础资产生成中",
      storyboarding: "故事板生成中",
      videoing: "视频片段生成中",
      exporting: "导出中",
      completed: "已完成",
      failed: "失败",
    }[project.status] || "进行中";
  }

  function pillClass(project) {
    if (project.status === "completed") return "ok";
    if (project.status === "failed") return "fail";
    return "info";
  }

  function progressHTML(project) {
    const no = stageNo(project);
    let html = "";
    for (let i = 1; i <= 5; i += 1) {
      let cls = "";
      if (project.status === "completed" || i < no) cls = "done";
      else if (i === no) cls = "cur";
      html += '<span class="' + cls + '"></span>';
    }
    return html;
  }

  function projectHref(project) {
    return "pipeline.html?project_id=" + encodeURIComponent(project.id) + "#stage-" + stageNo(project);
  }

  function liveProjectRowHTML(project, productName) {
    const href = projectHref(project);
    const status = statusBucket(project);
    const label = projectStatusLabel(project);
    const shots = (project.video_segments && project.video_segments.length) || 4;
    const no = stageNo(project);
    return (
      '<tr data-live="1" data-project-id="' +
      esc(project.id) +
      '" data-href="' +
      esc(href) +
      '" data-status="' +
      esc(status) +
      '" data-name="' +
      esc(project.name) +
      '">' +
      '<td><div class="proj-name-cell"><div class="placeholder proj-thumb"><span class="ph-frame">9:16</span></div><div><div class="proj-name">' +
      esc(project.name) +
      '</div><div class="proj-sub">' +
      esc(shots) +
      " 镜 · 0-60s</div></div></div></td>" +
      "<td>" +
      esc(productName || "未命名商品") +
      '</td><td><span class="muted">AI 全生</span></td><td><div class="hstack"><div class="prog">' +
      progressHTML(project) +
      '</div><span class="muted-2 mono" style="font-size:11px;">' +
      esc(no) +
      '/5</span></div></td><td><span class="pill ' +
      pillClass(project) +
      '"><span class="dot"></span>' +
      esc(label) +
      '</span></td><td class="muted-2">' +
      esc(dateOnly(project.updated_at)) +
      '</td><td><div class="row-action"><a href="' +
      esc(href) +
      '" title="继续"><svg width="14" height="14" viewBox="0 0 16 16"><path d="M5 4l6 4-6 4z" fill="currentColor"/></svg></a><span class="row-more"><svg width="14" height="14" viewBox="0 0 16 16"><circle cx="3" cy="8" r="1.2" fill="currentColor"/><circle cx="8" cy="8" r="1.2" fill="currentColor"/><circle cx="13" cy="8" r="1.2" fill="currentColor"/></svg></span></div></td></tr>'
    );
  }

  function liveProjectCardHTML(project, productName) {
    const href = projectHref(project);
    const status = statusBucket(project);
    const label = projectStatusLabel(project);
    const shots = (project.video_segments && project.video_segments.length) || 4;
    const no = stageNo(project);
    return (
      '<div class="proj-card" data-live="1" data-project-id="' +
      esc(project.id) +
      '" data-href="' +
      esc(href) +
      '" data-status="' +
      esc(status) +
      '" data-name="' +
      esc(project.name) +
      '">' +
      '<span class="card-check" aria-hidden="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="3 8 7 12 13 4"/></svg></span>' +
      '<button class="card-del-btn" type="button" title="删除项目" data-action="delete-project" data-project-id="' +
      esc(project.id) +
      '"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/><path d="M19 6l-1.5 14a2 2 0 01-2 1.8H8.5a2 2 0 01-2-1.8L5 6"/></svg></button>' +
      '<div class="placeholder card-thumb"><span class="ph-frame">9:16 · 阶段 ' +
      esc(no) +
      '/5</span></div><div class="card-body"><div><div class="card-name">' +
      esc(project.name) +
      '</div><div class="card-sub" style="margin-top:4px;">' +
      esc(productName || "未命名商品") +
      " · " +
      esc(shots) +
      ' 镜</div></div><div class="hstack"><div class="prog">' +
      progressHTML(project) +
      '</div><span class="muted-2 mono" style="font-size:10.5px;">' +
      esc(no) +
      '/5</span></div><div class="card-foot"><span class="pill ' +
      pillClass(project) +
      '"><span class="dot"></span>' +
      esc(label) +
      '</span><span class="card-time">' +
      esc(dateOnly(project.updated_at)) +
      "</span></div></div></div>"
    );
  }

  function syncProjectCount() {
    const rows = Array.from(document.querySelectorAll("#list-tbody tr"));
    const visible = rows.filter(function (row) {
      return row.style.display !== "none";
    }).length;
    const counts = { total: rows.length, wip: 0, done: 0, fail: 0 };
    rows.forEach(function (row) {
      const s = row.dataset.status;
      if (counts[s] !== undefined) counts[s] += 1;
    });
    const total = document.getElementById("sub-total");
    const wip = document.getElementById("sub-wip");
    const done = document.getElementById("sub-done");
    const fail = document.getElementById("sub-fail");
    if (total) total.textContent = counts.total;
    if (wip) wip.textContent = counts.wip;
    if (done) done.textContent = counts.done;
    if (fail) fail.textContent = counts.fail;
    document.querySelectorAll("#status-tabs .tab").forEach(function (tab) {
      const filter = tab.dataset.filter;
      const n = filter === "all" ? counts.total : counts[filter] || 0;
      const el = tab.querySelector(".count");
      if (el) el.textContent = n;
    });
    const meta = document.getElementById("result-meta");
    if (meta) meta.innerHTML = '// 显示 <span class="count">' + visible + "</span> / " + counts.total + " 个项目";
    setShellBadge("projects.html", counts.total);
    const empty = document.getElementById("empty");
    if (empty) empty.hidden = visible !== 0;
  }

  function applyLiveProjectFilter() {
    const active = document.querySelector("#status-tabs .tab.active");
    const filter = active ? active.dataset.filter : "all";
    const needle = ((document.getElementById("search-input") || {}).value || "").trim().toLowerCase();
    const test = function (el) {
      const statusOk = filter === "all" || el.dataset.status === filter;
      const textOk = !needle || (el.dataset.name || "").toLowerCase().indexOf(needle) >= 0;
      return statusOk && textOk;
    };
    document.querySelectorAll("#list-tbody tr, #grid-body .proj-card").forEach(function (el) {
      el.style.display = test(el) ? "" : "none";
    });
    syncProjectCount();
  }

  function bindLiveProjects() {
    document.querySelectorAll("#list-tbody tr[data-project-id], #grid-body .proj-card[data-project-id]").forEach(function (el) {
      el.addEventListener("click", function (event) {
        if (event.target.closest("button, a, .row-more")) return;
        if (document.body.classList.contains("edit-mode")) {
          event.preventDefault();
          event.stopImmediatePropagation();
          el.classList.toggle("selected");
          return;
        }
        go(el.dataset.href || projectHref({ id: el.dataset.projectId, current_stage: "script", status: "" }));
      });
    });

    document.querySelectorAll('[data-action="delete-project"][data-project-id]').forEach(function (button) {
      button.addEventListener("click", async function (event) {
        event.preventDefault();
        event.stopImmediatePropagation();
        const id = button.dataset.projectId;
        const el = button.closest(".proj-card, tr");
        const name = el ? el.dataset.name : "";
        if (!id || !confirm("确认删除项目「" + name + "」?")) return;
        try {
          await api("/api/projects/" + encodeURIComponent(id) + "/", { method: "DELETE" });
          document.querySelectorAll('[data-project-id="' + CSS.escape(id) + '"]').forEach(function (node) {
            node.remove();
          });
          forgetCache("projects:bundle");
          forgetCache("projects:list");
          forgetCache("dashboard");
          applyLiveProjectFilter();
          toast("已删除", "项目已从 Django 数据库移除");
        } catch (error) {
          toast("删除失败", error.message);
        }
      });
    });

    const search = document.getElementById("search-input");
    if (search) {
      search.addEventListener(
        "input",
        function (event) {
          if (!canUseApi()) return;
          event.stopImmediatePropagation();
          applyLiveProjectFilter();
        },
        true
      );
    }
    document.querySelectorAll("#status-tabs .tab").forEach(function (tab) {
      tab.addEventListener(
        "click",
        function (event) {
          if (!canUseApi()) return;
          event.preventDefault();
          event.stopImmediatePropagation();
          document.querySelectorAll("#status-tabs .tab").forEach(function (x) {
            x.classList.remove("active");
          });
          tab.classList.add("active");
          applyLiveProjectFilter();
        },
        true
      );
    });
  }

  function renderLiveProjectsPayload(payload) {
    const tbody = document.getElementById("list-tbody");
    const grid = document.getElementById("grid-body");
    if (!tbody || !grid) return;
    const productData = payload.products || {};
    const projectData = payload.projects || {};
    const productMap = {};
    (productData.results || []).forEach(function (product) {
      productMap[product.id] = product.title;
    });
    const projects = projectData.results || [];
    tbody.innerHTML = projects.length
      ? projects
        .map(function (project) {
          return liveProjectRowHTML(project, productMap[project.product]);
        })
        .join("")
      : '<tr data-live="1"><td colspan="6" class="muted">// 当前团队还没有真实项目</td></tr>';
    grid.innerHTML = projects.length
      ? projects
        .map(function (project) {
          return liveProjectCardHTML(project, productMap[project.product]);
        })
        .join("")
      : '<div class="empty-filter">// 当前团队还没有真实项目</div>';
    bindLiveProjects();
    applyLiveProjectFilter();
  }

  async function loadLiveProjects() {
    if (!canUseApi()) return;
    const tbody = document.getElementById("list-tbody");
    const grid = document.getElementById("grid-body");
    if (!tbody || !grid) return;
    seedProjectsBundleFromCache();
    return loadWithCache(
      "projects:bundle",
      async function () {
        const productData = await apiGet("products:list", "/api/products/");
        writeCache("products:list", productData);
        const projectData = await apiGet("projects:list", "/api/projects/");
        return { products: productData, projects: projectData };
      },
      renderLiveProjectsPayload,
      "项目加载失败"
    );
  }

  async function ensureProduct(title, category, id) {
    if (id) return api("/api/products/" + encodeURIComponent(id) + "/");
    const data = await api("/api/products/?search=" + encodeURIComponent(title));
    const found = (data.results || []).find(function (product) {
      return product.title === title;
    });
    if (found) return found;
    return api("/api/products/", {
      method: "POST",
      body: JSON.stringify({ title: title, category: category || "" }),
    });
  }

  function selectedWizardProduct() {
    const card = document.querySelector("#step-pane-1 .product-card.selected, #step-pane-1 .product-pick.selected");
    if (!card) return null;
    return {
      id: card.dataset.productId || card.dataset.id || "",
      title: (card.querySelector(".product-name, .name") || {}).textContent || "",
      category: (card.querySelector(".product-cat, .meta") || {}).textContent || "",
    };
  }

  function projectNameFromWizard(productTitle) {
    const inputs = Array.from(document.querySelectorAll("#step-pane-2 input.input"));
    const named = inputs.find(function (input) {
      return input.value && input.value.trim().length >= 2;
    });
    const value = named ? named.value.trim() : "";
    return value || (productTitle ? productTitle + " · AI 视频" : "未命名项目");
  }

  function liveWizardProductHTML(product, index) {
    const title = product.title || "未命名商品";
    const category = product.category || "未分类";
    const date = dateOnly(product.created_at);
    return (
      '<div class="product-card' +
      (index === 0 ? " selected" : "") +
      '" data-live="1" data-product-id="' +
      esc(product.id) +
      '">' +
      '<div class="placeholder product-thumb"><span class="ph-frame">' +
      esc(shortLabel(title)) +
      " · 1200×800</span></div>" +
      '<div class="product-body"><div class="product-name">' +
      esc(title) +
      '</div><div class="product-cat">' +
      esc(category) +
      '</div><div class="product-date">' +
      esc(date) +
      " 创建</div></div></div>"
    );
  }

  function renderLiveWizardProductsPayload(data) {
    const grid = document.querySelector("#step-pane-1 .pp-grid");
    if (!grid) return;
    const products = data.results || [];
    const createCard =
      '<div class="pp-create-card" data-live-create="1"><div class="pc-plus"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg></div><div class="pc-t">新建商品</div><div class="pc-d">// 进入商品库创建</div></div>';
    grid.innerHTML =
      createCard +
      (products.length
        ? products.map(liveWizardProductHTML).join("")
        : '<div class="pp-empty">// 当前团队还没有商品 · 请先新建商品</div>');
    grid.querySelector('[data-live-create="1"]')?.addEventListener("click", function () {
      go("products.html");
    });
    grid.querySelectorAll(".product-card[data-product-id]").forEach(function (card) {
      card.addEventListener("click", function () {
        grid.querySelectorAll(".product-card").forEach(function (node) {
          node.classList.remove("selected");
        });
        card.classList.add("selected");
        const title = (card.querySelector(".product-name") || {}).textContent || "";
        const projectInputs = Array.from(document.querySelectorAll("#step-pane-2 input.input"));
        const target = projectInputs.find(function (input) {
          return input.placeholder && input.placeholder.indexOf("项目") >= 0;
        });
        if (target && !target.value.trim()) target.value = title + " · 痛点种草 · v1";
      });
    });
    const meta = document.querySelector("#step-pane-1 .pp-result-meta");
    if (meta) meta.textContent = "// 显示 " + products.length + " / " + products.length + " 个真实商品";
    document.querySelectorAll(".btn-start.disabled").forEach(function (button) {
      button.classList.remove("disabled");
    });
  }

  async function loadLiveWizardProducts() {
    if (page !== "projects-new.html" || !canUseApi()) return;
    const grid = document.querySelector("#step-pane-1 .pp-grid");
    if (!grid) return;
    return loadWithCache(
      "products:list",
      function () {
        return apiGet("products:list", "/api/products/");
      },
      renderLiveWizardProductsPayload,
      "商品加载失败"
    );
  }

  function wireProjectWizard() {
    if (page !== "projects-new.html") return;
    if (!window._wiz || typeof window._wiz.startGenerate !== "function") return;
    const original = window._wiz.startGenerate;
    window._wiz.startGenerate = async function () {
      if (!canUseApi()) return original();
      const picked = selectedWizardProduct();
      if (!picked || !picked.title.trim()) {
        toast("请选择商品", "项目必须绑定一个商品");
        return;
      }
      const startBtn = document.querySelector(".btn-start");
      const restore = setButtonBusy(startBtn, "创建项目中...");
      try {
        const product = await ensureProduct(picked.title.trim(), picked.category.trim(), picked.id);
        const project = await api("/api/projects/", {
          method: "POST",
          body: JSON.stringify({
            name: projectNameFromWizard(product.title),
            product: product.id,
          }),
        });
        forgetCache("projects:bundle");
        forgetCache("projects:list");
        forgetCache("dashboard");
        localStorage.setItem("airshelf_current_project_id", project.id);
        toast("项目已创建", "已写入 Django,进入生产管线");
        go(
          "pipeline.html?project_id=" +
            encodeURIComponent(project.id) +
            "&product=" +
            encodeURIComponent(product.title || picked.title) +
            "#stage-1"
        );
      } catch (error) {
        restore();
        toast("创建项目失败", error.message);
      }
    };
  }

  function money(value) {
    const n = Number(value || 0);
    return "¥" + n.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  }

  function plainMoney(value) {
    const n = Number(value || 0);
    return "¥" + n.toFixed(2);
  }

  function roleUi(role) {
    if (role === "owner" || role === "super") return { key: "super", label: "超管" };
    if (role === "admin") return { key: "admin", label: "团管" };
    if (role === "viewer") return { key: "member", label: "访客" };
    return { key: "member", label: "成员" };
  }

  function displayName(user) {
    const raw = String(user?.username || user?.email || "成员").trim();
    if (raw.indexOf("@") > 0) return raw.split("@")[0];
    return raw || "成员";
  }

  function userPublicId(user) {
    const id = String(user?.id || "").replace(/-/g, "").toUpperCase();
    return id ? "USR-" + id.slice(0, 12) : "USR-UNKNOWN";
  }

  function initial(name) {
    return String(name || "U").trim().slice(0, 1).toUpperCase() || "U";
  }

  function stageLabel(project) {
    return {
      script: "Stage 1 脚本",
      base_assets: "Stage 2 基础资产",
      storyboard: "Stage 3 故事板",
      video: "Stage 4 视频",
      export: "Stage 5 导出",
    }[project.current_stage] || "Stage 1 脚本";
  }

  function assetTab(asset) {
    const category = asset.category || "";
    if (category === "person") return "people";
    if (category === "scene") return "scenes";
    if (category === "product_image") return "products";
    if (category === "final_video" || category === "video_clip") return "finals";
    if (category === "upload") return "uploads";
    return "unclassified";
  }

  function assetMeta(asset, tab) {
    const source = asset.source === "ai_generated" ? "AI 生成" : asset.source === "exported" ? "导出" : "手动上传";
    const type = asset.asset_type === "video" ? "视频" : "图片";
    if (tab === "people") return "人物 · " + source + " · 用过 0 次";
    if (tab === "scenes") return "场景 · " + source + " · 用过 0 次";
    if (tab === "products") return "商品图 · " + source + " · 用过 0 次";
    if (tab === "finals") return "成片 · " + type + " · " + dateOnly(asset.created_at);
    return type + " · " + source + " · " + dateOnly(asset.created_at);
  }

  function liveAssetCardHTML(asset, tab) {
    const file = asset.files && asset.files[0];
    const isVideo = asset.asset_type === "video";
    const preview = file && file.preview_url;
    const thumb = preview
      ? isVideo
        ? '<video src="' + esc(preview) + '" muted playsinline style="width:100%;height:100%;object-fit:cover;display:block;border-radius:inherit;"></video>'
        : '<img src="' + esc(preview) + '" alt="" style="width:100%;height:100%;object-fit:cover;display:block;border-radius:inherit;">'
      : '<span class="ph-frame">' + esc(shortLabel(asset.name)) + "</span>";
    return (
      '<div class="asset-card' +
      (isVideo ? " video" : "") +
      '" data-live="1" data-asset-id="' +
      esc(asset.id) +
      '" data-name="' +
      esc(asset.name) +
      '" data-source="' +
      esc(asset.source || "") +
      '">' +
      '<span class="card-check" aria-hidden="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="3 8 7 12 13 4"/></svg></span>' +
      '<div class="placeholder asset-thumb">' +
      thumb +
      '</div><div class="asset-body"><div class="asset-name">' +
      esc(asset.name || "未命名资产") +
      '</div><div class="asset-meta">' +
      esc(assetMeta(asset, tab)) +
      "</div></div></div>"
    );
  }

  function applyLiveAssetTab(tab) {
    const assets = window.__airshelfLiveAssets || [];
    document.querySelectorAll("#asset-tabs .tab").forEach(function (node) {
      node.classList.toggle("active", node.dataset.tab === tab);
    });
    document.querySelectorAll(".asset-grid[data-tab]").forEach(function (grid) {
      grid.hidden = grid.dataset.tab !== tab;
    });
    const grid = document.getElementById("grid-" + tab);
    const queryText = ((document.getElementById("search-input") || {}).value || "").trim().toLowerCase();
    let visible = 0;
    if (grid) {
      grid.querySelectorAll(".asset-card").forEach(function (card) {
        const ok = !queryText || (card.dataset.name || "").toLowerCase().indexOf(queryText) >= 0;
        card.style.display = ok ? "" : "none";
        if (ok) visible += 1;
      });
    }
    const total = assets.filter(function (asset) {
      return assetTab(asset) === tab;
    }).length;
    const meta = document.getElementById("result-meta");
    if (meta) meta.innerHTML = '// 显示 <span class="count">' + visible + "</span> / " + total + " 个资产";
  }

  function renderLiveAssetsPayload(data) {
    const assets = data.results || [];
    window.__airshelfLiveAssets = assets;
    const tabs = ["people", "scenes", "products", "finals", "uploads", "unclassified"];
    tabs.forEach(function (tab) {
      const grid = document.getElementById("grid-" + tab);
      if (!grid) return;
      const list = assets.filter(function (asset) {
        return assetTab(asset) === tab;
      });
      grid.innerHTML = list.length
        ? list
            .map(function (asset) {
              return liveAssetCardHTML(asset, tab);
            })
            .join("")
        : '<div class="empty-filter">// 当前分类暂无真实资产</div>';
      const count = document.querySelector('#asset-tabs .tab[data-tab="' + tab + '"] .count');
      if (count) count.textContent = String(list.length);
    });
    const counts = tabs.reduce(function (acc, tab) {
      acc[tab] = assets.filter(function (asset) {
        return assetTab(asset) === tab;
      }).length;
      return acc;
    }, {});
    const people = document.getElementById("sub-people");
    const scenes = document.getElementById("sub-scenes");
    const products = document.getElementById("sub-products");
    const finals = document.getElementById("sub-finals");
    if (people) people.textContent = String(counts.people || 0);
    if (scenes) scenes.textContent = String(counts.scenes || 0);
    if (products) products.textContent = String(counts.products || 0);
    if (finals) finals.textContent = String(counts.finals || 0);
    const active = document.querySelector("#asset-tabs .tab.active");
    applyLiveAssetTab(active ? active.dataset.tab : "people");
  }

  async function loadLiveAssets() {
    if (page !== "library.html" || !canUseApi()) return;
    return loadWithCache(
      "assets:list",
      function () {
        return apiGet("assets:list", "/api/assets/");
      },
      renderLiveAssetsPayload,
      "资产加载失败"
    );
  }

  let assetUploadInFlight = false;

  async function submitLiveAsset(submit) {
    if (!canUseApi()) return false;
    if (assetUploadInFlight) return true;

    const file = document.getElementById("upload-file")?.files?.[0];
    const name = (document.getElementById("upload-name")?.value || "").trim();
    const kind = document.getElementById("upload-kind")?.value || "uploads";
    if (!file) {
      toast("请选择文件", "资产上传需要真实文件");
      return true;
    }
    if (!name) {
      toast("请填写资产名称", "必填项");
      return true;
    }
    const categoryMap = {
      people: "person",
      scenes: "scene",
      products: "product_image",
      finals: "final_video",
      uploads: "upload",
      unclassified: "uncategorized",
    };
    const form = new FormData();
    form.append("file", file);
    form.append("name", name);
    form.append("asset_type", kind === "finals" ? "video" : "image");
    form.append("category", categoryMap[kind] || "upload");
    assetUploadInFlight = true;
    const restore = setButtonBusy(submit, "上传中...");
    try {
      await api("/api/assets/upload/", { method: "POST", body: form });
      forgetCache("assets:list");
      forgetCache("dashboard");
      if (window.Shell) Shell.closeModal("upload-modal-bg");
      toast("资产已上传", "已写入 TOS 与资产表");
      await loadLiveAssets();
    } catch (error) {
      toast("上传失败", error.message);
    } finally {
      assetUploadInFlight = false;
      restore();
    }
    return true;
  }

  function wireLiveAssets() {
    if (page !== "library.html") return;
    document.querySelectorAll("#asset-tabs .tab").forEach(function (tab) {
      tab.addEventListener(
        "click",
        function (event) {
          if (!canUseApi()) return;
          event.preventDefault();
          event.stopImmediatePropagation();
          applyLiveAssetTab(tab.dataset.tab);
        },
        true
      );
    });
    const search = document.getElementById("search-input");
    if (search) {
      search.addEventListener(
        "input",
        function (event) {
          if (!canUseApi()) return;
          event.stopImmediatePropagation();
          const active = document.querySelector("#asset-tabs .tab.active");
          applyLiveAssetTab(active ? active.dataset.tab : "people");
        },
        true
      );
    }
    const submit = document.getElementById("upload-submit");
    if (!submit) return;
    const submitHandler = function (event) {
      const button = event.target && event.target.closest ? event.target.closest("#upload-submit") : null;
      if (!button || !canUseApi()) return;
      event.preventDefault();
      event.stopPropagation();
      event.stopImmediatePropagation();
      submitLiveAsset(button);
    };
    document.addEventListener("click", submitHandler, true);
    submit.addEventListener("click", submitHandler, true);
  }

  function ledgerStageKey(ledger) {
    const text = String(ledger.reason || ledger.ledger_type || ledger.metadata?.stage || ledger.metadata?.task_type || "").toLowerCase();
    if (text.indexOf("video") >= 0 || text.indexOf("seedance") >= 0 || text.indexOf("视频") >= 0) return "video";
    if (text.indexOf("story") >= 0 || text.indexOf("image-2") >= 0 || text.indexOf("故事板") >= 0) return "storyboard";
    if (text.indexOf("asset") >= 0 || text.indexOf("image") >= 0 || text.indexOf("基础资产") >= 0) return "asset";
    if (text.indexOf("script") >= 0 || text.indexOf("llm") >= 0 || text.indexOf("脚本") >= 0) return "script";
    return "other";
  }

  function renderLiveAccountOverview(ledgers, used) {
    const spendByStage = { video: 0, storyboard: 0, asset: 0, script: 0 };
    const daily = {};
    ledgers.forEach(function (ledger) {
      const amount = Math.abs(Math.min(0, Number(ledger.amount || 0)));
      if (!amount) return;
      const key = ledgerStageKey(ledger);
      if (spendByStage[key] !== undefined) spendByStage[key] += amount;
      const day = dateOnly(ledger.created_at);
      daily[day] = (daily[day] || 0) + amount;
    });
    const stageRows = [
      ["video", "视频片段(Seedance)"],
      ["storyboard", "故事板(image-2)"],
      ["asset", "基础资产"],
      ["script", "脚本 LLM"],
    ];
    const stageTotal = stageRows.reduce(function (sum, row) {
      return sum + spendByStage[row[0]];
    }, 0);
    const denominator = Math.max(stageTotal, used, 1);
    const lines = document.querySelectorAll(".stage-pane .usage-line");
    const bars = document.querySelectorAll(".stage-pane .usage-bar > span");
    stageRows.forEach(function (row, index) {
      const value = spendByStage[row[0]];
      const valueEl = lines[index]?.querySelector(".v");
      if (valueEl) valueEl.textContent = money(value);
      if (bars[index]) bars[index].style.width = Math.min(100, (value / denominator) * 100).toFixed(1) + "%";
    });
    const totalEl = document.querySelector(".stage-pane .total .v");
    if (totalEl) totalEl.textContent = money(used || stageTotal);

    const days = Object.keys(daily).sort().slice(-14);
    const values = days.map(function (day) {
      return daily[day] || 0;
    });
    const sum = values.reduce(function (acc, value) {
      return acc + value;
    }, 0);
    const peak = values.reduce(function (max, value) {
      return Math.max(max, value);
    }, 0);
    const trendSum = document.getElementById("trend-sum");
    const trendAvg = document.getElementById("trend-avg");
    const trendPeak = document.getElementById("trend-peak");
    if (trendSum) trendSum.textContent = money(sum);
    if (trendAvg) trendAvg.textContent = money(values.length ? sum / values.length : 0);
    if (trendPeak) trendPeak.textContent = money(peak);
  }

  function renderLiveAccountPayload(payload) {
    const summaryData = payload.summary || {};
    const ledgers = payload.ledgers || [];
    const members = payload.members || [];
    const projectsData = payload.projects || {};
    const productsData = payload.products || {};
    const projects = projectsData.results || [];
    const productMap = {};
    (productsData.results || []).forEach(function (product) {
      productMap[product.id] = product.title;
    });
    const account = summaryData.account || {};
    const used = Math.abs(Number(summaryData.charged_total || 0));
    const memberLimit = members.reduce(function (sum, member) {
      return sum + Math.max(0, Number(member.monthly_credit_limit || 0));
    }, 0);
    const limit = memberLimit || Number(account.balance || 0);
    const left = Math.max(0, limit - used);
    const pct = limit > 0 ? Math.min(100, (used / limit) * 100) : 0;
    const hero = document.querySelector(".balance-hero .v");
    if (hero) hero.textContent = money(account.balance || 0);
    const subValues = document.querySelectorAll(".balance-sub .col .v");
    if (subValues[0]) subValues[0].textContent = money(limit);
    if (subValues[1]) subValues[1].textContent = money(used);
    const subMeta = document.querySelectorAll(".balance-sub .col .meta");
    if (subMeta[1]) subMeta[1].textContent = "// 占比 " + pct.toFixed(1) + "% · " + (pct >= 80 ? "注意" : "健康");
    const meter = document.querySelector(".balance-meter > span");
    if (meter) meter.style.width = pct.toFixed(1) + "%";
    const foot = document.querySelectorAll(".balance-foot-meta span");
    if (foot[0]) foot[0].textContent = "团队月剩余 " + money(left);
    if (foot[1]) foot[1].textContent = "使用率 " + pct.toFixed(1) + "%";
    renderLiveAccountOverview(ledgers, used);
    const billsBody = document.getElementById("bills-body");
    if (billsBody) {
      billsBody.innerHTML = ledgers.length
        ? ledgers
            .map(function (ledger) {
              const n = Number(ledger.amount || 0);
              const cls = n > 0 ? "pos" : n < 0 ? "neg" : "zero";
              return (
                "<tr><td><span class=\"ts\">" +
                esc(dateOnly(ledger.created_at)) +
                '</span></td><td><div>' +
                esc(ledger.reason || ledger.ledger_type) +
                '<div class="ref">' +
                esc(ledger.ledger_type) +
                '</div></div></td><td><span class="muted">' +
                esc(ledger.id) +
                '</span></td><td><span class="who"><span class="av">U</span>成员</span></td><td><span class="status-tag ok">真实</span></td><td class="' +
                cls +
                '">' +
                plainMoney(n) +
                "</td></tr>"
              );
            })
            .join("")
        : '<tr><td colspan="6" class="muted">// 暂无真实账单流水</td></tr>';
    }
    const billsCount = document.getElementById("bills-count");
    if (billsCount) billsCount.textContent = String(ledgers.length);
    const projectBody = document.getElementById("proj-body");
    if (projectBody) {
      projectBody.innerHTML = projects.length
        ? projects
            .map(function (project) {
              const status = statusBucket(project);
              return (
                '<tr><td><strong>' +
                esc(project.name) +
                '</strong></td><td><span class="muted">' +
                esc(productMap[project.product] || "未命名商品") +
                '</span></td><td><span class="who"><span class="av">' +
                initial(members[0]?.user?.username) +
                "</span>" +
                esc(members[0]?.user?.username || "成员") +
                '</span></td><td><span class="muted">' +
                esc(stageLabel(project)) +
                '</span></td><td><span class="status-tag ' +
                (status === "fail" ? "fail" : status === "done" ? "ok" : "wip") +
                '">' +
                esc(projectStatusLabel(project)) +
                '</span></td><td class="neg">¥0.00</td></tr>'
              );
            })
            .join("")
        : '<tr><td colspan="6" class="muted">// 暂无真实项目</td></tr>';
    }
    const projCount = document.getElementById("proj-count");
    if (projCount) projCount.innerHTML = '共 <b style="color:var(--accent-black);">' + projects.length + "</b> 个项目 · 消耗 " + money(used);
    const memberBody = document.getElementById("member-body");
    if (memberBody) {
      memberBody.innerHTML = members.length
        ? members
            .map(function (member) {
              const role = roleUi(member.role);
              const name = member.user?.username || member.user?.email || "成员";
              const limitText = Number(member.monthly_credit_limit || 0) > 0 ? money(member.monthly_credit_limit) : "不限";
              return (
                '<tr><td><span class="who"><span class="av">' +
                initial(name) +
                "</span>" +
                esc(name) +
                '</span></td><td><span class="role-pill role-' +
                role.key +
                '"><span class="dot"></span>' +
                role.label +
                "</span></td><td>0</td><td>" +
                money(0) +
                " / " +
                limitText +
                '</td><td><span class="muted">真实成员表</span></td></tr>'
              );
            })
            .join("")
        : '<tr><td colspan="5" class="muted">// 暂无成员</td></tr>';
    }
    const memCount = document.getElementById("mem-count");
    if (memCount) memCount.innerHTML = '共 <b style="color:var(--accent-black);">' + members.length + "</b> 人 · 合计 " + money(used);
    document.querySelectorAll(".billing-tabs .tab .count").forEach(function (count) {
      const tab = count.closest(".tab")?.dataset.tab;
      if (tab === "by-project") count.textContent = String(projects.length);
      if (tab === "by-member") count.textContent = String(members.length);
      if (tab === "bills") count.textContent = String(ledgers.length);
    });
  }

  async function loadLiveAccount() {
    if (page !== "account.html" || !canUseApi()) return;
    seedAccountBundleFromCache();
    return loadWithCache(
      "account:bundle",
      async function () {
        const summaryData = await apiGet("billing:summary", "/api/billing/summary/");
        writeCache("billing:summary", summaryData);
        const ledgers = await apiGet("billing:ledgers", "/api/billing/ledgers/");
        const members = await apiGet("team:members", "/api/auth/team/members/");
        writeCache("team:members", members);
        const projectsData = await apiGet("projects:list", "/api/projects/");
        const productsData = await apiGet("products:list", "/api/products/");
        writeCache("products:list", productsData);
        return { summary: summaryData, ledgers: ledgers, members: members, projects: projectsData, products: productsData };
      },
      renderLiveAccountPayload,
      "账户数据加载失败"
    );
  }

  function parseMoneyText(text) {
    const n = Number(String(text || "").replace(/[^\d.-]/g, ""));
    return Number.isFinite(n) ? n : 0;
  }

  function liveTopupPayload() {
    const amount = parseMoneyText(document.getElementById("topup-amt")?.textContent || "");
    const bonusText = document.getElementById("topup-bonus")?.textContent || "";
    const bonusMatch = bonusText.match(/含\s*¥?([\d,.]+)/);
    const channelText = document.getElementById("topup-channel-label")?.textContent || "";
    return {
      amount: amount,
      bonus: bonusMatch ? parseMoneyText(bonusMatch[1]) : 0,
      channel: channelText.indexOf("支付宝") >= 0 ? "alipay" : "wechat",
    };
  }

  async function submitLiveTopup() {
    const payload = liveTopupPayload();
    if (payload.amount <= 0) {
      toast("充值金额不正确", "请重新选择金额");
      return;
    }
    try {
      await api("/api/billing/recharge/", {
        method: "POST",
        body: JSON.stringify(payload),
      });
      if (window.Shell) Shell.closeModal("topup-bg");
      forgetCache("billing:summary");
      forgetCache("billing:ledgers");
      forgetCache("account:bundle");
      forgetCache("dashboard");
      toast("充值成功", "已写入 Django 账本");
      await loadLiveAccount();
    } catch (error) {
      toast("充值确认失败", error.message);
    }
  }

  function wireLiveAccount() {
    if (page !== "account.html" || window.__airshelfLiveAccountWired) return;
    window.__airshelfLiveAccountWired = true;
    const fallbackTopupDone = window.topupDone;
    window.topupDone = function () {
      if (!canUseApi()) {
        if (typeof fallbackTopupDone === "function") fallbackTopupDone();
        return;
      }
      submitLiveTopup();
    };
  }

  function liveTeamMemberRowHTML(member) {
    const name = member.user?.first_name || member.user?.username || member.user?.email || "成员";
    const role = roleUi(member.role);
    const monthly = Number(member.monthly_credit_limit || 0);
    const usedPct = monthly > 0 ? Math.min(100, (0 / monthly) * 100) : 0;
    const isOwner = member.role === "owner";
    const actions = isOwner
      ? '<span style="font-family:var(--font-mono);font-size:10.5px;color:var(--black-alpha-32);align-self:center;">不可编辑</span>'
      : '<button class="icon-btn-sm" type="button" data-live-team-action="edit" data-member-id="' +
        esc(member.id) +
        '" title="编辑"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4z"/></svg></button><button class="icon-btn-sm" type="button" data-live-team-action="password" data-member-id="' +
        esc(member.id) +
        '" title="重置密码"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></button><button class="icon-btn-sm danger" type="button" data-live-team-action="remove" data-member-id="' +
        esc(member.id) +
        '" title="移出"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg></button>';
    return (
      '<tr data-live="1" data-id="' +
      esc(member.id) +
      '"><td><span class="member-cell"><span class="av">' +
      initial(name) +
      '</span><span><span class="nm">' +
      esc(name) +
      '</span><span class="em">' +
      esc(member.user?.email || "") +
      '</span></span></span></td><td><span class="role-pill role-' +
      role.key +
      '"><span class="dot"></span>' +
      role.label +
      '</span></td><td><span class="quota-cell"><span class="v">不限</span></span></td><td><span class="quota-cell"><span class="v">' +
      (monthly > 0 ? money(monthly) : "不限") +
      '</span></span></td><td><div class="quota-cell"><span class="v">¥0.00</span> <span class="lbl">/ ' +
      usedPct.toFixed(0) +
      '%</span></div><div class="used-bar"><span class="ok" style="width:' +
      usedPct.toFixed(0) +
      '%"></span></div></td><td><div class="acts">' +
      actions +
      "</div></td></tr>"
    );
  }

  function renderLiveTeamMembers(filterText) {
    const tbody = document.getElementById("members-tbody");
    if (!tbody) return;
    const members = window.__airshelfLiveTeamMembers || [];
    const needle = String(filterText || "").trim().toLowerCase();
    const list = members.filter(function (member) {
      const name = member.user?.first_name || member.user?.username || "";
      const email = member.user?.email || "";
      return !needle || (name + " " + email).toLowerCase().indexOf(needle) >= 0;
    });
    tbody.innerHTML = list.length
      ? list.map(liveTeamMemberRowHTML).join("")
      : '<tr data-live="1"><td colspan="6" class="muted">// 没有匹配的真实成员</td></tr>';
    const headingCount = document.querySelector(".members-table")?.closest(".pane")?.querySelector("h3 .ct");
    if (headingCount) headingCount.textContent = "// " + list.length + " / " + members.length + " 人 · 真实团队表";
  }

  function liveMemberById(id) {
    return (window.__airshelfLiveTeamMembers || []).find(function (member) {
      return String(member.id) === String(id);
    });
  }

  function selectedRoleValue(selector, fallback) {
    const selected = document.querySelector(selector + " .role-choice.selected");
    return selected?.dataset.role || selected?.dataset.editRole || fallback || "member";
  }

  async function refreshLiveTeamAfterMutation() {
    forgetCache("team:members");
    forgetCache("team:bundle");
    forgetCache("account:bundle");
    await loadLiveTeam();
  }

  async function submitLiveTeamMember(button) {
    const username = (document.getElementById("inv-username")?.value || "").trim();
    const password = (document.getElementById("inv-password")?.value || "").trim();
    const name = (document.getElementById("inv-name")?.value || "").trim() || username;
    const monthly = Number(document.getElementById("inv-monthly")?.value || 0);
    if (!username || !password) {
      toast("请填写用户名和密码", "成员未创建");
      return;
    }
    const restore = setButtonBusy(button, "创建中...");
    try {
      const member = await api("/api/auth/team/members/", {
        method: "POST",
        body: JSON.stringify({
          username: username,
          password: password,
          name: name,
          role: selectedRoleValue("#invite-bg", "member"),
          monthly_credit_limit: Number.isFinite(monthly) ? monthly : 0,
        }),
      });
      if (window.Shell) Shell.closeModal("invite-bg");
      const shareUser = document.getElementById("share-username");
      const sharePassword = document.getElementById("share-password");
      if (shareUser) shareUser.textContent = username;
      if (sharePassword) sharePassword.textContent = password;
      if (window.Shell) Shell.openModal("invite-share-bg");
      toast("账户已创建", (member.user?.username || username) + " · 已写入团队表");
      await refreshLiveTeamAfterMutation();
    } catch (error) {
      toast("创建成员失败", error.message);
    } finally {
      restore();
    }
  }

  function openLiveTeamEdit(memberId) {
    const member = liveMemberById(memberId);
    if (!member) return;
    window.__airshelfEditingMemberId = memberId;
    const name = member.user?.first_name || member.user?.username || "";
    const title = document.getElementById("edit-username");
    const input = document.getElementById("edit-name-readonly");
    if (title) title.textContent = name;
    if (input) input.value = name;
    document.querySelectorAll("#edit-role-choices .role-choice").forEach(function (choice) {
      choice.classList.toggle("selected", choice.dataset.editRole === member.role || (member.role === "owner" && choice.dataset.editRole === "super"));
    });
    const daily = document.getElementById("edit-daily");
    const monthly = document.getElementById("edit-monthly");
    const total = document.getElementById("edit-total");
    if (daily) daily.value = "-1";
    if (monthly) monthly.value = Number(member.monthly_credit_limit || 0);
    if (total) total.value = "-1";
    if (window.Shell) Shell.openModal("edit-member-bg");
  }

  async function submitLiveTeamMemberEdit(button) {
    const memberId = window.__airshelfEditingMemberId;
    const monthly = Number(document.getElementById("edit-monthly")?.value || 0);
    const restore = setButtonBusy(button, "保存中...");
    try {
      await api("/api/auth/team/members/" + encodeURIComponent(memberId) + "/", {
        method: "PATCH",
        body: JSON.stringify({
          name: (document.getElementById("edit-name-readonly")?.value || "").trim(),
          role: selectedRoleValue("#edit-role-choices", "member"),
          monthly_credit_limit: Number.isFinite(monthly) ? monthly : 0,
        }),
      });
      window.__airshelfEditingMemberId = null;
      if (window.Shell) Shell.closeModal("edit-member-bg");
      toast("成员已保存", "已写入 Django 团队表");
      await refreshLiveTeamAfterMutation();
    } catch (error) {
      toast("保存成员失败", error.message);
    } finally {
      restore();
    }
  }

  function openLiveTeamPassword(memberId) {
    const member = liveMemberById(memberId);
    if (!member) return;
    window.__airshelfPasswordMemberId = memberId;
    const name = member.user?.first_name || member.user?.username || "成员";
    const title = document.getElementById("reset-pwd-name");
    if (title) title.textContent = name;
    const input = document.getElementById("reset-pwd-input");
    if (input && !input.value.trim()) input.value = "Airshelf" + Math.floor(100000 + Math.random() * 900000);
    if (window.Shell) Shell.openModal("reset-pwd-bg");
  }

  async function submitLiveTeamPassword(button) {
    const memberId = window.__airshelfPasswordMemberId;
    const password = (document.getElementById("reset-pwd-input")?.value || "").trim();
    const restore = setButtonBusy(button, "重置中...");
    try {
      await api("/api/auth/team/members/" + encodeURIComponent(memberId) + "/password/", {
        method: "POST",
        body: JSON.stringify({ password: password }),
      });
      window.__airshelfPasswordMemberId = null;
      if (window.Shell) Shell.closeModal("reset-pwd-bg");
      toast("密码已重置", "成员旧会话已失效");
    } catch (error) {
      toast("重置密码失败", error.message);
    } finally {
      restore();
    }
  }

  async function removeLiveTeamMember(memberId) {
    const member = liveMemberById(memberId);
    const name = member?.user?.first_name || member?.user?.username || "成员";
    if (!confirm("确定将「" + name + "」移出团队?")) return;
    try {
      await api("/api/auth/team/members/" + encodeURIComponent(memberId) + "/", { method: "DELETE" });
      toast("成员已移出", name);
      await refreshLiveTeamAfterMutation();
    } catch (error) {
      toast("移出失败", error.message);
    }
  }

  function runLiveTeamAction(action, memberId) {
    if (action === "edit") openLiveTeamEdit(memberId);
    if (action === "password") openLiveTeamPassword(memberId);
    if (action === "remove") removeLiveTeamMember(memberId);
  }

  function wireLiveTeam() {
    if (page !== "team.html" || window.__airshelfLiveTeamWired) return;
    window.__airshelfLiveTeamWired = true;
    const search = document.getElementById("member-search");
    if (search) {
      search.addEventListener(
        "input",
        function (event) {
          if (!canUseApi()) return;
          event.stopImmediatePropagation();
          renderLiveTeamMembers(search.value);
        },
        true
      );
    }
    document.addEventListener(
      "click",
      function (event) {
        if (!canUseApi()) return;
        const actionButton = event.target && event.target.closest ? event.target.closest("[data-live-team-action]") : null;
        if (actionButton) {
          event.preventDefault();
          event.stopImmediatePropagation();
          runLiveTeamAction(actionButton.dataset.liveTeamAction, actionButton.dataset.memberId);
          return;
        }
        const createButton = event.target && event.target.closest ? event.target.closest("#inv-send") : null;
        if (createButton) {
          event.preventDefault();
          event.stopImmediatePropagation();
          submitLiveTeamMember(createButton);
          return;
        }
        const editButton = event.target && event.target.closest ? event.target.closest("#edit-save") : null;
        if (editButton && window.__airshelfEditingMemberId) {
          event.preventDefault();
          event.stopImmediatePropagation();
          submitLiveTeamMemberEdit(editButton);
          return;
        }
        const pwdButton = event.target && event.target.closest ? event.target.closest("#reset-pwd-confirm") : null;
        if (pwdButton && window.__airshelfPasswordMemberId) {
          event.preventDefault();
          event.stopImmediatePropagation();
          submitLiveTeamPassword(pwdButton);
        }
      },
      true
    );
  }

  function renderLiveTeamActivity(members) {
    const feed = document.querySelector(".team-feed .feed-list");
    if (feed) {
      const count = members.length;
      feed.innerHTML =
        '<div class="feed-item"><div class="av">Q</div><div><div class="txt"><span class="who">真实团队表</span><span class="act">已同步</span><span class="obj">' +
        esc(count) +
        ' 名成员</span></div><div class="ts">local cache</div></div></div>';
    }
    const feedCount = document.querySelector(".team-feed .h .ct");
    if (feedCount) feedCount.textContent = "// 真实动态接口待接入";
    const more = document.getElementById("open-feed-all");
    if (more) more.hidden = true;
    const allList = document.getElementById("feed-all-list");
    if (allList) allList.innerHTML = '<div class="feed-empty">// 暂无真实团队动态</div>';
    const allCount = document.getElementById("feed-all-count");
    if (allCount) allCount.textContent = "// 共 0 条";
    const allMeta = document.getElementById("feed-all-meta");
    if (allMeta) allMeta.textContent = "// 共 0 条";
  }

  function renderLiveTeamPayload(payload) {
    const meData = payload.me || {};
    const summaryData = payload.summary || {};
    const members = payload.members || [];
    rememberAuthShape(meData);
    window.__airshelfLiveTeamMembers = members;
    const account = summaryData.account || {};
    const used = Number(summaryData.charged_total || 0);
    const limit = members.reduce(function (sum, member) {
      return sum + Math.max(0, Number(member.monthly_credit_limit || 0));
    }, 0) || Number(account.balance || 0);
    const left = Math.max(0, limit - used);
    const pct = limit > 0 ? Math.min(100, (used / limit) * 100) : 0;
    const nameEl = document.querySelector(".banner-id .nm");
    if (nameEl) nameEl.innerHTML = esc(meData.team?.name || "团队") + ' <span class="tag">企业</span>';
    const metaEl = document.querySelector(".banner-id .meta");
    if (metaEl) metaEl.textContent = "// 团队 ID: " + (meData.team?.id || "-") + " · " + members.length + " 名成员";
    const statValues = document.querySelectorAll(".banner-stats .stat .v");
    if (statValues[0]) statValues[0].textContent = money(account.balance || 0);
    if (statValues[1]) statValues[1].textContent = money(limit);
    if (statValues[2]) statValues[2].textContent = money(used);
    if (statValues[3]) statValues[3].textContent = money(left);
    const usedSub = document.getElementById("stat-used-sub");
    const leftSub = document.getElementById("stat-left-sub");
    if (usedSub) usedSub.textContent = "// 占月限 " + pct.toFixed(1) + "%";
    if (leftSub) leftSub.textContent = "// 还可生成约 " + Math.max(0, Math.round(left / 10)) + " 个项目";
    renderLiveTeamActivity(members);
    renderLiveTeamMembers((document.getElementById("member-search") || {}).value || "");
  }

  async function loadLiveTeam() {
    if (page !== "team.html" || !canUseApi()) return;
    seedTeamBundleFromCache();
    return loadWithCache(
      "team:bundle",
      async function () {
        const meData = await apiGet("auth:me", "/api/auth/me/");
        writeCache("auth:me", meData);
        const summaryData = await apiGet("billing:summary", "/api/billing/summary/");
        writeCache("billing:summary", summaryData);
        const members = await apiGet("team:members", "/api/auth/team/members/");
        writeCache("team:members", members);
        return { me: meData, summary: summaryData, members: members };
      },
      renderLiveTeamPayload,
      "团队数据加载失败"
    );
  }

  function renderLivePipelinePayload(payload) {
    const project = payload.project;
    const productsData = payload.products || {};
    if (!project) return;
    const productMap = {};
    (productsData.results || []).forEach(function (product) {
      productMap[product.id] = product;
    });
    localStorage.setItem("airshelf_current_project_id", project.id);
    const product = productMap[project.product] || {};
    const productName = product.title || "未命名商品";
    const title = document.querySelector(".pipeline-topbar-title");
    if (title) {
      title.innerHTML =
        '<div style="font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">' +
        esc(project.name) +
        '</div><div class="mono">// ' +
        esc(productName) +
        " · " +
        esc(projectStatusLabel(project)) +
        "</div>";
    }
    ["asset-prod-name", "asset-prod-card-name"].forEach(function (id) {
      const el = document.getElementById(id);
      if (el) el.textContent = productName;
    });
    const thumb = document.getElementById("asset-prod-thumb-label");
    if (thumb) thumb.textContent = productName + " · 主图";
    const cat = document.querySelector("#asset-prod-card .prod-cat");
    if (cat) cat.textContent = product.category || "未分类";
    const date = document.querySelector("#asset-prod-card .prod-date");
    if (date) date.textContent = dateOnly(product.created_at) + " 创建";
    const activeNo = stageNo(project);
    document.querySelectorAll("#stage-pill .sp-dot").forEach(function (dot) {
      const no = Number(dot.dataset.stage);
      dot.classList.toggle("done", project.status === "completed" || no < activeNo);
      dot.classList.toggle("active", no === activeNo && project.status !== "completed");
      dot.classList.toggle("fail", project.status === "failed" && no === activeNo);
    });
    document.querySelectorAll("#stage-pill .sp-line").forEach(function (line, index) {
      line.classList.toggle("done", index + 1 < activeNo || project.status === "completed");
    });
  }

  async function loadLivePipeline() {
    if (page !== "pipeline.html" || !canUseApi()) return;
    const projectId = query.get("project_id") || localStorage.getItem("airshelf_current_project_id");
    return loadWithCache(
      "pipeline:" + (projectId || "latest"),
      async function () {
        const productsData = await apiGet("products:list", "/api/products/");
        writeCache("products:list", productsData);
        let project = null;
        if (projectId) project = await apiGet("project:" + projectId, "/api/projects/" + encodeURIComponent(projectId) + "/");
        if (!project) {
          const data = await apiGet("projects:list", "/api/projects/");
          project = (data.results || [])[0];
        }
        return new Promise(function (resolve) {
          setTimeout(function () {
            resolve({ products: productsData, project: project });
          }, 120);
        });
      },
      renderLivePipelinePayload,
      "Pipeline 数据加载失败"
    );
  }

  function monthStart() {
    const now = new Date();
    return new Date(now.getFullYear(), now.getMonth(), 1);
  }

  function isThisMonth(value) {
    if (!value) return false;
    const date = new Date(value);
    return !Number.isNaN(date.getTime()) && date >= monthStart();
  }

  function moneyHTML(value) {
    const text = money(value).replace("¥", "");
    const parts = text.split(".");
    return "¥" + esc(parts[0]) + (parts[1] ? "<small>." + esc(parts[1]) + "</small>" : "");
  }

  function dashboardRecentRowHTML(project, productMap) {
    const product = productMap[project.product] || {};
    const productName = product.title || "未命名商品";
    const href = projectHref(project);
    const shots = (project.video_segments && project.video_segments.length) || 4;
    return (
      '<a class="recent-row" data-live="1" href="' +
      esc(href) +
      '"><div class="placeholder thumb"><span class="ph-frame">9:16</span></div><div class="recent-meta"><div class="name">' +
      esc(project.name || "未命名项目") +
      '</div><div class="sub">' +
      esc(productName) +
      " / AI 全生 / " +
      esc(shots) +
      ' 镜</div></div><div class="prog">' +
      progressHTML(project) +
      '</div><span class="pill ' +
      pillClass(project) +
      '"><span class="dot"></span>' +
      esc(projectStatusLabel(project)) +
      '</span><span class="btn btn-sm">' +
      (project.status === "completed" ? "打开" : "继续") +
      "</span></a>"
    );
  }

  function renderLiveDashboardPayload(payload) {
    const meData = payload.me || {};
    const summaryData = payload.summary || {};
    const productsData = payload.products || {};
    const projectsData = payload.projects || {};
    const assetsData = payload.assets || {};
    rememberAuthShape(meData);

    const projects = projectsData.results || [];
    const products = productsData.results || [];
    const assets = assetsData.results || [];
    const productMap = {};
    products.forEach(function (product) {
      productMap[product.id] = product;
    });
    const wip = projects.filter(function (project) {
      return project.status !== "completed" && project.status !== "failed";
    }).length;
    const done = projects.filter(function (project) {
      return project.status === "completed";
    }).length;
    const monthDone = projects.filter(function (project) {
      return project.status === "completed" && isThisMonth(project.updated_at || project.created_at);
    }).length;
    const failed = projects.filter(function (project) {
      return project.status === "failed";
    }).length;
    const balance = Number(summaryData.account?.balance || 0);
    const charged = Number(summaryData.charged_total || 0);
    const budget = Math.max(balance + charged, 1);
    const budgetPct = Math.min(100, (charged / budget) * 100);

    const h1 = document.querySelector(".page-head h1");
    if (h1) h1.textContent = "欢迎回来，" + displayName(meData.user);
    const sub = document.querySelector(".page-head .sub");
    if (sub) {
      const now = new Date();
      const day = new Intl.DateTimeFormat("zh-CN", { month: "2-digit", day: "2-digit", weekday: "short" }).format(now);
      sub.innerHTML =
        '<span class="mono">// ' +
        esc(day.replace(/\//g, ".")) +
        '</span><span>·</span><span>你有 <b style="color:var(--accent-black)">' +
        esc(wip) +
        " 个项目</b> 正在进行中</span>";
    }

    const stats = document.querySelectorAll(".stats .stat");
    if (stats[0]) {
      const v = stats[0].querySelector(".v");
      const d = stats[0].querySelector(".delta");
      if (v) v.textContent = String(projects.length);
      if (d) d.textContent = "本月完成 " + monthDone;
    }
    if (stats[1]) {
      const v = stats[1].querySelector(".v");
      const d = stats[1].querySelector(".delta");
      if (v) v.textContent = String(wip);
      if (d) d.textContent = failed ? failed + " 个失败需处理" : "全部正常推进";
    }
    if (stats[2]) {
      const v = stats[2].querySelector(".v");
      const d = stats[2].querySelector(".delta");
      if (v) v.textContent = String(monthDone || done);
      if (d) d.textContent = "累计完成 " + done;
    }
    if (stats[3]) {
      const v = stats[3].querySelector(".v");
      const bar = stats[3].querySelector(".bar span");
      const subText = stats[3].querySelector(".sub");
      if (v) v.innerHTML = moneyHTML(balance);
      if (bar) bar.style.width = budgetPct.toFixed(0) + "%";
      if (subText) subText.textContent = "已用 " + money(charged) + " / " + money(budget);
    }

    const more = document.querySelector(".dash-grid .section-h .more[href='projects.html']");
    if (more) more.textContent = "[ ALL · " + projects.length + " ]";
    const recentBox = document.querySelector(".card-hard");
    if (recentBox) {
      const recent = projects.slice(0, 5);
      recentBox.innerHTML = recent.length
        ? recent
            .map(function (project) {
              return dashboardRecentRowHTML(project, productMap);
            })
            .join("")
        : '<div class="recent-row" style="grid-template-columns:1fr;cursor:default;">// 当前团队还没有真实项目</div>';
    }
    const shortcutData = [
      products.length + " SKU",
      "资产 " + assets.length + " 个",
      money(balance),
      projects.length + " 个",
    ];
    document.querySelectorAll(".shortcut .d").forEach(function (el, index) {
      if (shortcutData[index]) el.textContent = shortcutData[index];
    });
  }

  async function loadLiveDashboard() {
    if (page !== "index.html" || !canUseApi()) return;
    return loadWithCache(
      "dashboard",
      async function () {
        const meData = await apiGet("auth:me", "/api/auth/me/");
        writeCache("auth:me", meData);
        const summaryData = await apiGet("billing:summary", "/api/billing/summary/");
        writeCache("billing:summary", summaryData);
        const productsData = await apiGet("products:list", "/api/products/");
        writeCache("products:list", productsData);
        const projectsData = await apiGet("projects:list", "/api/projects/");
        const assetsData = await apiGet("assets:list", "/api/assets/");
        writeCache("assets:list", assetsData);
        return { me: meData, summary: summaryData, products: productsData, projects: projectsData, assets: assetsData };
      },
      renderLiveDashboardPayload,
      "工作台数据加载失败"
    );
  }

  function settingsRowByLabel(label) {
    return Array.from(document.querySelectorAll("#sec-profile .form-row")).find(function (row) {
      const lbl = row.querySelector(".lbl");
      return lbl && lbl.textContent.indexOf(label) >= 0;
    });
  }

  function collectSettingsPrefs() {
    const fields = {};
    document.querySelectorAll('[data-track], input[type="checkbox"], select').forEach(function (el) {
      if (!el.id) return;
      fields[el.id] = el.type === "checkbox" ? Boolean(el.checked) : el.value;
    });
    const choices = {};
    ["pref-template", "pref-duration", "pref-subtitle"].forEach(function (id) {
      const selected = document.querySelector("#" + id + " .selected");
      if (selected) choices[id] = selected.dataset.v || selected.textContent.trim();
    });
    const avatar = document.getElementById("prof-avatar-preview");
    return {
      fields: fields,
      choices: choices,
      avatarText: avatar ? avatar.textContent.trim() : "",
    };
  }

  function applySettingsPrefs(prefs) {
    if (!prefs) return;
    Object.keys(prefs.fields || {}).forEach(function (id) {
      const el = document.getElementById(id);
      if (!el) return;
      if (el.type === "checkbox") el.checked = Boolean(prefs.fields[id]);
      else el.value = prefs.fields[id];
    });
    Object.keys(prefs.choices || {}).forEach(function (id) {
      const value = prefs.choices[id];
      document.querySelectorAll("#" + id + " .pref-choice, #" + id + " .dur-chip").forEach(function (node) {
        const nodeValue = node.dataset.v || node.textContent.trim();
        node.classList.toggle("selected", nodeValue === value);
      });
    });
    if (prefs.avatarText) {
      ["prof-avatar-preview", "av-up-preview"].forEach(function (id) {
        const el = document.getElementById(id);
        if (el) el.textContent = prefs.avatarText;
      });
    }
  }

  function renderLiveSettingsPayload(payload) {
    const meData = payload.me || {};
    const members = payload.members || [];
    rememberAuthShape(meData);
    const user = meData.user || {};
    const team = meData.team || {};
    const name = displayName(user);
    const email = user.email || "";
    const phone = user.phone || "";
    const avatar = initial(name);
    const member =
      members.find(function (item) {
        return item.user?.id === user.id || item.user?.email === email;
      }) || members[0] || {};
    const role = roleUi(member.role);

    const avatarEl = document.getElementById("prof-avatar-preview");
    if (avatarEl) avatarEl.textContent = avatar;
    const avatarUp = document.getElementById("av-up-preview");
    if (avatarUp) avatarUp.textContent = avatar;
    const avatarName = document.getElementById("av-up-preview-name");
    if (avatarName) avatarName.textContent = "当前头像 · 默认";
    const avatarInfo = document.getElementById("av-up-preview-info");
    if (avatarInfo) avatarInfo.textContent = "// 系统生成 · 取显示名称首字";

    const nameInput = document.getElementById("prof-name");
    const emailInput = document.getElementById("prof-email");
    const phoneInput = document.getElementById("prof-phone");
    if (nameInput) nameInput.value = name;
    if (emailInput) emailInput.value = email;
    if (phoneInput) phoneInput.value = phone;

    const emailBtn = emailInput?.closest(".val")?.querySelector("button");
    if (emailBtn) emailBtn.onclick = function () { Shell.toast("已发送验证邮件", email || "当前邮箱"); };
    const phoneBtn = phoneInput?.closest(".val")?.querySelector("button");
    if (phoneBtn) phoneBtn.onclick = function () { Shell.toast("已发送短信验证码", phone || "未绑定手机号"); };

    const teamRow = settingsRowByLabel("所属团队");
    if (teamRow) {
      const staticEl = teamRow.querySelector(".static");
      const roleEl = teamRow.querySelector(".role-tag");
      if (staticEl) staticEl.textContent = team.name || "团队";
      if (roleEl) roleEl.innerHTML = '<span class="dot"></span>' + esc(role.label) + (member.role === "owner" ? " · 创建者" : "");
    }
    const idRow = settingsRowByLabel("用户 ID");
    if (idRow) {
      const idEl = idRow.querySelector(".static");
      if (idEl) idEl.textContent = userPublicId(user);
    }

    applySettingsPrefs(readCache("settings:prefs"));
  }

  function wireLiveSettings() {
    if (page !== "settings.html") return;
    const save = document.getElementById("save-btn");
    if (save) {
      save.addEventListener("click", function () {
        setTimeout(function () {
          const invalid = document.querySelector("#sec-profile .input.invalid");
          if (!invalid) {
            writeCache("settings:prefs", collectSettingsPrefs());
            toast("本地设置已更新", "已写入 localStorage, 下次访问会先显示本地数据");
          }
        }, 0);
      });
    }
    const reset = document.getElementById("prof-avatar-reset");
    if (reset) {
      reset.addEventListener("click", function () {
        setTimeout(function () {
          const current = readCache("settings:profile");
          if (current) renderLiveSettingsPayload(current);
        }, 0);
      });
    }
  }

  async function loadLiveSettings() {
    if (page !== "settings.html" || !canUseApi()) return;
    if (!readCache("settings:profile")) {
      const me = cachedAuthShape();
      if (me) {
        writeCache("settings:profile", {
          me: me,
          summary: readCache("billing:summary"),
          members: readCache("team:members") || [],
        });
      }
    }
    return loadWithCache(
      "settings:profile",
      async function () {
        const meData = await apiGet("auth:me", "/api/auth/me/");
        writeCache("auth:me", meData);
        const summaryData = await apiGet("billing:summary", "/api/billing/summary/").catch(function () {
          return null;
        });
        if (summaryData) writeCache("billing:summary", summaryData);
        const members = await apiGet("team:members", "/api/auth/team/members/").catch(function () {
          return [];
        });
        writeCache("team:members", members);
        return { me: meData, summary: summaryData, members: members };
      },
      renderLiveSettingsPayload,
      "设置数据加载失败"
    );
  }

  const messageTypeLabel = { all: "全部", unread: "未读", task: "任务", team: "团队", billing: "计费", system: "系统" };
  const messageTypeIcon = { task: "clapperboard", team: "users", billing: "creditCard", system: "info" };
  const messagePriorityLabel = { ok: "已完成", warn: "需关注", err: "风险", info: "更新" };

  function messageIcon(name) {
    return window.IconKit && typeof window.IconKit.svg === "function" ? window.IconKit.svg(name) : "";
  }

  function messageDate(value) {
    const date = value ? new Date(value) : new Date();
    return Number.isNaN(date.getTime()) ? new Date() : date;
  }

  function messageTimeAgo(value) {
    const diff = Math.max(0, Date.now() - messageDate(value).getTime());
    const minutes = Math.floor(diff / 60000);
    if (minutes < 1) return "刚刚";
    if (minutes < 60) return minutes + "m";
    if (minutes < 1440) return Math.floor(minutes / 60) + "h";
    return Math.floor(minutes / 1440) + "d";
  }

  function messageFullTime(value) {
    const date = messageDate(value);
    const z = function (n) {
      return String(n).padStart(2, "0");
    };
    return date.getFullYear() + "-" + z(date.getMonth() + 1) + "-" + z(date.getDate()) + " " + z(date.getHours()) + ":" + z(date.getMinutes());
  }

  function notificationType(item) {
    return item.type || item.notification_type || "system";
  }

  function notificationUnread(item) {
    if (item.unread !== undefined) return Boolean(item.unread);
    return item.is_read !== true;
  }

  function normalizeNotification(item) {
    const metadata = item.metadata || {};
    const type = notificationType(item);
    return {
      id: String(item.id || item.dedupe_key || item.title || ""),
      type: type,
      priority: item.priority || metadata.priority || "info",
      unread: notificationUnread(item),
      title: item.title || "系统消息",
      brief: item.brief || metadata.brief || "",
      body: item.body || item.brief || metadata.body || "暂无详情。",
      source: item.source || metadata.source || "Airshelf",
      project: item.project_name || metadata.project_name || metadata.project || "系统",
      stage: item.stage || metadata.stage || "通知",
      owner: item.owner_label || metadata.owner || metadata.owner_label || "系统",
      cost: item.cost_label || metadata.cost || metadata.cost_label || "-",
      href: item.related_url || metadata.href || metadata.related_url || "",
      time: item.created_at || item.updated_at || item.time || new Date().toISOString(),
      metadata: metadata,
      raw: item,
    };
  }

  function normalizeNotificationsPayload(data) {
    const rows = listResults(data).map(normalizeNotification).filter(function (item) {
      return item.id;
    });
    const unread = unreadNotificationCount(data);
    return {
      count: collectionCount(data) ?? rows.length,
      unread_count: unread === null || unread === undefined ? rows.filter(function (item) { return item.unread; }).length : unread,
      results: rows,
    };
  }

  function notificationCachePayload(messages) {
    const rows = (messages || []).map(function (message) {
      return {
        id: message.id,
        type: message.type,
        notification_type: message.type,
        priority: message.priority,
        title: message.title,
        brief: message.brief,
        body: message.body,
        source: message.source,
        project_name: message.project,
        stage: message.stage,
        owner_label: message.owner,
        cost_label: message.cost,
        related_url: message.href,
        is_read: !message.unread,
        unread: message.unread,
        created_at: message.time,
        metadata: message.metadata || {},
      };
    });
    return {
      count: rows.length,
      next: null,
      previous: null,
      unread_count: rows.filter(function (item) { return item.unread; }).length,
      results: rows,
    };
  }

  function persistLiveMessages(messages) {
    const payload = notificationCachePayload(messages || []);
    delete requestMemo["notifications:list"];
    writeCache("notifications:list", payload);
    writeCache("notifications:summary", { count: payload.count, unread_count: payload.unread_count });
    return payload;
  }

  function liveMessageState() {
    if (!window.__airshelfLiveMessageState) {
      window.__airshelfLiveMessageState = { tab: "all", q: "", selectedId: null, showLogId: null };
    }
    return window.__airshelfLiveMessageState;
  }

  function liveMessages() {
    return Array.isArray(window.__airshelfLiveMessages) ? window.__airshelfLiveMessages : [];
  }

  function liveVisibleMessages() {
    const state = liveMessageState();
    const q = String(state.q || "").trim().toLowerCase();
    return liveMessages().filter(function (message) {
      if (state.tab === "unread" && !message.unread) return false;
      if (!["all", "unread"].includes(state.tab) && message.type !== state.tab) return false;
      if (!q) return true;
      return [message.title, message.brief, message.body, message.source, message.project, message.stage]
        .join(" ")
        .toLowerCase()
        .indexOf(q) >= 0;
    });
  }

  function liveMessageCounts() {
    const rows = liveMessages();
    return {
      all: rows.length,
      unread: rows.filter(function (message) { return message.unread; }).length,
      task: rows.filter(function (message) { return message.type === "task"; }).length,
      team: rows.filter(function (message) { return message.type === "team"; }).length,
      billing: rows.filter(function (message) { return message.type === "billing"; }).length,
      system: rows.filter(function (message) { return message.type === "system"; }).length,
    };
  }

  function liveMessageTimeline(message) {
    if (Array.isArray(message.metadata?.timeline)) return message.metadata.timeline;
    const created = messageFullTime(message.time);
    const rows = [[created, "通知写入团队消息中心"]];
    if (!message.unread) rows.push([messageFullTime(message.raw?.read_at || message.time), "当前用户已读"]);
    return rows;
  }

  function renderLiveMessageFilters() {
    const box = document.getElementById("msg-filters");
    if (!box) return;
    const state = liveMessageState();
    const c = liveMessageCounts();
    const filters = [
      ["all", "全部", c.all],
      ["unread", "未读", c.unread],
      ["task", "任务", c.task],
      ["team", "团队", c.team],
      ["billing", "计费", c.billing],
      ["system", "系统", c.system],
    ];
    box.innerHTML = filters
      .map(function (filter) {
        return (
          '<button class="msg-filter' +
          (state.tab === filter[0] ? " active" : "") +
          '" type="button" data-live-tab="' +
          esc(filter[0]) +
          '">' +
          esc(filter[1]) +
          '<span class="ct">' +
          esc(filter[2]) +
          "</span></button>"
        );
      })
      .join("");
    box.querySelectorAll("[data-live-tab]").forEach(function (button) {
      button.addEventListener("click", function (event) {
        event.preventDefault();
        event.stopImmediatePropagation();
        state.tab = button.dataset.liveTab || "all";
        renderLiveMessages();
      });
    });
  }

  function renderLiveMessageList() {
    const listEl = document.getElementById("msg-list");
    const countEl = document.getElementById("msg-list-count");
    if (!listEl) return;
    const state = liveMessageState();
    const list = liveVisibleMessages();
    if (countEl) countEl.textContent = "// 显示 " + list.length + " 条";
    if (!list.length) {
      listEl.innerHTML = '<div class="msg-empty">' + messageIcon("search") + "<span>没有符合条件的消息</span></div>";
      return;
    }
    listEl.innerHTML = list
      .map(function (message) {
        return (
          '<button class="msg-item' +
          (state.selectedId === message.id ? " active" : "") +
          (message.unread ? "" : " read") +
          '" type="button" data-live-message-id="' +
          esc(message.id) +
          '"><span class="msg-type-ic ' +
          esc(message.type) +
          '">' +
          messageIcon(messageTypeIcon[message.type] || "info") +
          '</span><span class="msg-item-main"><span class="msg-item-row"><span class="msg-dot"></span><span class="msg-item-title">' +
          esc(message.title) +
          '</span><span class="msg-time">' +
          esc(messageTimeAgo(message.time)) +
          '</span></span><span class="msg-brief">' +
          esc(message.brief) +
          '</span><span class="msg-item-foot"><span class="msg-priority ' +
          esc(message.priority) +
          '">' +
          esc(messagePriorityLabel[message.priority] || "更新") +
          '</span><span class="msg-priority">' +
          esc(message.project) +
          "</span></span></span></button>"
        );
      })
      .join("");
    listEl.querySelectorAll("[data-live-message-id]").forEach(function (button) {
      button.addEventListener("click", function (event) {
        event.preventDefault();
        event.stopImmediatePropagation();
        selectLiveMessage(button.dataset.liveMessageId);
      });
    });
  }

  function renderLiveMessageDetail() {
    const detail = document.getElementById("msg-detail");
    if (!detail) return;
    const state = liveMessageState();
    const list = liveMessages();
    let message = list.find(function (item) {
      return item.id === state.selectedId;
    });
    if (!message) message = liveVisibleMessages()[0] || list[0];
    if (!message) {
      state.selectedId = null;
      detail.innerHTML = '<div class="msg-detail-empty"><div class="ic">' + messageIcon("bell") + "</div><div>暂无消息</div></div>";
      return;
    }
    state.selectedId = message.id;
    const props = [
      ["来源", message.source],
      ["类别", messageTypeLabel[message.type] || "系统"],
      ["项目", message.project],
      ["阶段", message.stage],
      ["负责人", message.owner],
      ["费用", message.cost],
      ["时间", messageFullTime(message.time)],
      ["关联资源", message.href ? '<a href="' + esc(message.href) + '">' + esc(message.project) + " →</a>" : "无"],
    ]
      .map(function (row) {
        return '<span class="k">' + esc(row[0]) + '</span><span class="v">' + row[1] + "</span>";
      })
      .join("");
    const timeline = liveMessageTimeline(message)
      .map(function (row) {
        return '<div class="msg-step"><span class="t">' + esc(row[0]) + '</span><span class="d">' + esc(row[1]) + "</span></div>";
      })
      .join("");
    const primaryAction = message.href
      ? '<button class="btn btn-primary" type="button" data-live-action="goto">进入关联页面</button>'
      : '<button class="btn btn-primary" type="button" data-live-action="settings">通知设置</button>';
    const readAction = message.unread
      ? '<button class="btn" type="button" data-live-action="ack">标为已读</button>'
      : '<button class="btn" type="button" data-live-action="unread">标为未读</button>';
    detail.innerHTML =
      '<div class="msg-detail-body"><div class="msg-detail-top"><span class="msg-type-ic ' +
      esc(message.type) +
      '">' +
      messageIcon(messageTypeIcon[message.type] || "info") +
      '</span><div class="msg-detail-title"><h2>' +
      esc(message.title) +
      '</h2><div class="meta"><span>' +
      esc(message.source) +
      "</span><span>// " +
      esc(messageTypeLabel[message.type] || "系统") +
      "</span><span>" +
      esc(messageFullTime(message.time)) +
      '</span></div></div><span class="msg-priority ' +
      esc(message.priority) +
      '">' +
      esc(messagePriorityLabel[message.priority] || "更新") +
      '</span></div><p class="msg-body-text">' +
      esc(message.body) +
      '</p><div class="msg-props">' +
      props +
      '</div><div class="msg-timeline"><div class="msg-timeline-h">处理记录</div>' +
      timeline +
      '</div></div><div class="msg-detail-f"><button class="btn btn-ghost" type="button" data-live-action="archive">归档</button><button class="btn btn-ghost" type="button" data-live-action="mute">静音同类</button><span class="spacer"></span>' +
      readAction +
      primaryAction +
      "</div>";
    detail.querySelectorAll("[data-live-action]").forEach(function (button) {
      button.addEventListener("click", function (event) {
        event.preventDefault();
        event.stopImmediatePropagation();
        runLiveMessageAction(message.id, button.dataset.liveAction, button);
      });
    });
  }

  function renderLiveMessages() {
    const head = document.getElementById("msg-head-sub");
    const counts = liveMessageCounts();
    if (head) head.textContent = "// " + counts.unread + " 条未读 · " + counts.all + " 条总计";
    renderLiveMessageFilters();
    renderLiveMessageList();
    renderLiveMessageDetail();
    persistLiveMessages(liveMessages());
  }

  function setLiveMessageReadState(id, unread, response) {
    const rows = liveMessages();
    const next = response ? normalizeNotification(response) : null;
    const index = rows.findIndex(function (item) {
      return item.id === id;
    });
    if (index < 0) return;
    if (next) rows[index] = next;
    else rows[index].unread = unread;
    window.__airshelfLiveMessages = rows;
    renderLiveMessages();
  }

  async function markLiveMessageRead(id) {
    const message = liveMessages().find(function (item) {
      return item.id === id;
    });
    if (!message || !message.unread) return;
    setLiveMessageReadState(id, false);
    try {
      const response = await api("/api/ops/notifications/" + encodeURIComponent(id) + "/mark-read/", {
        method: "POST",
        body: JSON.stringify({}),
      });
      setLiveMessageReadState(id, false, response);
    } catch (error) {
      setLiveMessageReadState(id, true);
      toast("已读状态保存失败", error.message);
    }
  }

  async function markLiveMessageUnread(id) {
    setLiveMessageReadState(id, true);
    try {
      const response = await api("/api/ops/notifications/" + encodeURIComponent(id) + "/mark-unread/", {
        method: "POST",
        body: JSON.stringify({}),
      });
      setLiveMessageReadState(id, true, response);
    } catch (error) {
      setLiveMessageReadState(id, false);
      toast("未读状态保存失败", error.message);
    }
  }

  function selectLiveMessage(id) {
    const state = liveMessageState();
    state.selectedId = id;
    renderLiveMessages();
    markLiveMessageRead(id);
  }

  async function archiveLiveMessage(id, button) {
    const message = liveMessages().find(function (item) {
      return item.id === id;
    });
    if (!message) return;
    const restore = setButtonBusy(button, "归档中...");
    try {
      await api("/api/ops/notifications/" + encodeURIComponent(id) + "/archive/", {
        method: "POST",
        body: JSON.stringify({}),
      });
      window.__airshelfLiveMessages = liveMessages().filter(function (item) {
        return item.id !== id;
      });
      const state = liveMessageState();
      state.selectedId = liveMessages()[0]?.id || null;
      renderLiveMessages();
      toast("已归档", message.title);
    } catch (error) {
      toast("归档失败", error.message);
    } finally {
      restore();
    }
  }

  async function markAllLiveMessagesRead(button) {
    const rows = liveMessages();
    if (!rows.length) return;
    const restore = setButtonBusy(button, "保存中...");
    try {
      await api("/api/ops/notifications/mark-all-read/", {
        method: "POST",
        body: JSON.stringify({}),
      });
      rows.forEach(function (message) {
        message.unread = false;
      });
      window.__airshelfLiveMessages = rows;
      renderLiveMessages();
      toast("已全部标为已读", rows.length + " 条消息已写入数据库");
    } catch (error) {
      toast("批量更新失败", error.message);
    } finally {
      restore();
    }
  }

  function runLiveMessageAction(id, action, button) {
    const message = liveMessages().find(function (item) {
      return item.id === id;
    });
    if (!message) return;
    if (action === "goto") {
      go(message.href || "settings.html#sec-notify");
      return;
    }
    if (action === "settings") {
      go("settings.html#sec-notify");
      return;
    }
    if (action === "ack") {
      markLiveMessageRead(id);
      return;
    }
    if (action === "unread") {
      markLiveMessageUnread(id);
      return;
    }
    if (action === "archive") {
      archiveLiveMessage(id, button);
      return;
    }
    if (action === "mute") {
      toast("已记录静音偏好", (messageTypeLabel[message.type] || "系统") + " 类提醒设置入口在通知设置");
    }
  }

  function renderLiveMessagesPayload(data) {
    const payload = normalizeNotificationsPayload(data);
    window.__airshelfLiveMessages = payload.results;
    const state = liveMessageState();
    if (!payload.results.some(function (message) { return message.id === state.selectedId; })) {
      state.selectedId = payload.results[0]?.id || null;
    }
    renderLiveMessages();
  }

  function wireLiveMessages() {
    if (page !== "messages.html" || window.__airshelfLiveMessagesWired) return;
    window.__airshelfLiveMessagesWired = true;
    const search = document.getElementById("msg-search");
    if (search) {
      search.addEventListener(
        "input",
        function (event) {
          if (!canUseApi()) return;
          event.preventDefault();
          event.stopImmediatePropagation();
          liveMessageState().q = search.value.trim();
          renderLiveMessages();
        },
        true
      );
    }
    const markAll = document.getElementById("msg-mark-all");
    if (markAll) {
      markAll.addEventListener(
        "click",
        function (event) {
          if (!canUseApi()) return;
          event.preventDefault();
          event.stopPropagation();
          event.stopImmediatePropagation();
          markAllLiveMessagesRead(markAll);
        },
        true
      );
    }
    const settings = document.getElementById("msg-settings");
    if (settings) {
      settings.addEventListener(
        "click",
        function (event) {
          if (!canUseApi()) return;
          event.preventDefault();
          event.stopImmediatePropagation();
          go("settings.html#sec-notify");
        },
        true
      );
    }
  }

  async function loadLiveMessages() {
    if (page !== "messages.html" || !canUseApi()) return;
    return loadWithCache(
      "notifications:list",
      function () {
        return apiGet("notifications:list", "/api/ops/notifications/");
      },
      renderLiveMessagesPayload,
      "消息加载失败"
    );
  }

  function init() {
    wireAuth();
    ready(function () {
      applyContextHash();
      markHydrationLoading();
      const work = (async function () {
        hydrateShellIdentity();
        if (page === "index.html") await loadLiveDashboard();
        if (page === "products.html") {
          wireProductCreate();
          await loadLiveProducts();
        }
        if (page === "product-detail.html") await hydrateProductDetail();
        if (page === "projects.html") await loadLiveProjects();
        if (page === "projects-new.html") {
          wireProjectWizard();
          await loadLiveWizardProducts();
        }
        if (page === "library.html") {
          wireLiveAssets();
          await loadLiveAssets();
        }
        if (page === "account.html") {
          wireLiveAccount();
          await loadLiveAccount();
        }
        if (page === "settings.html") {
          wireLiveSettings();
          await loadLiveSettings();
        }
        if (page === "team.html") {
          wireLiveTeam();
          await loadLiveTeam();
        }
        if (page === "messages.html") {
          wireLiveMessages();
          await loadLiveMessages();
        }
        if (page === "pipeline.html") await loadLivePipeline();
      })();

      work
        .then(function () {
          applyContextHash();
          markHydrationDone();
        })
        .catch(function (error) {
          markHydrationError(error && error.message);
        });
    });
  }

  window.AirShelfBridge = {
    version: BRIDGE_VERSION,
    api: api,
    isLive: isLive,
    canUseApi: canUseApi,
    loadLiveProducts: loadLiveProducts,
    loadLiveProjects: loadLiveProjects,
    loadLiveDashboard: loadLiveDashboard,
    loadLiveSettings: loadLiveSettings,
    loadLiveMessages: loadLiveMessages,
  };

  init();
})();
