(() => {
  if (window.__MMW_PLUGIN_FLOATING_DESK__) return;
  window.__MMW_PLUGIN_FLOATING_DESK__ = true;

  const LEGAL_CONFIG = globalThis.MMW_LEGAL_CONSENT_CONFIG || {};
  const LEGAL_STORAGE_KEY_CONFIG = LEGAL_CONFIG.storageKeys || {};

  const STORAGE_KEYS = {
    compassJump: "compassJumpEnabled",
    douyinDownload: "douyinDetailDownloadEnabled",
    auth: "pluginAuth",
    dock: "floatingDeskPosition",
    sourceCostConfirmHidden: "mmwSourceVideoCostConfirmHidden",
    legalConsentAccepted: LEGAL_STORAGE_KEY_CONFIG.accepted || "legalConsentAccepted",
    legalConsentVersion: LEGAL_STORAGE_KEY_CONFIG.version || "legalConsentVersion",
    legalConsentAcceptedAt: LEGAL_STORAGE_KEY_CONFIG.acceptedAt || "legalConsentAcceptedAt"
  };

  const TEXT = {
    app: "MMW\u5de5\u4f5c\u53f0\u63d2\u4ef6\u52a9\u624b",
    authTitle: "\u767b\u5f55 / \u6ce8\u518c",
    video: "\u89c6\u9891\u83b7\u53d6",
    image: "\u56fe\u7247\u4e0b\u8f7d",
    imageHint: "\u6279\u91cf\u4e0b\u8f7d",
    imageEmpty: "\u6682\u65e0\u56fe\u7247\u8d44\u6e90\uff0c\u5148\u6253\u5f00\u56fe\u6587\u6216\u5546\u54c1\u9875\u518d\u5237\u65b0\u3002",
    imageOpenTabs: "\u5728\u65b0\u6807\u7b7e\u9875\u6253\u5f00",
    imageWidth: "\u5bbd\u5ea6",
    imageHeight: "\u9ad8\u5ea6",
    imageFormat: "\u683c\u5f0f",
    imagePng: "PNG",
    imageJpeg: "JPEG",
    imageOther: "\u5176\u5b83",
    jump: "\u6296\u97f3\u7f57\u76d8",
    jumpTitle: "\u6296\u97f3\u7f57\u76d8\u8df3\u8f6c",
    jumpHint: "\u52a9\u624b\u8df3\u8f6c",
    invite: "达人邀约",
    inviteHint: "官方邀约",
    inviteOpenFail: "达人邀约打开失败",
    copyMove: "复制搬家",
    copyMoveHint: "链接上架",
    ecommerceTool: "\u7535\u5546\u5de5\u5177",
    ecommerceToolHint: "\u642c\u5bb6\u4e0a\u67b6",
    qianchuanAgent: "千川托管",
    qianchuanAgentHint: "Agent 盯盘",
    qianchuanLaunch: "批量计划",
    qianchuanLaunchHint: "全域创建",
    commonTools: "常用功能",
    qianchuanTools: "千川工具",
    currentPage: "\u5f53\u524d\u9875\u9762",
    backHome: "\u8fd4\u56de\u9996\u9875",
    tutorialPage: "\u4f7f\u7528\u6559\u7a0b",
    supportPage: "\u7528\u6237\u540e\u53f0",
    downloadPage: "\u4e0b\u8f7d\u9875",
    scan: "\u5237\u65b0",
    download: "\u4e0b\u8f7d\u5df2\u9009",
    sourceBatchDownload: "\u6279\u91cf\u4e0b\u8f7d\u6e90\u89c6\u9891",
    downloadTitles: "\u4e0b\u8f7d\u6807\u9898",
    downloadOne: "\u4e0b\u8f7d",
    open: "\u6253\u5f00",
    clear: "\u6e05\u7a7a",
    all: "\u5168\u90e8",
    videoOnly: "\u89c6\u9891",
    imageOnly: "\u56fe\u7247",
    resourceEmpty: "\u6682\u65e0\u8d44\u6e90\uff0c\u5148\u64ad\u653e\u89c6\u9891\u518d\u5237\u65b0\u3002",
    openSelectorFail: "\u6253\u5f00\u5931\u8d25",
    downloadFail: "\u4e0b\u8f7d\u5931\u8d25",
    login: "\u767b\u5f55",
    logout: "\u9000\u51fa",
    logoutConfirm: "\u786e\u5b9a",
    register: "\u6ce8\u518c",
    username: "\u8d26\u53f7",
    password: "\u5bc6\u7801",
    loginNow: "\u7acb\u5373\u767b\u5f55",
    loggingIn: "\u767b\u5f55\u4e2d...",
    loginRequired: "\u8bf7\u5148\u767b\u5f55\u8d26\u53f7",
    adminRequired: "\u5f53\u524d\u8d26\u53f7\u65e0\u7ba1\u7406\u5458\u6743\u9650",
    loginMissing: "\u8bf7\u8f93\u5165\u8d26\u53f7\u548c\u5bc6\u7801",
    loginConnecting: "\u6b63\u5728\u8fde\u63a5\u8d26\u53f7\u670d\u52a1...",
    registerPageOpened: "\u5df2\u6253\u5f00\u7f51\u9875\u6ce8\u518c\u9875",
    registerPageOpenFail: "\u6ce8\u518c\u9875\u6253\u5f00\u5931\u8d25\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5",
    expired: "\u8d26\u53f7\u4f1a\u5458\u5df2\u5230\u671f",
    loginOk: "\u767b\u5f55\u6210\u529f",
    logoutOk: "\u5df2\u9000\u51fa\u767b\u5f55",
    pluginUpdateAvailable: "\u63d2\u4ef6\u6709\u65b0\u7248\u672c\u53ef\u66f4\u65b0",
    pluginAlreadyLatest: "\u5f53\u524d\u5df2\u662f\u6700\u65b0\u7248\u672c",
    compassLabel: "\u7f57\u76d8\u89c6\u9891\u8df3\u8f6c",
    compassDesc: "\u70b9\u51fb\u89c6\u9891\u76f4\u63a5\u6253\u5f00\u539f\u89c6\u9891",
    douyinLabel: "\u6296\u97f3\u89c6\u9891\u4e0b\u8f7d",
    douyinDesc: "\u8be6\u60c5\u9875\u663e\u793a\u4e0b\u8f7d\u6309\u94ae",
    selected: "\u5df2\u9009",
    resources: "\u4e2a\u8d44\u6e90",
    close: "\u5173\u95ed",
    selectAll: "\u5168\u9009",
    downloadSubmitting: "\u6b63\u5728\u63d0\u4ea4\u4e0b\u8f7d\u4efb\u52a1...",
    selectedPrefix: "\u5df2\u63d0\u4ea4 ",
    selectedSuffix: " \u4e2a\u4e0b\u8f7d",
    downloadSubmittedHint: "\uff0c\u8bf7\u5728\u6d4f\u89c8\u5668\u4e0b\u8f7d\u8bb0\u5f55\u67e5\u770b",
    titleDownloadEmpty: "\u5df2\u9009\u89c6\u9891\u672a\u6293\u53d6\u5230\u6807\u9898",
    titleDownloadSubmitting: "\u6b63\u5728\u4e0b\u8f7d\u6807\u9898...",
    titleDownloadDone: "\u5df2\u4e0b\u8f7d",
    titleDownloadUnit: "\u4e2a\u6807\u9898",
    sourceMissing: "\u5df2\u9009\u89c6\u9891\u672a\u5339\u914d\u5230\u53ef\u6eaf\u6e90\u7684\u6296\u97f3\u94fe\u63a5",
    sourceBatchConfirmPrefix: "\u6279\u91cf\u83b7\u53d6 ",
    sourceBatchConfirmMiddle: " \u4e2a\u6e90\u89c6\u9891\u9700\u8981\u82b1\u8d39 ",
    sourceBatchConfirmSuffix: " \u70b9\u6570\uff0c\u662f\u5426\u7ee7\u7eed\u83b7\u53d6\uff1f",
    sourceBatchSubmitting: "\u6b63\u5728\u63d0\u4ea4\u6279\u91cf\u6e90\u89c6\u9891...",
    sourceBatchDone: "\u5df2\u63d0\u4ea4\u6e90\u89c6\u9891 ",
    sourceLinkCached: "\u94fe\u63a5",
    titleCaptured: "\u6807\u9898",
    sourceMatched: "\u5df2\u5339\u914d",
    sourceSelected: "\u5df2\u9009",
    dontRemind: "\u4e0d\u518d\u63d0\u9192",
    cancel: "\u53d6\u6d88",
    continueFetch: "\u83b7\u53d6\u4e0b\u8f7d",
    pointsBalance: "\u4f59\u989d",
    pointsUnit: "\u79ef\u5206",
    pointsInsufficient: "\u79ef\u5206\u4f59\u989d\u4e0d\u8db3",
    pointsNeedPrefix: "\u9700\u8981",
    pointsCurrentPrefix: "\u5f53\u524d",
    legalTitle: "免责声明与用户协议",
    legalTopLabel: "使用前确认",
    legalIntro: "使用插件功能前，请确认你会在合法授权范围内操作。",
    legalRequired: String(LEGAL_CONFIG.requiredMessage || "请先阅读并同意免责声明和用户协议"),
    legalAccept: "同意并继续",
    legalAccepted: "已同意协议",
    legalDecline: "拒绝",
    legalReadConfirm: "我已阅读、理解并同意免责声明和用户协议。",
    legalLoading: "协议正文加载中...",
    legalLoadFail: "协议正文加载失败，请重新打开插件后再试。"
  };

  const ICON_URL = chrome.runtime.getURL("icon128.png");
  const DAREN_SQUARE_URL = "https://buyin.jinritemai.com/dashboard/servicehall/daren-square";
  const QIANCHUAN_LAUNCH_URL = "https://qianchuan.jinritemai.com/uni-prom?mmw_qc_launch=1";
  const QIANCHUAN_AGENT_URL = "https://qianchuan.jinritemai.com/uni-prom?mmw_qc_agent=1";
  const QIANCHUAN_AGENT_PANEL_EVENT = "MMW_QIANCHUAN_AGENT_PANEL";
  const SUPPORT_LOGIN_URL = "https://mmwgzt.xyz/support/login";
  const SUPPORT_REGISTER_URL = `${SUPPORT_LOGIN_URL}?mode=register`;
  const DOWNLOAD_CENTER_URL = "https://mmwgzt.xyz/download";
  const SHOP_MOVE_URL = chrome.runtime.getURL("features/shop-move/shop-move.html?embed=1");
  const ECOMMERCE_TOOL_PANEL_MESSAGE = "MMW_OPEN_ORIGINAL_ECOMMERCE_TOOLBAR";
  const ECOMMERCE_TOOL_PANEL_DISABLE_MESSAGE = "MMW_DISABLE_ORIGINAL_ECOMMERCE_TOOLBAR";
  const ECOMMERCE_TOOL_PANEL_STATE_MESSAGE = "MMW_ORIGINAL_ECOMMERCE_TOOLBAR_STATE";
  const ECOMMERCE_TOOL_RUNTIME_SESSION_KEY = "mmwOriginalEcommerceRuntimeEnabled";
  const ASTRONAUT_IMAGE_URL = "https://uiverse.io/astronaut.png";
  const PLUGIN_UPDATE_POLL_MS = 5 * 60 * 1000;
  const RESOURCE_REFRESH_DEBOUNCE_MS = 180;
  const LAUNCHER_SIZE = 54;
  const DESK_WIDTH = 430;
  const DESK_MAX_HEIGHT = 680;
  const SHOP_MOVE_DESK_WIDTH = 1060;
  const SHOP_MOVE_DESK_MAX_HEIGHT = 760;
  const ECOMMERCE_TOOL_PANEL_WIDTH = 188;
  const ECOMMERCE_TOOL_PANEL_HEIGHT = 520;
  const ECOMMERCE_TOOL_PANEL_LEFT = 16;
  const ECOMMERCE_TOOL_PANEL_TOP = 84;
  const EDGE_GAP = 8;
  const EDGE_SNAP = 26;
  const PEEK_OFFSET = 22;
  const PANEL_GAP = 12;
  const TOAST_LIFETIME = 2200;
  const LEGAL_CONSENT_VERSION = String(LEGAL_CONFIG.version || "");
  const LEGAL_AGREEMENT_PATH = String(LEGAL_CONFIG.agreementPath || "legal/consent-agreement.html");
  const PAGE_EXTENSIONS = new Set(["asp", "aspx", "htm", "html", "php", "shtml", "xhtml"]);
  const AUDIO_EXTENSIONS = new Set(["aac", "flac", "m4a", "mp3", "oga", "ogg", "wav"]);
  const MEDIA_EXTENSIONS = new Set([
    "3g2", "3gp", "aac", "avi", "flac", "m4a", "m4s", "m4v", "mkv", "mov",
    "mp3", "mp4", "mpeg", "mpd", "m3u8", "oga", "ogg", "ogv", "ts", "wav", "webm"
  ]);
  const TRUSTED_MEDIA_HOSTS = /(^|\.)((douyinvod|douyinpic|bytev|pstatp|ixigua|snssdk)\.com|douyinstatic\.com)$/i;
  const DOUYIN_CARD_SELECTOR = '[id^="waterfall_item_"], .search-result-card';
  const DOUYIN_PROFILE_LINK_SELECTOR = 'a[href*="/video/"], a[href*="/note/"]';

  const state = {
    opened: false,
    view: "home",
    homePage: "common",
    resourceFilter: "video",
    imageFilters: {
      format: "all",
      minWidth: 0,
      minHeight: 0,
      widthBounds: { min: 0, max: 0 },
      heightBounds: { min: 0, max: 0 }
    },
    previewId: "",
    resources: [],
    selected: new Set(),
    authLoading: false,
    authMessage: "",
    loginDraft: { username: "", password: "" },
    authRedirectView: "",
    authRedirectAction: "",
    legalConsent: {
      accepted: false,
      version: "",
      acceptedAt: 0
    },
    legalWindowOpen: false,
    legalCheck: false,
    legalAgreementHtml: "",
    legalAgreementLoading: false,
    legalAgreementError: "",
    legalRedirectView: "",
    legalRedirectAction: "",
    auth: {
      loggedIn: false,
      active: false,
      user: {},
      account: {},
      entitlement: { active: false, label: TEXT.loginRequired }
    },
    updateInfo: {
      checkedAt: 0,
      currentVersion: chrome.runtime.getManifest().version,
      latestVersion: "",
      downloadUrl: "",
      hasUpdate: false
    },
    tutorialNavigation: {
      url: "",
      revision: 0,
      checkedAt: 0
    },
    settings: {
      compassJump: true,
      douyinDownload: true
    },
    ecommerceToolPanelOpen: false,
    dock: createDefaultDock(),
    drag: null,
    suppressLauncherClick: false
  };

  let root;
  let shadow;
  let pluginUpdateTimer = 0;
  let sourceConfirmPromise = null;
  let resourceRefreshTimer = 0;
  let resourceRefreshPending = false;
  let resourceRefreshNeedsScan = false;
  let resourceRefreshInFlight = false;
  let resourceRenderFingerprint = "";

  onReady(init);

  function init() {
    syncUiMount();
    bindStorage();
    bindWindow();
    chrome.runtime.onMessage.addListener((message) => {
      if (message?.type === "FLOATING_RESOURCES_CHANGED") {
        if (!shouldDeferEmbeddedToolRefresh()) scheduleResourceRefresh(false);
      }
      if (message?.type === "OPEN_FLOATING_DESK") {
        state.opened = true;
        const nextView = message.view || "home";
        if (requiresLegalConsent(nextView) && !hasLegalConsent()) {
          state.view = "home";
          openLegalWindow(nextView);
          return;
        }
        if (!hasViewAccess(nextView)) {
          state.view = "auth";
          state.authRedirectView = nextView;
          state.authMessage = authGateMessage(nextView);
        } else {
          if (nextView === "ecommerceTool") {
            openEcommerceToolPanel();
            return;
          }
          if (nextView !== "home") state.ecommerceToolPanelOpen = false;
          state.view = nextView;
          if (nextView === "auth") {
            state.authMessage = "";
          }
        }
        syncUiMount();
      }
    });
    Promise.all([refreshLegalConsent(), refreshAuth(false), refreshSettings(), refreshDock(), refreshTutorialNavigation()])
      .then(() => {
        if (!hasLegalConsent()) openLegalWindow();
        return refreshResources(false);
      })
      .finally(syncUiMount);
  }

  function createRoot() {
    if (root && shadow) return;
    root = document.createElement("div");
    root.id = "mmw-plugin-floating-root";
    shadow = root.attachShadow({ mode: "open" });
    document.documentElement.append(root);
  }

  function syncUiMount() {
    createRoot();
    syncPluginUpdatePolling();
    render();
    syncEcommerceToolPanelPosition();
  }

  function bindStorage() {
    chrome.storage.onChanged.addListener((changes, areaName) => {
      if (areaName !== "local") return;
      let shouldRender = false;
      if (changes[STORAGE_KEYS.compassJump]) {
        state.settings.compassJump = changes[STORAGE_KEYS.compassJump].newValue !== false;
        shouldRender = true;
      }
      if (changes[STORAGE_KEYS.douyinDownload]) {
        state.settings.douyinDownload = changes[STORAGE_KEYS.douyinDownload].newValue !== false;
        shouldRender = true;
      }
      if (changes[STORAGE_KEYS.auth]) {
        state.auth = normalizeAuth(changes[STORAGE_KEYS.auth].newValue);
        if (!hasViewAccess(state.view)) {
          if (isAuthProtectedView(state.view)) state.view = "home";
        } else if (!shouldDeferEmbeddedToolRefresh()) {
          refreshResources(false);
        }
        shouldRender = !shouldDeferEmbeddedToolRefresh();
      }
      if (changes[STORAGE_KEYS.dock] && !state.drag) {
        state.dock = normalizeDock(changes[STORAGE_KEYS.dock].newValue);
        shouldRender = true;
      }
      if (
        changes[STORAGE_KEYS.legalConsentAccepted]
        || changes[STORAGE_KEYS.legalConsentVersion]
        || changes[STORAGE_KEYS.legalConsentAcceptedAt]
      ) {
        refreshLegalConsent().then(() => {
          if (hasLegalConsent()) state.legalWindowOpen = false;
          syncUiMount();
        }).catch(() => {});
        shouldRender = false;
      }
      if (shouldRender) syncUiMount();
    });
  }

  function bindWindow() {
    window.addEventListener("resize", () => {
      state.dock = normalizeDock(state.dock);
      if (shadow) {
        render();
        syncEcommerceToolPanelPosition();
      }
      persistDock();
    }, { passive: true });

    window.addEventListener("message", (event) => {
      const data = event.data;
      if (!data || data.type !== ECOMMERCE_TOOL_PANEL_STATE_MESSAGE) return;
      if (data.source !== "mmw-original-runtime-loader") return;
      state.ecommerceToolPanelOpen = data.open === true;
    }, false);
  }

  async function refreshSettings() {
    const values = await chrome.storage.local.get([
      STORAGE_KEYS.compassJump,
      STORAGE_KEYS.douyinDownload
    ]);
    state.settings.compassJump = values[STORAGE_KEYS.compassJump] !== false;
    state.settings.douyinDownload = values[STORAGE_KEYS.douyinDownload] !== false;
  }

  async function refreshDock() {
    const values = await chrome.storage.local.get([STORAGE_KEYS.dock]).catch(() => ({}));
    state.dock = normalizeDock(values[STORAGE_KEYS.dock]);
  }

  async function refreshLegalConsent() {
    const values = await chrome.storage.local.get([
      STORAGE_KEYS.legalConsentAccepted,
      STORAGE_KEYS.legalConsentVersion,
      STORAGE_KEYS.legalConsentAcceptedAt
    ]).catch(() => ({}));
    state.legalConsent = normalizeLegalConsent(values);
    if (state.legalConsent.accepted) state.legalWindowOpen = false;
    return state.legalConsent;
  }

  async function refreshAuth(refresh = false) {
    const previousView = state.view;
    const response = await sendMessage({ type: "PLUGIN_AUTH_STATUS", refresh });
    if (response?.ok) {
      state.auth = normalizeAuth(response.auth);
      if (!hasPremiumAccess() || !hasEcommerceToolAccess()) disableEcommerceToolRuntime();
    }
    if (!hasViewAccess(state.view)) {
      if (isAuthProtectedView(state.view)) state.view = "home";
    }
    if (!(isEmbeddedToolView(previousView) && state.view === previousView && hasViewAccess(state.view))) {
      syncUiMount();
    }
    return state.auth;
  }

  async function refreshPluginUpdateInfo() {
    const response = await sendMessage({ type: "GET_PLUGIN_UPDATE_INFO" });
    if (response?.ok) {
      state.updateInfo = normalizePluginUpdateInfo(response.updateInfo);
      if (shadow && !shouldDeferEmbeddedToolRefresh()) render();
    }
    return state.updateInfo;
  }

  async function refreshTutorialNavigation() {
    const response = await sendMessage({ type: "GET_TUTORIAL_NAVIGATION" });
    if (response?.ok) {
      const navigation = response.tutorialNavigation || {};
      state.tutorialNavigation = {
        url: /^https?:\/\//i.test(String(navigation.url || "").trim()) ? String(navigation.url).trim() : "",
        revision: Number(navigation.revision || 0),
        checkedAt: Number(navigation.checkedAt || Date.now())
      };
      if (shadow && !shouldDeferEmbeddedToolRefresh()) render();
    }
    return state.tutorialNavigation;
  }

  function isEmbeddedToolView(view = state.view) {
    return view === "shopMove";
  }

  function shouldDeferEmbeddedToolRefresh() {
    return state.opened && isEmbeddedToolView(state.view);
  }

  async function refreshResources(scan = true) {
    if (scan) {
      try {
        window.__MRP_SCANNER__?.scan?.();
      } catch {}
      await delay(300);
    }
    const response = await sendMessage({ type: "GET_RESOURCES" });
    const nextResources = decorateResourcesWithPageMeta(
      sortResources((response.resources || []).filter(isTrustedDownloadResource))
    );
    const nextFingerprint = createResourceRenderFingerprint(nextResources);
    const resourcesChanged = nextFingerprint !== resourceRenderFingerprint;
    state.resources = nextResources;
    resourceRenderFingerprint = nextFingerprint;
    syncImageFilters();
    state.selected = new Set(
      state.resources
        .filter((item) => state.selected.has(item.id))
        .map((item) => item.id)
    );
    if (state.previewId && !state.resources.some((item) => item.id === state.previewId)) state.previewId = "";
    syncBrowserAddress();
    if (!resourcesChanged || !state.opened) return state.resources;
    if (state.view === "home" && patchHomeResourceSummary()) return state.resources;
    render();
    return state.resources;
  }

  function scheduleResourceRefresh(scan = false, delay = RESOURCE_REFRESH_DEBOUNCE_MS) {
    resourceRefreshPending = true;
    resourceRefreshNeedsScan = resourceRefreshNeedsScan || scan;
    if (resourceRefreshTimer) window.clearTimeout(resourceRefreshTimer);
    resourceRefreshTimer = window.setTimeout(runScheduledResourceRefresh, Math.max(0, delay));
  }

  async function runScheduledResourceRefresh() {
    resourceRefreshTimer = 0;
    if (resourceRefreshInFlight) {
      scheduleResourceRefresh(resourceRefreshNeedsScan);
      return;
    }

    const scan = resourceRefreshNeedsScan;
    resourceRefreshPending = false;
    resourceRefreshNeedsScan = false;
    resourceRefreshInFlight = true;
    try {
      await refreshResources(scan);
    } finally {
      resourceRefreshInFlight = false;
      if (resourceRefreshPending) scheduleResourceRefresh(resourceRefreshNeedsScan);
    }
  }

  function createResourceRenderFingerprint(resources) {
    return resources.map((item) => [
      item.id,
      item.url,
      item.kind,
      item.type,
      item.mime,
      item.name,
      item.fileName,
      item.videoTitle,
      item.size,
      item.width,
      item.height,
      item.duration,
      item.quality,
      item.poster,
      item.thumbnail,
      item.cover,
      item.pageUrl,
      item.sourceUrl
    ].map((value) => String(value ?? "")).join("\u001f")).join("\u001e");
  }

  function patchHomeResourceSummary() {
    if (!shadow || state.view !== "home" || !shadow.querySelector(".homeScene")) return false;
    const videoCount = resolveDisplayResources(getVideoResources()).length;
    const imageCount = resolveDisplayImageResources(getImageResources()).length;
    const summaries = {
      video: `${videoCount} ${TEXT.resources}`,
      image: imageCount ? `${imageCount} ${TEXT.resources}` : TEXT.imageHint
    };
    Object.entries(summaries).forEach(([key, value]) => {
      const target = shadow.querySelector(`[data-home-resource-count="${key}"] .featureCardContent em`);
      if (target && target.textContent !== value) target.textContent = value;
    });
    return true;
  }

  function syncBrowserAddress() {
    if (!shadow) return;
    const browser = getBrowserState();
    const addressButton = shadow.querySelector(".browserAddress");
    if (addressButton) {
      addressButton.dataset.openLink = browser.currentUrl;
      const addressText = addressButton.querySelector(".browserAddressText");
      if (addressText && addressText.textContent !== browser.address) addressText.textContent = browser.address;
    }
    const openButton = shadow.querySelector(".browserToolbar .browserNavButton[data-open-link]");
    if (openButton) openButton.dataset.openLink = browser.currentUrl;
  }

  function render() {
    if (!shadow) return;

    const launcherRect = getLauncherRect();
    const deskRect = getDeskRect();
    const launcherClasses = [
      "launcher",
      state.dock.hidden && !state.opened && !state.drag ? `peek-${state.dock.side}` : "",
      state.opened ? "is-open" : "",
      state.drag ? "is-dragging" : "",
      state.updateInfo?.hasUpdate ? "has-update" : ""
    ].filter(Boolean).join(" ");

    shadow.innerHTML = `
      <style>${styles()}</style>
      <button
        class="${launcherClasses}"
        type="button"
        title="${state.updateInfo?.hasUpdate ? TEXT.pluginUpdateAvailable : TEXT.app}"
        aria-label="${state.updateInfo?.hasUpdate ? TEXT.pluginUpdateAvailable : TEXT.app}"
        style="left:${launcherRect.left}px;top:${launcherRect.top}px;"
      >
        <img class="launcherImage" src="${ICON_URL}" alt="">
      </button>
      ${state.opened ? desktopHtml(deskRect) : ""}
      ${state.legalWindowOpen && !hasLegalConsent() ? legalWindowHtml() : ""}
    `;

    bindUi();
  }

  function desktopHtml(deskRect) {
    const autoHeight = state.view === "home" || state.view === "auth";
    const deskClasses = ["desk", state.dock.side === "right" ? "open-left" : "", autoHeight ? "auto-height" : ""].filter(Boolean).join(" ");
    const deskHeight = autoHeight ? "" : `height:${deskRect.maxHeight}px;`;
    return `
      <section
        class="${deskClasses}"
        role="dialog"
        aria-label="${TEXT.app}"
        style="left:${deskRect.left}px;top:${deskRect.top}px;width:${deskRect.width}px;${deskHeight}max-height:${deskRect.maxHeight}px;"
      >
        ${browserShellHtml()}
      </section>
    `;
  }

  function legalWindowHtml() {
    const acceptDisabled = state.legalCheck ? "" : " disabled";
    const agreementHtml = state.legalAgreementError
      ? `<p class="legalFloatError">${escapeHtml(state.legalAgreementError)}</p>`
      : (state.legalAgreementHtml || `<p>${TEXT.legalLoading}</p>`);
    return `
      <section class="legalFloat" role="dialog" aria-label="${TEXT.legalTitle}">
        <div class="legalFloatHeader">
          <span class="legalFloatIcon"><img src="${ICON_URL}" alt=""></span>
          <div class="legalFloatTitle">
            <small>${TEXT.legalTopLabel}</small>
            <strong>${TEXT.legalTitle}</strong>
          </div>
          <button class="legalFloatClose" data-action="close-legal-window" type="button" aria-label="${TEXT.close}">&#10005;</button>
        </div>
        <p class="legalFloatIntro">${TEXT.legalIntro}</p>
        <div class="legalFloatBody" tabindex="0">${agreementHtml}</div>
        <label class="legalFloatCheck">
          <input type="checkbox" data-action="toggle-legal-check"${state.legalCheck ? " checked" : ""}>
          <span>${TEXT.legalReadConfirm}</span>
        </label>
        <div class="legalFloatActions">
          <button class="legalFloatSecondary" data-action="decline-legal-consent" type="button">${TEXT.legalDecline}</button>
          <button class="legalFloatPrimary" data-action="accept-legal-consent" type="button"${acceptDisabled}>${TEXT.legalAccept}</button>
        </div>
      </section>
    `;
  }

  function browserShellHtml() {
    const browser = getBrowserState();
    const manifestVersion = escapeHtml(chrome.runtime.getManifest().version || "");
    const viewportClassName = [
      "browserViewportInner",
      state.view === "auth" ? "authViewport" : "",
      state.view === "home" ? "homeViewport" : "",
      state.view === "shopMove" ? "shopMoveViewport" : ""
    ].filter(Boolean).join(" ");
    return `
      <div class="browserShell">
        <div class="browserTabs" data-drag-handle="desk">
          <div class="browserTabsLeft">
            <button class="browserTab browserHomeTab${state.view === "home" ? " active" : ""}" data-view="home" type="button" aria-label="${TEXT.app}">
              <img class="browserTabIcon" src="${ICON_URL}" alt="">
              <span class="browserHomeMeta">
                <span>${TEXT.app}</span>
                <small class="browserVersionTag">v${manifestVersion}</small>
              </span>
            </button>
            ${state.view !== "home" ? `
              <div class="browserTab browserFeatureTab active">
                <span>${escapeHtml(browser.pageTitle)}</span>
                <button class="browserTabClose" data-action="close-feature-tab" type="button" aria-label="${TEXT.close}">&#10005;</button>
              </div>
            ` : ""}
          </div>
          ${pluginUpdateBellHtml()}
          <div class="browserWindowButtons">
            <span class="browserWindowDot"></span>
            <span class="browserWindowDot"></span>
            <button class="browserWindowClose" data-action="close" type="button" aria-label="${TEXT.close}">&#10005;</button>
          </div>
        </div>
        <div class="browserToolbar">
          <button class="browserNavButton" data-view="home" type="button"${state.view === "home" ? " disabled" : ""} aria-label="${TEXT.app}">&#8592;</button>
          <button class="browserAddress" data-open-link="${escapeHtml(browser.currentUrl)}" type="button" aria-label="${TEXT.currentPage}">
            <span class="browserAddressBadge"></span>
            <span class="browserAddressText">${escapeHtml(browser.address)}</span>
          </button>
          <button class="browserNavButton" data-open-link="${escapeHtml(browser.currentUrl)}" type="button" aria-label="${TEXT.open}">&#8599;</button>
        </div>
        <div class="browserStatusBar">
          ${browser.links.map(browserLinkHtml).join("")}
        </div>
        <div class="browserViewport">
          <div class="${viewportClassName}">${currentViewHtml()}</div>
        </div>
      </div>
    `;
  }

  function browserLinkHtml(link) {
    const disabled = !link.url;
    return `
      <button class="browserPageChip${link.active ? " active" : ""}${disabled ? " disabled" : ""}" ${disabled ? "disabled title=\"\u7ba1\u7406\u5458\u5c1a\u672a\u914d\u7f6e\u6559\u7a0b\u94fe\u63a5\"" : `data-open-link="${escapeHtml(link.url)}"`} type="button">
        <b>${escapeHtml(link.label)}</b>
      </button>
    `;
  }

  function pluginUpdateBellHtml() {
    const hasUpdate = Boolean(state.updateInfo?.hasUpdate);
    const tooltip = hasUpdate ? TEXT.pluginUpdateAvailable : TEXT.pluginAlreadyLatest;
    const updateLink = hasUpdate ? (state.updateInfo.downloadUrl || DOWNLOAD_CENTER_URL) : "";
    return `
      <div class="browserUpdateSlot">
        <button
          class="browserUpdateBell${hasUpdate ? " has-update" : ""}"
          type="button"
          aria-label="${tooltip}"
          title="${tooltip}"
          data-tooltip="${tooltip}"
          ${updateLink ? `data-open-link="${escapeHtml(updateLink)}"` : ""}
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path d="M12 3a4 4 0 0 0-4 4v1.1c0 .72-.2 1.43-.58 2.03l-1.2 1.9A2 2 0 0 0 7.91 15h8.18a2 2 0 0 0 1.69-2.97l-1.2-1.9A3.8 3.8 0 0 1 16 8.1V7a4 4 0 0 0-4-4Zm0 18a2.5 2.5 0 0 0 2.45-2h-4.9A2.5 2.5 0 0 0 12 21Z"></path>
          </svg>
        </button>
      </div>
    `;
  }

  function currentViewHtml() {
    if (state.view === "auth") return authPageHtml();
    if (state.view === "video") return videoHtml();
    if (state.view === "image") return imageHtml();
    if (state.view === "jump") return jumpHtml();
    if (state.view === "shopMove") return shopMoveHtml();
    return homeHtml();
  }

  function getBrowserState() {
    return {
      currentUrl: location.href,
      address: formatBrowserAddress(location.href),
      pageTitle: currentBrowserViewTitle(),
      links: getBrowserLinks()
    };
  }

  function currentBrowserViewTitle() {
    if (state.view === "auth") return TEXT.authTitle;
    if (state.view === "video") return TEXT.video;
    if (state.view === "image") return TEXT.image;
    if (state.view === "jump") return TEXT.jumpTitle;
    if (state.view === "shopMove") return TEXT.copyMove;
    return TEXT.app;
  }

  function getBrowserLinks() {
    return [
      { label: TEXT.supportPage, url: SUPPORT_LOGIN_URL, active: false },
      { label: TEXT.downloadPage, url: DOWNLOAD_CENTER_URL, active: false },
      { label: TEXT.tutorialPage, url: state.tutorialNavigation.url, active: false }
    ];
  }

  function homeHtml() {
    const videoCount = resolveDisplayResources(getVideoResources()).length;
    const imageCount = resolveDisplayImageResources(getImageResources()).length;
    const loginLocked = hasLoginAccess() ? "" : " locked";
    const premiumLocked = hasPremiumAccess() ? "" : " locked";
    const ecommerceVisible = hasEcommerceToolAccess();
    const qianchuanAgentLocked = hasQianchuanAgentAccess() ? "" : " locked";
    return `
      <div class="homeScene">
        <div class="homeSceneBackdrop" aria-hidden="true">
          <span class="homeSceneMoon"></span>
          <span class="homeSceneGlow"></span>
          <span class="homeSceneStars homeSceneStarsA"></span>
          <span class="homeSceneStars homeSceneStarsB"></span>
          <span class="homeSceneStars homeSceneStarsC"></span>
          <span class="homeSceneTrail"></span>
          <img class="homeSceneAstronaut" src="${ASTRONAUT_IMAGE_URL}" alt="" draggable="false">
        </div>
        <div class="homeSceneContent">
          ${authHtml()}
          <div class="homePageStage">
            <section id="mmw-home-page-common" class="homePagePanel${state.homePage === "common" ? " is-active" : ""}" data-home-panel="common" role="tabpanel" aria-label="${TEXT.commonTools}"${state.homePage === "common" ? "" : " hidden"}>
              <div class="homeGrid">
                ${featureCardHtml("video", TEXT.video, `${videoCount} ${TEXT.resources}`, "cyan", loginLocked)}
                ${featureCardHtml("image", TEXT.image, imageCount ? `${imageCount} ${TEXT.resources}` : TEXT.imageHint, "emerald", loginLocked)}
                ${featureCardHtml("jump", TEXT.jump, TEXT.jumpHint, "violet", premiumLocked)}
                ${featureCardHtml("", TEXT.invite, TEXT.inviteHint, "amber", premiumLocked, 'data-action="open-invite-entry"')}
                ${ecommerceVisible ? featureCardHtml("ecommerceTool", TEXT.ecommerceTool, TEXT.ecommerceToolHint, "blue", premiumLocked) : ""}
              </div>
            </section>
            <section id="mmw-home-page-qianchuan" class="homePagePanel${state.homePage === "qianchuan" ? " is-active" : ""}" data-home-panel="qianchuan" role="tabpanel" aria-label="${TEXT.qianchuanTools}"${state.homePage === "qianchuan" ? "" : " hidden"}>
              <div class="homeGrid homeGridQianchuan">
                ${featureCardHtml("", TEXT.qianchuanAgent, TEXT.qianchuanAgentHint, "blue", qianchuanAgentLocked, 'data-action="open-qianchuan-agent"')}
                ${featureCardHtml("", TEXT.qianchuanLaunch, TEXT.qianchuanLaunchHint, "cyan", loginLocked, 'data-action="open-qianchuan-launch"')}
              </div>
            </section>
          </div>
          <div class="homePager" role="tablist" aria-label="功能分页">
            ${homePageButtonHtml("common", TEXT.commonTools)}
            ${homePageButtonHtml("qianchuan", TEXT.qianchuanTools)}
          </div>
        </div>
      </div>
    `;
  }

  function homePageButtonHtml(page, label) {
    const active = state.homePage === page;
    return `<button class="homePagerButton${active ? " is-active" : ""}" data-home-page="${page}" type="button" role="tab" aria-controls="mmw-home-page-${page}" aria-selected="${active}" tabindex="${active ? "0" : "-1"}">${escapeHtml(label)}</button>`;
  }

  function featureCardHtml(view, title, subtitle, accent, lockedClass, triggerAttr = "") {
    const cardTrigger = triggerAttr || `data-view="${view}"`;
    const resourceCountAttr = view === "video" || view === "image"
      ? ` data-home-resource-count="${view}"`
      : "";
    return `
      <button class="feature featureCard feature-${accent}${lockedClass}" ${cardTrigger}${resourceCountAttr} type="button">
        <span class="featureCardInner">
          <span class="featureCardGlow"></span>
          <span class="featureCardGrid"></span>
          <span class="featureCardLines"><span></span><span></span><span></span></span>
          <span class="featureCardParticles"><span></span><span></span><span></span><span></span><span></span></span>
          <span class="featureCardCorners"><span></span><span></span><span></span><span></span></span>
          <span class="featureCardContent">
            <strong>${escapeHtml(title)}</strong>
            <em>${escapeHtml(subtitle)}</em>
          </span>
        </span>
      </button>
    `;
  }

  function authHtml() {
    if (state.auth.loggedIn) {
      const name = escapeHtml(authUsername());
      const note = escapeHtml(authStatusText());
      const balance = pointsBalanceInfo();
      return `
        <div class="authCard">
          <div class="avatar">${escapeHtml(name.slice(0, 1) || "U")}</div>
          <div class="authMeta">
            <div class="authNameRow">
              <b>${name}</b>
              ${balance.available ? `<span class="pointsBadge">${TEXT.pointsBalance} ${escapeHtml(balance.text)}</span>` : ""}
            </div>
            <small>${note}</small>
          </div>
          ${logoutButtonHtml()}
        </div>
      `;
    }
    return loginButtonHtml();
  }

  function loginButtonHtml() {
    return `
      <button class="loginButton" data-action="open-login" type="button" aria-label="${TEXT.login}">
        ${authActionButtonIconHtml()}
        <span class="loginButtonNow">\u7acb\u5373!</span>
        <span class="loginButtonMain">${TEXT.login}</span>
      </button>
    `;
  }

  function logoutButtonHtml() {
    return `
      <button class="loginButton authLogoutButton" data-action="logout" type="button" aria-label="${TEXT.logout}">
        ${authActionButtonIconHtml()}
        <span class="loginButtonNow">${TEXT.logoutConfirm}</span>
        <span class="loginButtonMain">${TEXT.logout}</span>
      </button>
    `;
  }

  function authActionButtonIconHtml() {
    return `
      <svg class="loginButtonIcon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" width="36" height="36" aria-hidden="true">
          <rect width="36" height="36" x="0" y="0" fill="#fdd835"></rect>
          <path fill="#e53935" d="M38.67,42H11.52C11.27,40.62,11,38.57,11,36c0-5,0-11,0-11s1.44-7.39,3.22-9.59c1.67-2.06,2.76-3.48,6.78-4.41c3-0.7,7.13-0.23,9,1c2.15,1.42,3.37,6.67,3.81,11.29c1.49-0.3,5.21,0.2,5.5,1.28C40.89,30.29,39.48,38.31,38.67,42z"></path>
          <path fill="#b71c1c" d="M39.02,42H11.99c-0.22-2.67-0.48-7.05-0.49-12.72c0.83,4.18,1.63,9.59,6.98,9.79c3.48,0.12,8.27,0.55,9.83-2.45c1.57-3,3.72-8.95,3.51-15.62c-0.19-5.84-1.75-8.2-2.13-8.7c0.59,0.66,3.74,4.49,4.01,11.7c0.03,0.83,0.06,1.72,0.08,2.66c4.21-0.15,5.93,1.5,6.07,2.35C40.68,33.85,39.8,38.9,39.02,42z"></path>
          <path fill="#212121" d="M35,27.17c0,3.67-0.28,11.2-0.42,14.83h-2C32.72,38.42,33,30.83,33,27.17c0-5.54-1.46-12.65-3.55-14.02c-1.65-1.08-5.49-1.48-8.23-0.85c-3.62,0.83-4.57,1.99-6.14,3.92L15,16.32c-1.31,1.6-2.59,6.92-3,8.96v10.8c0,2.58,0.28,4.61,0.54,5.92H10.5c-0.25-1.41-0.5-3.42-0.5-5.92l0.02-11.09c0.15-0.77,1.55-7.63,3.43-9.94l0.08-0.09c1.65-2.03,2.96-3.63,7.25-4.61c3.28-0.76,7.67-0.25,9.77,1.13C33.79,13.6,35,22.23,35,27.17z"></path>
          <path fill="#01579b" d="M17.165,17.283c5.217-0.055,9.391,0.283,9,6.011c-0.391,5.728-8.478,5.533-9.391,5.337c-0.913-0.196-7.826-0.043-7.696-5.337C9.209,18,13.645,17.32,17.165,17.283z"></path>
          <path fill="#212121" d="M40.739,37.38c-0.28,1.99-0.69,3.53-1.22,4.62h-2.43c0.25-0.19,1.13-1.11,1.67-4.9c0.57-4-0.23-11.79-0.93-12.78c-0.4-0.4-2.63-0.8-4.37-0.89l0.1-1.99c1.04,0.05,4.53,0.31,5.71,1.49C40.689,24.36,41.289,33.53,40.739,37.38z"></path>
          <path fill="#81d4fa" d="M10.154,20.201c0.261,2.059-0.196,3.351,2.543,3.546s8.076,1.022,9.402-0.554c1.326-1.576,1.75-4.365-0.891-5.267C19.336,17.287,12.959,16.251,10.154,20.201z"></path>
          <path fill="#212121" d="M17.615,29.677c-0.502,0-0.873-0.03-1.052-0.069c-0.086-0.019-0.236-0.035-0.434-0.06c-5.344-0.679-8.053-2.784-8.052-6.255c0.001-2.698,1.17-7.238,8.986-7.32l0.181-0.002c3.444-0.038,6.414-0.068,8.272,1.818c1.173,1.191,1.712,3,1.647,5.53c-0.044,1.688-0.785,3.147-2.144,4.217C22.785,29.296,19.388,29.677,17.615,29.677zM17.086,17.973c-7.006,0.074-7.008,4.023-7.008,5.321c-0.001,3.109,3.598,3.926,6.305,4.27c0.273,0.035,0.48,0.063,0.601,0.089c0.563,0.101,4.68,0.035,6.855-1.732c0.865-0.702,1.299-1.57,1.326-2.653c0.051-1.958-0.301-3.291-1.073-4.075c-1.262-1.281-3.834-1.255-6.825-1.222L17.086,17.973z"></path>
          <path fill="#e1f5fe" d="M15.078,19.043c1.957-0.326,5.122-0.529,4.435,1.304c-0.489,1.304-7.185,2.185-7.185,0.652C12.328,19.467,15.078,19.043,15.078,19.043z"></path>
      </svg>
    `;
  }

  function authPageHtml() {
    if (state.auth.loggedIn) {
      const name = escapeHtml(authUsername());
      const note = escapeHtml(authStatusText());
      return `
        <div class="authPage authPageLoggedIn">
          <div class="authPageSummary">
            <div class="avatar">${escapeHtml(name.slice(0, 1) || "U")}</div>
            <div class="authPageSummaryMeta">
              <b>${name}</b>
              <small>${note}</small>
            </div>
          </div>
          <div class="authPageActions">
            <button data-view="home" type="button">${TEXT.backHome}</button>
            <button data-action="logout" type="button">${TEXT.logout}</button>
          </div>
        </div>
      `;
    }

    const loginSubmitDisabled = state.authLoading ? " disabled" : "";
    return `
      <div class="authPage">
        <div class="authSwitch" role="group" aria-label="${TEXT.authTitle}">
          <span class="authSwitchLabel active">${TEXT.login}</span>
          <button class="authSwitchToggle" data-action="open-web-register" type="button" aria-label="${TEXT.register}">
            <span class="authSwitchThumb"></span>
          </button>
          <button class="authSwitchLabel" data-action="open-web-register" type="button">${TEXT.register}</button>
        </div>
        <div class="authFlipCard">
          <div class="authFlipInner">
            <section class="authPanel authPanelFront">
              <div class="authPanelTitle">${TEXT.login}</div>
              <form class="authForm" data-auth-form="login">
                <input name="username" autocomplete="username" placeholder="${TEXT.username}" value="${escapeHtml(state.loginDraft.username)}">
                <input name="password" autocomplete="current-password" type="password" placeholder="${TEXT.password}" value="${escapeHtml(state.loginDraft.password)}">
                <button class="authSubmit" type="submit"${loginSubmitDisabled}>${state.authLoading ? TEXT.loggingIn : TEXT.loginNow}</button>
              </form>
            </section>
          </div>
        </div>
        ${state.authMessage ? `<div class="authPageMessage">${escapeHtml(state.authMessage)}</div>` : ""}
      </div>
    `;
  }

  function videoHtml() {
    const visibleResources = getVisibleResources();
    const selectedCount = visibleResources.filter((item) => state.selected.has(item.id)).length;
    const sourceVisible = hasSourceTraceAccess();
    const selectedSourceCount = sourceVisible
      ? visibleResources.filter((item) => state.selected.has(item.id) && getSourceTraceUrl(item)).length
      : 0;
    const allSelected = Boolean(visibleResources.length && selectedCount === visibleResources.length);
    const list = visibleResources.length
      ? visibleResources.map(resourceItemHtml).join("")
      : `<div class="empty">${TEXT.resourceEmpty}</div>`;
    return `
      <div class="videoPage">
        <div class="subHead">
          <button class="back" data-view="home" type="button" aria-label="${TEXT.app}">&#8592;</button>
          <b>${TEXT.video}</b>
        </div>
        <div class="tools">
          <label class="selectAll${visibleResources.length ? "" : " disabled"}">
            <input data-action="toggle-all" type="checkbox"${allSelected ? " checked" : ""}${visibleResources.length ? "" : " disabled"}>
            <span>${TEXT.selectAll}</span>
          </label>
          <button data-action="clear" type="button">${TEXT.clear}</button>
          <button data-action="scan" type="button">${TEXT.scan}</button>
          <button class="titleDownloadButton" data-action="download-titles" type="button"${selectedCount ? "" : " disabled"}>${TEXT.downloadTitles}</button>
          <button data-action="download" type="button"${selectedCount ? "" : " disabled"}>${TEXT.download}</button>
          ${sourceVisible ? `<button class="sourceBatchButton" data-action="download-source-batch" type="button"${selectedSourceCount ? "" : " disabled"}>${TEXT.sourceBatchDownload}</button>` : ""}
        </div>
        <div class="count">${TEXT.selected} ${selectedCount} / ${visibleResources.length}</div>
        <div class="resourceList">${list}</div>
      </div>
    `;
  }

  function imageHtml() {
    const visibleResources = getVisibleResources();
    const dimensionFilteredResources = getDimensionFilteredImageResources();
    const selectedCount = visibleResources.filter((item) => state.selected.has(item.id)).length;
    const allSelected = Boolean(visibleResources.length && selectedCount === visibleResources.length);
    const formatStats = buildImageFormatStats(dimensionFilteredResources);
    const list = visibleResources.length
      ? visibleResources.map(imageCardHtml).join("")
      : `<div class="empty">${TEXT.imageEmpty}</div>`;
    return `
      <div class="imagePage">
        <div class="subHead">
          <button class="back" data-view="home" type="button" aria-label="${TEXT.app}">&#8592;</button>
          <b>${TEXT.image}</b>
        </div>
        <div class="imageFiltersPanel">
          ${imageRangeControlHtml("width", TEXT.imageWidth, state.imageFilters.minWidth, state.imageFilters.widthBounds)}
          ${imageRangeControlHtml("height", TEXT.imageHeight, state.imageFilters.minHeight, state.imageFilters.heightBounds)}
          <div class="imageFormatRow">
            <span class="imageFilterLabel">${TEXT.imageFormat}:</span>
            ${imageFormatChipHtml("png", TEXT.imagePng, formatStats.png)}
            ${imageFormatChipHtml("jpeg", TEXT.imageJpeg, formatStats.jpeg)}
            ${imageFormatChipHtml("other", TEXT.imageOther, formatStats.other)}
          </div>
          <div class="imageActionRow">
            <div class="imageActionMeta">${selectedCount}/${visibleResources.length}</div>
            <label class="selectAll${visibleResources.length ? "" : " disabled"}">
              <input data-action="toggle-all" type="checkbox"${allSelected ? " checked" : ""}${visibleResources.length ? "" : " disabled"}>
              <span>${TEXT.selectAll}</span>
            </label>
            <button data-action="download" type="button"${selectedCount ? "" : " disabled"}>${TEXT.download}</button>
            <button data-action="scan" type="button">${TEXT.scan}</button>
          </div>
        </div>
        <div class="imageGrid">${list}</div>
        <div class="imageFooterBar">
          <button class="imageFooterButton" data-action="open-selected-images" type="button"${selectedCount ? "" : " disabled"}>${TEXT.imageOpenTabs}</button>
        </div>
      </div>
    `;
  }

  function imageRangeControlHtml(axis, label, value, bounds) {
    const min = 0;
    const max = Math.max(0, Number(bounds?.max) || 0);
    const current = clampNumber(Number(value) || 0, min, max);
    return `
      <div class="imageRangeRow">
        <span class="imageRangeLabel">${escapeHtml(label)}:</span>
        <span class="imageRangeValue">${current}px</span>
        <input
          class="imageRangeInput"
          data-image-range="${escapeHtml(axis)}"
          type="range"
          min="${min}"
          max="${max}"
          value="${current}"
          ${max > min ? "" : "disabled"}
        >
        <span class="imageRangeMax">${max}px</span>
      </div>
    `;
  }

  function imageFormatChipHtml(format, label, count) {
    const active = state.imageFilters.format === format ? " active" : "";
    return `<button class="imageFormatChip${active}" data-image-format="${format}" type="button">${escapeHtml(label)} ${count}</button>`;
  }

  function imageCardHtml(item) {
    const checked = state.selected.has(item.id) ? " checked" : "";
    const id = escapeHtml(item.id);
    const title = escapeHtml(item.fileName || item.name || item.url || "image");
    const url = escapeHtml(item.url || "");
    const metaText = escapeHtml(formatImageMeta(item));
    return `
      <article class="imageCard">
        <label class="imageCardCheckbox">
          <input type="checkbox" data-resource-id="${id}"${checked}>
          <span></span>
        </label>
        <button class="imageCardPreview" data-resource-open="${id}" type="button" aria-label="${title}">
          <img class="imageCardThumb" data-image-thumb="${id}" src="${url}" alt="${title}">
        </button>
        <div class="imageCardFooter">
          <div class="imageCardMeta" title="${title}">
            <span>${metaText}</span>
          </div>
          <div class="imageCardActions">
            <button class="iconActionButton" data-resource-open="${id}" type="button" aria-label="${TEXT.open}" title="${TEXT.open}">
              <svg viewBox="0 0 20 20" aria-hidden="true"><path d="M11 3h6v6h-1.8V6.07l-6.66 6.66-1.28-1.27 6.67-6.66H11V3ZM5 5h4v1.8H6.8v6.4h6.4V11H15v4H5V5Z"/></svg>
            </button>
            <button class="iconActionButton" data-resource-download="${id}" type="button" aria-label="${TEXT.downloadOne}" title="${TEXT.downloadOne}">
              <svg viewBox="0 0 20 20" aria-hidden="true"><path d="M9.1 3h1.8v7.02l2.52-2.52 1.27 1.28L10 13.47 5.31 8.78l1.27-1.28 2.52 2.52V3ZM4 15h12v2H4v-2Z"/></svg>
            </button>
          </div>
        </div>
      </article>
    `;
  }

  function filterButtonHtml(filter, label) {
    const active = state.resourceFilter === filter ? " active" : "";
    return `<button class="filter${active}" data-filter="${filter}" type="button">${label}</button>`;
  }

  function resourceItemHtml(item) {
    const checked = state.selected.has(item.id) ? " checked" : "";
    const expanded = state.previewId === item.id ? " expanded" : "";
    const type = resourceType(item);
    const name = escapeHtml(item.videoTitle || item.name || item.url || "media");
    const url = escapeHtml(item.url || "");
    const size = formatSize(item.size);
    const downloadable = isTrustedDownloadResource(item);
    return `
      <div class="resource${expanded}">
        <input type="checkbox" data-resource-id="${escapeHtml(item.id)}"${checked}>
        <button class="previewButton" data-preview-id="${escapeHtml(item.id)}" type="button" aria-label="${name}">
          ${resourcePreviewHtml(item)}
        </button>
        <div class="resourceMeta" data-preview-id="${escapeHtml(item.id)}">
          <b>${name}</b>
          <small>${url}</small>
          ${resourceInfoHtml(item)}
          <div class="resourceTags">
            <em>${escapeHtml(type)}</em>
            ${size ? `<em>${size}</em>` : ""}
          </div>
        </div>
        <div class="resourceActions">
          <button data-resource-download="${escapeHtml(item.id)}" type="button"${downloadable ? "" : " disabled"}>${TEXT.downloadOne}</button>
          <button data-resource-open="${escapeHtml(item.id)}" type="button">${TEXT.open}</button>
        </div>
        ${state.previewId === item.id ? resourcePlayerHtml(item) : ""}
      </div>
    `;
  }

  function resourceInfoHtml(item) {
    const authorName = escapeHtml(item.authorName || "");
    const likeCountText = escapeHtml(item.likeCountText || "");
    const videoTitle = String(item.videoTitle || "").trim();
    const sourceTraceUrl = hasSourceTraceAccess() ? getSourceTraceUrl(item) : "";
    if (!authorName && !likeCountText && !sourceTraceUrl && !videoTitle) return "";
    return `
      <div class="resourceInfo">
        ${authorName ? `<span>${authorName}</span>` : ""}
        ${likeCountText ? `<span>${likeCountText}</span>` : ""}
        ${sourceTraceUrl ? `<span class="sourceLinkChip" title="${escapeHtml(sourceTraceUrl)}">${TEXT.sourceLinkCached}</span>` : ""}
        ${videoTitle ? `<span class="titleCapturedChip" title="${escapeHtml(videoTitle)}">${TEXT.titleCaptured}</span>` : ""}
      </div>
    `;
  }

  function resourcePreviewHtml(item) {
    const url = escapeHtml(item.url || "");
    if (item.kind === "image") {
      return `<img class="resourcePreview" src="${url}" alt="">`;
    }
    if (item.kind === "video" && isDirectVideoUrl(item.url)) {
      return `<video class="resourcePreview" src="${url}" muted preload="metadata"></video>`;
    }
    return `<div class="resourcePreview icon">${escapeHtml(resourceType(item))}</div>`;
  }

  function resourcePlayerHtml(item) {
    const url = escapeHtml(item.url || "");
    if (item.kind === "image") {
      return `<div class="resourcePlayer"><img src="${url}" alt=""></div>`;
    }
    if (isPlayableVideoResource(item)) {
      return `<div class="resourcePlayer"><video src="${url}" controls autoplay muted playsinline preload="metadata"></video></div>`;
    }
    return `<div class="resourcePlayer icon">${escapeHtml(resourceType(item))}</div>`;
  }

  function jumpHtml() {
    return `
      <div class="subHead">
        <button class="back" data-view="home" type="button" aria-label="${TEXT.app}">&#8592;</button>
        <b>${TEXT.jumpTitle}</b>
      </div>
      <label class="toggle">
        <span>
          <b>${TEXT.compassLabel}</b>
          <small>${TEXT.compassDesc}</small>
        </span>
        <input type="checkbox" data-setting="compassJump"${state.settings.compassJump ? " checked" : ""}>
      </label>
    `;
  }

  function shopMoveHtml() {
    return `
      <div class="shopMovePage">
        <iframe class="shopMoveFrame" src="${escapeHtml(SHOP_MOVE_URL)}" title="${TEXT.copyMove}"></iframe>
      </div>
    `;
  }

  function toggleEcommerceToolPanel() {
    openEcommerceToolPanel();
  }

  async function openEcommerceToolPanel() {
    await refreshAuth(true);
    if (!hasEcommerceToolAccess()) {
      const reason = ecommerceToolDeniedReason();
      disableEcommerceToolRuntime();
      state.opened = true;
      state.view = "home";
      state.authMessage = reason;
      toast(reason, "error", 4200);
      syncUiMount();
      return;
    }
    if (!hasPremiumAccess()) {
      state.opened = true;
      openAuthGate("ecommerceTool");
      return;
    }
    window.postMessage({
      source: "mmw-floating-desktop",
      type: ECOMMERCE_TOOL_PANEL_MESSAGE
    }, "*");
    state.ecommerceToolPanelOpen = true;
    state.opened = false;
    state.view = "home";
    state.authMessage = "";
    syncUiMount();
  }

  function disableEcommerceToolRuntime() {
    state.ecommerceToolPanelOpen = false;
    chrome.storage.session.remove(ECOMMERCE_TOOL_RUNTIME_SESSION_KEY).catch(() => {});
    window.postMessage({
      source: "mmw-floating-desktop",
      type: ECOMMERCE_TOOL_PANEL_DISABLE_MESSAGE
    }, "*");
  }

  function closeEcommerceToolPanel() {
    state.ecommerceToolPanelOpen = false;
    syncEcommerceToolPanelPosition();
  }

  function syncEcommerceToolPanelPosition() {
    state.ecommerceToolPanelOpen = false;
  }

  function resolveEcommerceToolPanelPlacement() {
    const width = ECOMMERCE_TOOL_PANEL_WIDTH;
    const height = Math.min(ECOMMERCE_TOOL_PANEL_HEIGHT, Math.max(360, window.innerHeight - 24));
    const left = clamp(ECOMMERCE_TOOL_PANEL_LEFT, 12, Math.max(12, window.innerWidth - width - 12));
    const top = clamp(ECOMMERCE_TOOL_PANEL_TOP, 12, Math.max(12, window.innerHeight - height - 12));
    return {
      left: Math.round(left),
      top: Math.round(top),
      width,
      side: "right",
      fixedLeft: true
    };
  }

  function bindUi() {
    const launcher = shadow.querySelector(".launcher");
    launcher?.addEventListener("pointerdown", (event) => beginDrag(event, "launcher"));
    launcher?.addEventListener("click", () => {
      if (state.suppressLauncherClick) {
        state.suppressLauncherClick = false;
        return;
      }
      state.opened = !state.opened;
      if (state.opened) state.view = "home";
      if (!state.opened) {
        closeEcommerceToolPanel();
        state.authMessage = "";
      }
      syncUiMount();
      if (state.opened) refreshTutorialNavigation().catch(() => {});
      if (state.opened && state.auth.loggedIn) refreshAuth(true).catch(() => {});
    });

    shadow.querySelector("[data-drag-handle='desk']")?.addEventListener("pointerdown", (event) => {
      if (event.target.closest("button,input,label,video")) return;
      beginDrag(event, "desk");
    });

    shadow.querySelectorAll("[data-view]").forEach((button) => {
      button.addEventListener("click", () => {
        const nextView = button.dataset.view || "home";
        if (!ensureLegalConsent(nextView)) return;
        if (!hasViewAccess(nextView)) {
          openAuthGate(nextView);
          return;
        }
        if (nextView === "ecommerceTool") {
          toggleEcommerceToolPanel();
          return;
        }
        if (nextView !== "home") state.ecommerceToolPanelOpen = false;
        state.view = nextView;
        if (nextView === "home") state.authMessage = "";
        syncUiMount();
      });
    });

    shadow.querySelectorAll(".featureCard").forEach((card) => {
      card.addEventListener("pointermove", updateFeatureCardTilt);
      card.addEventListener("pointerleave", resetFeatureCardTilt);
      card.addEventListener("pointercancel", resetFeatureCardTilt);
    });

    shadow.querySelectorAll("[data-home-page]").forEach((button) => {
      button.addEventListener("click", () => {
        state.homePage = button.dataset.homePage === "qianchuan" ? "qianchuan" : "common";
        syncUiMount();
      });
    });

    shadow.querySelector('[data-action="open-invite-entry"]')?.addEventListener("click", openInviteEntry);
    shadow.querySelector('[data-action="open-qianchuan-agent"]')?.addEventListener("click", openQianchuanAgentEntry);
    shadow.querySelector('[data-action="open-qianchuan-launch"]')?.addEventListener("click", openQianchuanLaunchEntry);

    shadow.querySelector('[data-action="open-login"]')?.addEventListener("click", () => {
      state.opened = true;
      state.view = "auth";
      state.authMessage = "";
      syncUiMount();
    });

    shadow.querySelectorAll('[data-action="open-web-register"]').forEach((button) => {
      button.addEventListener("click", openWebRegistration);
    });

    shadow.querySelector('[data-auth-form="login"]')?.addEventListener("submit", loginSubmit);

    shadow.querySelector('[data-action="logout"]')?.addEventListener("click", logout);

    shadow.querySelectorAll("[data-filter]").forEach((button) => {
      button.addEventListener("click", () => {
        state.resourceFilter = button.dataset.filter || "all";
        render();
      });
    });

    shadow.querySelectorAll("[data-image-format]").forEach((button) => {
      button.addEventListener("click", () => {
        const nextFormat = button.dataset.imageFormat || "all";
        state.imageFilters.format = state.imageFilters.format === nextFormat ? "all" : nextFormat;
        render();
      });
    });

    shadow.querySelectorAll("[data-image-range]").forEach((input) => {
      input.addEventListener("input", () => {
        const axis = input.dataset.imageRange;
        const nextValue = Number(input.value) || 0;
        if (axis === "width") state.imageFilters.minWidth = nextValue;
        if (axis === "height") state.imageFilters.minHeight = nextValue;
        render();
      });
    });

    shadow.querySelectorAll("[data-preview-id]").forEach((node) => {
      node.addEventListener("click", () => {
        const id = node.dataset.previewId || "";
        toggleResourcePreview(id);
      });
    });

    shadow.querySelector('[data-action="close"]')?.addEventListener("click", () => {
      state.opened = false;
      state.authMessage = "";
      syncUiMount();
    });

    shadow.querySelector('[data-action="close-legal-window"]')?.addEventListener("click", () => {
      state.legalWindowOpen = false;
      state.legalCheck = false;
      state.legalRedirectView = "";
      state.legalRedirectAction = "";
      syncUiMount();
    });

    shadow.querySelector('[data-action="toggle-legal-check"]')?.addEventListener("change", (event) => {
      state.legalCheck = event.currentTarget.checked === true;
      syncUiMount();
    });

    shadow.querySelector('[data-action="accept-legal-consent"]')?.addEventListener("click", acceptLegalConsent);

    shadow.querySelector('[data-action="decline-legal-consent"]')?.addEventListener("click", () => {
      state.legalWindowOpen = false;
      state.legalCheck = false;
      state.legalRedirectView = "";
      state.legalRedirectAction = "";
      syncUiMount();
      toast(TEXT.legalRequired, "error");
    });

    shadow.querySelector('[data-action="close-feature-tab"]')?.addEventListener("click", () => {
      state.view = "home";
      syncUiMount();
    });

    shadow.querySelector('[data-action="scan"]')?.addEventListener("click", () => {
      if (!ensureLegalConsent(state.view, "scan")) return;
      refreshResources(true);
    });

    shadow.querySelector('[data-action="clear"]')?.addEventListener("click", async () => {
      if (!ensureLegalConsent(state.view, "clear")) return;
      state.selected.clear();
      await sendMessage({ type: "CLEAR_TAB_RESOURCES" });
      await refreshResources(true);
    });

    shadow.querySelector('[data-action="download"]')?.addEventListener("click", downloadSelected);

    shadow.querySelector('[data-action="download-titles"]')?.addEventListener("click", downloadSelectedTitles);

    shadow.querySelector('[data-action="download-source-batch"]')?.addEventListener("click", downloadSelectedSourceVideos);

    const toggleAllInput = shadow.querySelector('[data-action="toggle-all"]');
    if (toggleAllInput) {
      const visibleResources = getVisibleResources();
      const selectedCount = visibleResources.filter((item) => state.selected.has(item.id)).length;
      toggleAllInput.indeterminate = selectedCount > 0 && selectedCount < visibleResources.length;
      toggleAllInput.addEventListener("change", () => {
        visibleResources.forEach((item) => {
          if (toggleAllInput.checked) state.selected.add(item.id);
          else state.selected.delete(item.id);
        });
        syncSelectionControls();
      });
    }

    shadow.querySelectorAll("[data-resource-download]").forEach((button) => {
      button.addEventListener("click", () => {
        if (!ensureLegalConsent(state.view, `downloadResource:${button.dataset.resourceDownload || ""}`)) return;
        const item = findResource(button.dataset.resourceDownload);
        if (item) downloadResources([item]);
      });
    });

    shadow.querySelectorAll("[data-resource-open]").forEach((button) => {
      button.addEventListener("click", () => {
        const item = findResource(button.dataset.resourceOpen);
        if (item?.url) window.open(item.url, "_blank", "noopener");
      });
    });

    shadow.querySelectorAll("[data-image-thumb]").forEach((image) => {
      image.addEventListener("load", () => {
        const item = findResource(image.dataset.imageThumb || "");
        if (!item) return;
        const width = normalizeDimensionValue(image.naturalWidth);
        const height = normalizeDimensionValue(image.naturalHeight);
        if (!width || !height) return;
        if (width === (Number(item.width) || 0) && height === (Number(item.height) || 0)) return;
        item.width = width;
        item.height = height;
        syncImageFilters();
        render();
      }, { once: true });
      image.addEventListener("error", () => {
        image.closest(".imageCardPreview")?.classList?.add("is-error");
      }, { once: true });
    });

    shadow.querySelector('[data-action="open-selected-images"]')?.addEventListener("click", async () => {
      if (!ensureLegalConsent(state.view, "openSelectedImages")) return;
      await openSelectedImages();
    });

    shadow.querySelectorAll("[data-open-link]").forEach((button) => {
      button.addEventListener("click", () => {
        const url = button.dataset.openLink || "";
        if (url) window.open(url, "_blank", "noopener");
      });
    });

    shadow.querySelectorAll("[data-resource-id]").forEach((input) => {
      input.addEventListener("change", () => {
        if (input.checked) state.selected.add(input.dataset.resourceId);
        else state.selected.delete(input.dataset.resourceId);
        syncSelectionControls();
      });
    });

    shadow.querySelectorAll("[data-setting]").forEach((input) => {
      input.addEventListener("change", async () => {
        if (!ensureLegalConsent(state.view)) {
          input.checked = !input.checked;
          return;
        }
        const key = input.dataset.setting;
        if (key === "compassJump" && input.checked) {
          await refreshAuth(false);
          if (!hasPremiumAccess()) {
            input.checked = false;
            state.settings.compassJump = false;
            await chrome.storage.local.set({ [STORAGE_KEYS.compassJump]: false }).catch(() => {});
            render();
            return;
          }
        }
        state.settings[key] = input.checked;
        await chrome.storage.local.set({ [STORAGE_KEYS[key]]: input.checked });
        render();
      });
    });
  }

  async function openSelectedImages() {
    const urls = getVisibleResources()
      .filter((item) => state.selected.has(item.id))
      .map((item) => item.url)
      .filter(Boolean);
    if (!urls.length) return;
    const response = await sendMessage({ type: "VD_OPEN_URLS", urls });
    if (!response?.ok) toast(response?.error || TEXT.openSelectorFail);
  }

  function syncSelectionControls() {
    const visibleResources = getVisibleResources();
    const selectedCount = visibleResources.filter((item) => state.selected.has(item.id)).length;
    const allSelected = Boolean(visibleResources.length && selectedCount === visibleResources.length);
    const toggleAllInput = shadow.querySelector('[data-action="toggle-all"]');
    if (toggleAllInput) {
      toggleAllInput.checked = allSelected;
      toggleAllInput.indeterminate = selectedCount > 0 && selectedCount < visibleResources.length;
      toggleAllInput.disabled = !visibleResources.length;
      toggleAllInput.closest(".selectAll")?.classList.toggle("disabled", !visibleResources.length);
    }
    shadow.querySelectorAll("[data-resource-id]").forEach((input) => {
      const id = input.dataset.resourceId || "";
      input.checked = state.selected.has(id);
    });
    const downloadButton = shadow.querySelector('[data-action="download"]');
    if (downloadButton) downloadButton.disabled = selectedCount === 0;
    const titleDownloadButton = shadow.querySelector('[data-action="download-titles"]');
    if (titleDownloadButton) titleDownloadButton.disabled = selectedCount === 0;
    const sourceButton = shadow.querySelector('[data-action="download-source-batch"]');
    if (sourceButton) {
      const selectedSourceCount = visibleResources.filter((item) => state.selected.has(item.id) && getSourceTraceUrl(item)).length;
      sourceButton.disabled = selectedSourceCount === 0;
    }
    const openSelectedImagesButton = shadow.querySelector('[data-action="open-selected-images"]');
    if (openSelectedImagesButton) openSelectedImagesButton.disabled = selectedCount === 0;
    const countNode = shadow.querySelector(".count");
    if (countNode) countNode.textContent = `${TEXT.selected} ${selectedCount} / ${visibleResources.length}`;
    const imageMetaNode = shadow.querySelector(".imageActionMeta");
    if (imageMetaNode) imageMetaNode.textContent = `${selectedCount}/${visibleResources.length}`;
  }

  function toggleResourcePreview(id) {
    const targetId = String(id || "");
    if (!targetId) return;
    const list = shadow.querySelector(".resourceList");
    const scrollTop = list ? list.scrollTop : 0;
    const previousId = state.previewId;
    const nextId = previousId === targetId ? "" : targetId;
    if (previousId) setResourcePreviewExpanded(previousId, false);
    state.previewId = nextId;
    if (nextId) setResourcePreviewExpanded(nextId, true);
    if (list) list.scrollTop = scrollTop;
  }

  function setResourcePreviewExpanded(id, expanded) {
    const resource = findPreviewResourceElement(id);
    if (!resource) return;
    resource.classList.toggle("expanded", expanded);
    Array.from(resource.children)
      .filter((child) => child.classList?.contains("resourcePlayer"))
      .forEach((child) => child.remove());
    if (!expanded) return;
    const item = findResource(id);
    if (!item) return;
    const template = document.createElement("template");
    template.innerHTML = resourcePlayerHtml(item);
    const player = template.content.firstElementChild;
    if (player) resource.append(player);
  }

  function findPreviewResourceElement(id) {
    const targetId = String(id || "");
    const trigger = Array.from(shadow.querySelectorAll("[data-preview-id]"))
      .find((node) => node.dataset.previewId === targetId);
    return trigger?.closest(".resource") || null;
  }

  async function loginSubmit(event) {
    event.preventDefault();
    const form = event.currentTarget;
    const username = form?.elements?.username?.value?.trim() || "";
    const password = form?.elements?.password?.value || "";
    state.loginDraft = { username, password };
    if (!username || !password) {
      state.authMessage = TEXT.loginMissing;
      syncUiMount();
      toast(TEXT.loginMissing, "error");
      return;
    }
    state.authLoading = true;
    state.authMessage = TEXT.loginConnecting;
    syncUiMount();
    const response = await sendMessage({ type: "PLUGIN_AUTH_LOGIN", username, password });
    state.authLoading = false;
    if (!response?.ok) {
      state.view = "auth";
      state.authMessage = response?.error || "\u767b\u5f55\u5931\u8d25";
      syncUiMount();
      toast(state.authMessage, "error");
      return;
    }
    state.auth = normalizeAuth(response.auth);
    state.authMessage = "";
    state.loginDraft = { username: "", password: "" };
    await refreshResources(false);
    state.view = resolvePostAuthView();
    syncUiMount();
    toast(state.view === "auth" ? (state.authMessage || authGateMessage()) : (response?.message || TEXT.loginOk));
  }

  async function openWebRegistration() {
    const response = await sendMessage({ type: "VD_OPEN_URLS", urls: [SUPPORT_REGISTER_URL] });
    if (!response?.ok) {
      state.authMessage = response?.error || TEXT.registerPageOpenFail;
      syncUiMount();
      toast(state.authMessage, "error");
      return;
    }
    state.authMessage = "";
    syncUiMount();
    toast(TEXT.registerPageOpened);
  }

  async function logout() {
    const response = await sendMessage({ type: "PLUGIN_AUTH_LOGOUT" });
    if (response?.ok) state.auth = normalizeAuth(response.auth);
    state.view = "home";
    state.authMessage = "";
    state.loginDraft = { username: "", password: "" };
    state.authRedirectView = "";
    state.authRedirectAction = "";
    state.resources = [];
    state.selected.clear();
    syncUiMount();
    toast(TEXT.logoutOk);
  }

  async function openInviteEntry() {
    if (!ensureLegalConsent("home", "openInvite")) return;
    if (!hasFeatureAccess()) {
      state.opened = true;
      openAuthGate("home", "openInvite");
      return;
    }

    try {
      if (isDarenSquarePage()) {
        window.postMessage({ action: "trigger-find-talent-drawer" }, "*");
        toast("已打开达人邀约");
        return;
      }

      const response = await chrome.runtime.sendMessage({
        name: "newPageTask",
        body: {
          action: "create-task",
          name: "open-daren-invite",
          openUrl: DAREN_SQUARE_URL,
          data: { open: true }
        }
      });

      if (!response?.ok) throw new Error(response?.error || TEXT.inviteOpenFail);
      state.opened = false;
      syncUiMount();
    } catch (error) {
      toast(error?.message || TEXT.inviteOpenFail, "error");
    }
  }

  async function openQianchuanLaunchEntry() {
    if (!ensureLegalConsent("home", "openQianchuanLaunch")) return;
    if (!ensureLoginAccess("home", "openQianchuanLaunch")) return;

    try {
      if (location.hostname === "qianchuan.jinritemai.com") {
        window.dispatchEvent(new CustomEvent("MMW_QIANCHUAN_LAUNCH_OPEN"));
        state.opened = false;
        syncUiMount();
        toast("已打开批量计划");
        return;
      }

      const response = await chrome.runtime.sendMessage({
        name: "newPageTask",
        body: {
          action: "create-task",
          name: "open-qianchuan-launch",
          openUrl: QIANCHUAN_LAUNCH_URL,
          data: { openQianchuanLaunch: true }
        }
      });

      if (!response?.ok) window.open(QIANCHUAN_LAUNCH_URL, "_blank", "noopener");
      state.opened = false;
      syncUiMount();
    } catch (error) {
      window.open(QIANCHUAN_LAUNCH_URL, "_blank", "noopener");
      state.opened = false;
      syncUiMount();
    }
  }

  async function openQianchuanAgentEntry() {
    if (!ensureLegalConsent("home", "openQianchuanAgent")) return;
    if (!ensureLoginAccess("home", "openQianchuanAgent")) return;
    if (!ensureFeatureAccess()) return;

    try {
      if (location.hostname === "qianchuan.jinritemai.com") {
        if (!document.getElementById("mmw-qianchuan-agent-root")) {
          toast("千川 Agent 尚未加载，请刷新当前千川页面后重试", "info", 4800);
          return;
        }
        window.dispatchEvent(new CustomEvent(QIANCHUAN_AGENT_PANEL_EVENT, {
          detail: { source: "mmw-floating-desktop", open: true }
        }));
        state.opened = false;
        syncUiMount();
        return;
      }

      const response = await chrome.runtime.sendMessage({
        name: "newPageTask",
        body: {
          action: "create-task",
          name: "open-qianchuan-agent",
          openUrl: QIANCHUAN_AGENT_URL,
          data: { openQianchuanAgent: true }
        }
      });

      if (!response?.ok) window.open(QIANCHUAN_AGENT_URL, "_blank", "noopener");
      state.opened = false;
      syncUiMount();
    } catch {
      window.open(QIANCHUAN_AGENT_URL, "_blank", "noopener");
      state.opened = false;
      syncUiMount();
    }
  }

  function isDarenSquarePage() {
    return location.hostname === "buyin.jinritemai.com"
      && location.pathname.startsWith("/dashboard/servicehall/daren-square");
  }

  function ensureLoginAccess(targetView = state.view, targetAction = "") {
    if (hasLoginAccess()) return true;
    state.opened = true;
    openAuthGate(targetView, targetAction);
    return false;
  }

  function ensureFeatureAccess() {
    if (hasPremiumAccess()) return true;
    state.opened = true;
    openAuthGate(state.view);
    return false;
  }

  function hasFeatureAccess() {
    return hasPremiumAccess();
  }

  function hasLoginAccess() {
    return Boolean(state.auth?.loggedIn);
  }

  function hasSourceTraceAccess() {
    return globalThis.MMWPluginFeatureAccess?.isVisible?.(
      state.auth,
      globalThis.MMWPluginFeatureAccess.SOURCE_TRACE
    ) === true;
  }

  function sourceTraceDeniedReason() {
    return globalThis.MMWPluginFeatureAccess?.deniedReason?.(
      state.auth,
      globalThis.MMWPluginFeatureAccess.SOURCE_TRACE
    ) || "当前账号未开通视频溯源。";
  }

  function hasEcommerceToolAccess() {
    return globalThis.MMWPluginFeatureAccess?.isVisible?.(
      state.auth,
      globalThis.MMWPluginFeatureAccess.ECOMMERCE_TOOL
    ) === true;
  }

  function ecommerceToolDeniedReason() {
    return globalThis.MMWPluginFeatureAccess?.deniedReason?.(
      state.auth,
      globalThis.MMWPluginFeatureAccess.ECOMMERCE_TOOL
    ) || "当前账号未开通电商工具。";
  }

  function hasPremiumAccess() {
    return Boolean(state.auth?.loggedIn && state.auth?.active);
  }

  function hasAdminAccess() {
    if (!hasLoginAccess()) return false;
    const account = state.auth?.account || {};
    const user = state.auth?.user || {};
    const userType = String(account.user_type || user.user_type || account.role || user.role || "").toLowerCase();
    return Boolean(
      isTruthyFlag(account.is_admin)
      || isTruthyFlag(user.is_admin)
      || isTruthyFlag(account.admin)
      || isTruthyFlag(user.admin)
      || isTruthyFlag(account.is_staff)
      || isTruthyFlag(user.is_staff)
      || isTruthyFlag(account.staff)
      || isTruthyFlag(user.staff)
      || isTruthyFlag(account.is_superuser)
      || isTruthyFlag(user.is_superuser)
      || isTruthyFlag(account.superuser)
      || isTruthyFlag(user.superuser)
      || isTruthyFlag(account.is_points_admin)
      || isTruthyFlag(user.is_points_admin)
      || isTruthyFlag(account.points_admin)
      || isTruthyFlag(user.points_admin)
      || userType === "admin"
      || userType === "administrator"
      || userType.includes("\u7ba1\u7406\u5458")
    );
  }

  function hasQianchuanAgentAccess() {
    return hasPremiumAccess();
  }

  function isTruthyFlag(value) {
    if (value === true || value === 1) return true;
    return /^(1|true|yes|on)$/i.test(String(value || "").trim());
  }

  function hasViewAccess(view) {
    if (isAdminProtectedView(view)) return hasAdminAccess();
    if (isProtectedView(view)) return hasPremiumAccess();
    if (isLoginProtectedView(view)) return hasLoginAccess();
    return true;
  }

  function isAuthProtectedView(view) {
    return isAdminProtectedView(view) || isProtectedView(view) || isLoginProtectedView(view);
  }

  function authGateMessage(targetView = state.view) {
    if (!state.auth?.loggedIn) return TEXT.loginRequired;
    if (isAdminProtectedView(targetView) && !hasAdminAccess()) return TEXT.adminRequired;
    return state.auth?.message || state.auth?.entitlement?.label || TEXT.expired;
  }

  function normalizeAuth(value) {
    const auth = value && typeof value === "object" ? value : {};
    return {
      loggedIn: Boolean(auth.loggedIn),
      active: Boolean(auth.active),
      user: auth.user && typeof auth.user === "object" ? auth.user : {},
      account: auth.account && typeof auth.account === "object" ? auth.account : {},
      entitlement: auth.entitlement && typeof auth.entitlement === "object" ? auth.entitlement : { active: false, label: TEXT.loginRequired },
      message: String(auth.message || auth.entitlement?.label || "")
    };
  }

  function normalizePluginUpdateInfo(value) {
    const info = value && typeof value === "object" ? value : {};
    return {
      checkedAt: Number(info.checkedAt || 0),
      currentVersion: String(info.currentVersion || chrome.runtime.getManifest().version || "").trim(),
      latestVersion: String(info.latestVersion || info.version || "").trim(),
      downloadUrl: String(info.downloadUrl || info.download_url || "").trim(),
      hasUpdate: Boolean(info.hasUpdate)
    };
  }

  function authUsername() {
    return String(state.auth?.account?.username || state.auth?.user?.username || TEXT.username).trim();
  }

  function authStatusText() {
    const account = state.auth?.account || {};
    const plan = String(account.plan_name || account.membership_name || "").trim();
    const status = String(account.member_status || account.status || "").trim();
    const expire = String(account.expires_at || account.expire_at || "").trim();
    if (!state.auth?.active) return authGateMessage();
    if (expire) return `${plan || status || "\u4f1a\u5458"} \u00b7 ${expire}`;
    return plan || status || state.auth?.entitlement?.label || "\u8d26\u53f7\u4f1a\u5458\u6709\u6548";
  }

  function pointsBalanceInfo() {
    const account = state.auth?.account || {};
    const user = state.auth?.user || {};
    const keys = [
      "points_balance",
      "point_balance",
      "points",
      "point",
      "credits",
      "credit",
      "balance",
      "score",
      "integral",
      "remaining_points"
    ];
    for (const source of [account, user]) {
      for (const key of keys) {
        if (source?.[key] === undefined || source?.[key] === null || source?.[key] === "") continue;
        const value = normalizePointNumber(source[key]);
        if (Number.isFinite(value)) {
          return {
            available: true,
            value,
            text: `${formatPointNumber(value)}${TEXT.pointsUnit}`
          };
        }
      }
    }
    return { available: false, value: 0, text: "" };
  }

  function ensureSourcePointsBalance(cost) {
    const balance = pointsBalanceInfo();
    if (!balance.available) return true;
    if (balance.value >= cost) return true;
    toast(`${TEXT.pointsInsufficient}\uff1a${TEXT.pointsNeedPrefix}${formatPointNumber(cost)}${TEXT.pointsUnit}\uff0c${TEXT.pointsCurrentPrefix}${formatPointNumber(balance.value)}${TEXT.pointsUnit}`, "error");
    return false;
  }

  function normalizePointNumber(value) {
    if (typeof value === "number") return value;
    const text = String(value || "").replace(/,/g, "").trim();
    const match = text.match(/-?\d+(?:\.\d+)?/);
    return match ? Number(match[0]) : Number.NaN;
  }

  function formatPointNumber(value) {
    const number = Number(value);
    if (!Number.isFinite(number)) return "0";
    return Number.isInteger(number) ? String(number) : String(Math.round(number * 100) / 100);
  }

  function normalizeLegalConsent(values) {
    const accepted = values?.[STORAGE_KEYS.legalConsentAccepted] === true
      && values?.[STORAGE_KEYS.legalConsentVersion] === LEGAL_CONSENT_VERSION;
    return {
      accepted,
      version: String(values?.[STORAGE_KEYS.legalConsentVersion] || ""),
      acceptedAt: Number(values?.[STORAGE_KEYS.legalConsentAcceptedAt] || 0)
    };
  }

  function hasLegalConsent() {
    return state.legalConsent?.accepted === true;
  }

  function requiresLegalConsent(view) {
    return view === "video" || view === "image" || view === "jump";
  }

  function ensureLegalConsent(targetView = state.view, targetAction = "") {
    if (hasLegalConsent()) return true;
    const view = String(targetView || "");
    const action = String(targetAction || "");
    if (!requiresLegalConsent(view) && !action) return true;
    state.opened = true;
    openLegalWindow(view, action);
    toast(TEXT.legalRequired, "error");
    return false;
  }

  function openLegalWindow(targetView = "", targetAction = "") {
    state.legalWindowOpen = true;
    state.legalCheck = false;
    state.legalRedirectView = requiresLegalConsent(targetView) ? String(targetView) : "";
    state.legalRedirectAction = String(targetAction || "");
    syncUiMount();
    loadLegalAgreement();
  }

  async function runLegalRedirectAction(action) {
    const value = String(action || "");
    if (!value) return false;
    if (value === "openInvite") {
      await openInviteEntry();
      return true;
    }
    if (value === "openQianchuanAgent") {
      await openQianchuanAgentEntry();
      return true;
    }
    if (value === "openQianchuanLaunch") {
      await openQianchuanLaunchEntry();
      return true;
    }
    if (value === "scan") {
      await refreshResources(true);
      return true;
    }
    if (value === "clear") {
      state.selected.clear();
      await sendMessage({ type: "CLEAR_TAB_RESOURCES" });
      await refreshResources(true);
      syncUiMount();
      return true;
    }
    if (value === "download") {
      await downloadSelected();
      return true;
    }
    if (value === "downloadTitles") {
      await downloadSelectedTitles();
      return true;
    }
    if (value === "downloadSourceBatch") {
      await downloadSelectedSourceVideos();
      return true;
    }
    if (value === "openSelectedImages") {
      await openSelectedImages();
      return true;
    }
    if (value.startsWith("downloadResource:")) {
      const id = value.slice("downloadResource:".length);
      const item = findResource(id);
      if (item) await downloadResources([item]);
      return true;
    }
    return false;
  }

  async function acceptLegalConsent() {
    if (!state.legalCheck) {
      toast(TEXT.legalRequired, "error");
      return;
    }
    const acceptedAt = Date.now();
    const redirectView = state.legalRedirectView;
    const redirectAction = state.legalRedirectAction;
    try {
      await chrome.storage.local.set({
        [STORAGE_KEYS.legalConsentAccepted]: true,
        [STORAGE_KEYS.legalConsentVersion]: LEGAL_CONSENT_VERSION,
        [STORAGE_KEYS.legalConsentAcceptedAt]: acceptedAt
      });
      await refreshLegalConsent();
      if (!hasLegalConsent()) throw new Error("legal consent storage verification failed");
    } catch {
      state.legalWindowOpen = true;
      syncUiMount();
      toast("\u534f\u8bae\u72b6\u6001\u4fdd\u5b58\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5", "error");
      return;
    }
    state.legalWindowOpen = false;
    state.legalCheck = false;
    state.legalRedirectView = "";
    state.legalRedirectAction = "";
    if (redirectAction) {
      syncUiMount();
      toast(TEXT.legalAccepted, "success");
      window.setTimeout(() => {
        runLegalRedirectAction(redirectAction).catch(() => {});
      }, 0);
      return;
    }
    if (requiresLegalConsent(redirectView)) {
      state.opened = true;
      if (!hasViewAccess(redirectView)) {
        openAuthGate(redirectView);
        return;
      }
      state.view = redirectView;
    }
    syncUiMount();
    toast(TEXT.legalAccepted, "success");
  }

  async function loadLegalAgreement() {
    if (state.legalAgreementHtml || state.legalAgreementLoading) return;
    state.legalAgreementLoading = true;
    state.legalAgreementError = "";
    try {
      const response = await fetch(chrome.runtime.getURL(LEGAL_AGREEMENT_PATH));
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      state.legalAgreementHtml = await response.text();
    } catch {
      state.legalAgreementError = TEXT.legalLoadFail;
    } finally {
      state.legalAgreementLoading = false;
      syncUiMount();
    }
  }

  function isProtectedView(view) {
    return view === "jump" || view === "ecommerceTool";
  }

  function isAdminProtectedView(view) {
    return view === "shopMove";
  }

  function isLoginProtectedView(view) {
    return view === "video" || view === "image";
  }

  function openAuthGate(targetView, targetAction = "") {
    state.view = "auth";
    state.authRedirectView = isAuthProtectedView(targetView) ? targetView : "";
    state.authRedirectAction = String(targetAction || "");
    state.authMessage = authGateMessage(targetView);
    syncUiMount();
    toast(authGateMessage(targetView), "error");
  }

  function resolvePostAuthView() {
    const action = state.authRedirectAction;
    const redirectView = isAuthProtectedView(state.authRedirectView) ? state.authRedirectView : "";
    if (action === "openInvite") {
      if (!hasPremiumAccess()) {
        state.view = "auth";
        state.authMessage = authGateMessage();
        state.authRedirectView = "";
        state.authRedirectAction = "";
        return "auth";
      }
      state.authRedirectView = "";
      state.authRedirectAction = "";
      state.authMessage = "";
      window.setTimeout(() => openInviteEntry(), 0);
      return "home";
    }
    if (action === "openQianchuanAgent" || action === "openQianchuanLaunch") {
      if (!hasLoginAccess()) {
        state.view = "auth";
        state.authMessage = authGateMessage();
        state.authRedirectView = "";
        state.authRedirectAction = "";
        return "auth";
      }
      state.authRedirectView = "";
      state.authRedirectAction = "";
      state.authMessage = "";
      window.setTimeout(() => {
        if (action === "openQianchuanAgent") void openQianchuanAgentEntry();
        else void openQianchuanLaunchEntry();
      }, 0);
      return "home";
    }
    if (isAdminProtectedView(redirectView) && !hasAdminAccess()) {
      state.view = "auth";
      state.authMessage = authGateMessage(redirectView);
      state.authRedirectView = "";
      state.authRedirectAction = "";
      return "auth";
    }
    if (isProtectedView(redirectView) && !hasPremiumAccess()) {
      state.view = "auth";
      state.authMessage = authGateMessage();
      state.authRedirectView = "";
      state.authRedirectAction = "";
      return "auth";
    }
    if (isLoginProtectedView(redirectView) && !hasLoginAccess()) {
      state.view = "auth";
      state.authMessage = authGateMessage();
      state.authRedirectView = "";
      state.authRedirectAction = "";
      return "auth";
    }
    if (redirectView === "ecommerceTool") {
      state.authRedirectView = "";
      state.authRedirectAction = "";
      state.authMessage = "";
      window.setTimeout(() => openEcommerceToolPanel(), 0);
      return "home";
    }
    const nextView = redirectView || "home";
    state.authRedirectView = "";
    state.authRedirectAction = "";
    state.authMessage = "";
    return nextView;
  }

  function beginDrag(event, source) {
    if (event.button !== 0) return;
    state.drag = {
      source,
      pointerId: event.pointerId,
      lastX: event.clientX,
      lastY: event.clientY,
      moved: false
    };
    event.preventDefault();
    document.body.style.userSelect = "none";
    window.addEventListener("pointermove", onPointerMove, true);
    window.addEventListener("pointerup", onPointerEnd, true);
    window.addEventListener("pointercancel", onPointerEnd, true);
  }

  function onPointerMove(event) {
    if (!state.drag || event.pointerId !== state.drag.pointerId) return;
    const dx = event.clientX - state.drag.lastX;
    const dy = event.clientY - state.drag.lastY;
    if (dx === 0 && dy === 0) return;

    state.drag.lastX = event.clientX;
    state.drag.lastY = event.clientY;
    state.drag.moved = true;
    state.dock.hidden = false;
    state.dock.x += dx;
    state.dock.y += dy;
    state.dock.side = state.dock.x + LAUNCHER_SIZE / 2 >= window.innerWidth / 2 ? "right" : "left";
    state.dock = normalizeDock(state.dock);
    render();
    syncEcommerceToolPanelPosition();
  }

  function onPointerEnd(event) {
    if (!state.drag || event.pointerId !== state.drag.pointerId) return;
    const draggedFromLauncher = state.drag.source === "launcher" && state.drag.moved;
    const clickedLauncher = state.drag.source === "launcher" && !state.drag.moved;
    const moved = state.drag.moved;

    window.removeEventListener("pointermove", onPointerMove, true);
    window.removeEventListener("pointerup", onPointerEnd, true);
    window.removeEventListener("pointercancel", onPointerEnd, true);
    document.body.style.userSelect = "";

    state.drag = null;
    if (moved) {
      state.dock = snapDock(state.dock);
      persistDock();
    }
    if (clickedLauncher) {
      state.opened = !state.opened;
      if (state.opened) state.view = "home";
      if (!state.opened) {
        closeEcommerceToolPanel();
        state.authMessage = "";
      }
      if (state.opened && state.auth.loggedIn) refreshAuth(true).catch(() => {});
      state.suppressLauncherClick = true;
      setTimeout(() => { state.suppressLauncherClick = false; }, 0);
    }
    if (draggedFromLauncher) {
      state.suppressLauncherClick = true;
      setTimeout(() => { state.suppressLauncherClick = false; }, 0);
    }
    syncUiMount();
  }

  async function downloadSelected() {
    if (!ensureLegalConsent(state.view, "download")) return;
    if (!ensureLoginAccess(state.view)) return;
    const items = getVisibleResources().filter((item) => state.selected.has(item.id));
    if (!items.length) return;
    await downloadResources(items);
  }

  async function downloadSelectedTitles() {
    if (!ensureLegalConsent(state.view, "downloadTitles")) return;
    if (!ensureLoginAccess(state.view)) return;
    const selectedItems = getVisibleResources().filter((item) => state.selected.has(item.id));
    if (!selectedItems.length) return;
    const items = selectedItems.filter((item) => normalizeVideoTitle(item.videoTitle));
    if (!items.length) {
      toast(TEXT.titleDownloadEmpty, "error");
      return;
    }

    toast(TEXT.titleDownloadSubmitting, "info", 4000);
    let downloaded = 0;
    let failed = 0;
    for (const item of items) {
      const response = await sendMessage({
        type: "DOWNLOAD_VIDEO_TITLE",
        title: item.videoTitle,
        awemeId: item.awemeId || extractAwemeId(getSourceTraceUrl(item)),
        sourceTraceUrl: getSourceTraceUrl(item)
      });
      if (response?.ok) downloaded += 1;
      else failed += 1;
    }

    const skipped = selectedItems.length - items.length;
    if (!downloaded) {
      toast(TEXT.titleDownloadEmpty, "error", 4200);
      return;
    }
    const suffix = failed || skipped ? `，跳过 ${failed + skipped} 个` : "";
    toast(`${TEXT.titleDownloadDone} ${downloaded} ${TEXT.titleDownloadUnit}${suffix}`, "success", 4200);
  }

  async function downloadSelectedSourceVideos() {
    if (!ensureLegalConsent(state.view, "downloadSourceBatch")) return;
    if (!ensureLoginAccess(state.view)) return;
    await refreshAuth(true);
    if (!hasSourceTraceAccess()) {
      toast(sourceTraceDeniedReason(), "error", 4200);
      return;
    }
    const selectedItems = getVisibleResources().filter((item) => state.selected.has(item.id));
    const items = selectedItems.map(buildSourceTraceBatchItem).filter(Boolean);
    if (!items.length) {
      toast(TEXT.sourceMissing, "error");
      return;
    }
    const totalCost = items.length * 10;
    if (!ensureSourcePointsBalance(totalCost)) return;
    if (selectedItems.length !== items.length) {
      toast(`${TEXT.sourceSelected}${selectedItems.length}\u4e2a\uff0c${TEXT.sourceMatched}${items.length}\u4e2a\u94fe\u63a5`);
    }
    const confirmed = await confirmSourceBatchCost(items.length, totalCost);
    if (!confirmed) return;
    toast(TEXT.sourceBatchSubmitting, "info", 4000);
    const response = await sendMessage({
      type: "SOURCE_TRACE_BATCH_DOWNLOAD",
      items
    });
    const results = Array.isArray(response?.results) ? response.results : [];
    const okCount = results.filter((item) => item.ok).length;
    const failed = results.find((item) => !item.ok && item.error);
    if (!response?.ok) {
      toast(failed?.error || response?.error || TEXT.downloadFail, "error", 4200);
      return;
    }
    toast(`${TEXT.sourceBatchDone}${okCount || items.length}${TEXT.selectedSuffix}${TEXT.downloadSubmittedHint}`, "success", 4200);
  }

  async function confirmSourceBatchCost(count, cost) {
    if (await readSourceCostConfirmHidden()) return true;
    const message = `\u83b7\u53d6${count}\u6761\u6e90\u89c6\u9891\uff0c\u9700\u8981\u6d88\u8017${cost}\u79ef\u5206\uff0c\u662f\u5426\u83b7\u53d6\u4e0b\u8f7d\uff1f`;
    return showSourceCostDialog(message);
  }

  function readSourceCostConfirmHidden() {
    return chrome.storage.local
      .get([STORAGE_KEYS.sourceCostConfirmHidden])
      .then((data) => Boolean(data?.[STORAGE_KEYS.sourceCostConfirmHidden]))
      .catch(() => false);
  }

  function writeSourceCostConfirmHidden(value) {
    return chrome.storage.local
      .set({ [STORAGE_KEYS.sourceCostConfirmHidden]: Boolean(value) })
      .catch(() => {});
  }

  function showSourceCostDialog(message) {
    if (sourceConfirmPromise) return sourceConfirmPromise;
    createRoot();
    sourceConfirmPromise = new Promise((resolve) => {
      const mask = document.createElement("div");
      mask.className = "sourceConfirmMask";
      mask.innerHTML = `
        <div class="sourceConfirmDialog" role="dialog" aria-modal="true">
          <div class="sourceConfirmMessage">${escapeHtml(message)}</div>
          <label class="sourceConfirmCheck">
            <input type="checkbox">
            <span>${TEXT.dontRemind}</span>
          </label>
          <div class="sourceConfirmActions">
            <button type="button" class="sourceConfirmCancel">${TEXT.cancel}</button>
            <button type="button" class="sourceConfirmOk">${TEXT.continueFetch}</button>
          </div>
        </div>
      `;
      const checkbox = mask.querySelector('input[type="checkbox"]');
      const cancelButton = mask.querySelector(".sourceConfirmCancel");
      const okButton = mask.querySelector(".sourceConfirmOk");
      let settled = false;
      const cleanup = (value) => {
        if (settled) return;
        settled = true;
        document.removeEventListener("keydown", onKeydown, true);
        mask.classList.remove("is-show");
        setTimeout(() => mask.remove(), 160);
        sourceConfirmPromise = null;
        resolve(value);
      };
      const onKeydown = (event) => {
        if (event.key === "Escape") cleanup(false);
      };
      cancelButton?.addEventListener("click", () => cleanup(false));
      okButton?.addEventListener("click", async () => {
        okButton.disabled = true;
        if (checkbox?.checked) await writeSourceCostConfirmHidden(true);
        cleanup(true);
      });
      mask.addEventListener("click", (event) => {
        if (event.target === mask) cleanup(false);
      });
      document.addEventListener("keydown", onKeydown, true);
      shadow.append(mask);
      setTimeout(() => mask.classList.add("is-show"), 0);
      setTimeout(() => okButton?.focus?.(), 30);
    });
    return sourceConfirmPromise;
  }

  async function downloadResources(items) {
    const candidates = items.filter(isTrustedDownloadResource);
    if (!candidates.length) {
      toast("\u672a\u627e\u5230\u53ef\u4e0b\u8f7d\u7684\u89c6\u9891\u8d44\u6e90");
      return;
    }
    const response = await sendMessage({ type: "FLOATING_DOWNLOAD_SELECTED", items: candidates });
    const results = Array.isArray(response.results) ? response.results : [];
    const failedItems = results.filter((item) => !item.ok);
    const okCount = results.filter((item) => item.ok).length;
    if (!response.ok) {
      toast(firstDownloadError(response, failedItems) || TEXT.downloadFail);
      return;
    }
    toast(`${TEXT.selectedPrefix}${okCount || candidates.length}${TEXT.selectedSuffix}`);
  }

  function firstDownloadError(response, failedItems) {
    if (response?.error) return response.error;
    const error = failedItems.find((item) => item.error)?.error;
    return error ? `下载失败：${error}` : "";
  }

  async function downloadResources(items) {
    const attempts = items
      .map((item) => listDownloadCandidates(item))
      .filter((candidateItems) => candidateItems.length);
    if (!attempts.length) {
      toast("\u672a\u627e\u5230\u53ef\u4e0b\u8f7d\u7684\u89c6\u9891\u8d44\u6e90", "error");
      return;
    }
    toast(TEXT.downloadSubmitting, "info", 4000);
    const response = await sendMessage({
      type: "FLOATING_DOWNLOAD_SELECTED",
      groups: attempts
    });
    const results = Array.isArray(response?.results) ? response.results : [];
    const okCount = results.filter((item) => item?.ok).length;
    if (!okCount) {
      toast(firstDownloadError(response) || TEXT.downloadFail, "error", 4200);
      return;
    }
    toast(`${TEXT.selectedPrefix}${okCount}${TEXT.selectedSuffix}${TEXT.downloadSubmittedHint}`, "success", 4200);
  }

  function firstDownloadError(response, failedItems = []) {
    if (response?.error) return response.error;
    const error = failedItems.find((item) => item.error)?.error;
    if (error) return `\u4e0b\u8f7d\u5931\u8d25\uff1a${error}`;
    const results = Array.isArray(response?.results) ? response.results : [];
    const resultError = results.find((item) => !item.ok && item.error)?.error;
    return resultError ? `\u4e0b\u8f7d\u5931\u8d25\uff1a${resultError}` : "";
  }

  function toast(text, type = "info", lifetime = TOAST_LIFETIME) {
    const old = shadow.querySelector(".toast");
    old?.remove();
    const deskRect = getDeskRect();
    const node = document.createElement("div");
    node.className = `toast ${type === "success" ? "success" : type === "error" ? "error" : "info"}`;
    node.textContent = text;
    node.style.left = `${deskRect.left + 16}px`;
    node.style.top = `${deskRect.top + 74}px`;
    node.style.width = `${Math.max(220, deskRect.width - 32)}px`;
    shadow.append(node);
    setTimeout(() => node.remove(), Math.max(1200, Number(lifetime) || TOAST_LIFETIME));
  }

  function sendMessage(message) {
    return chrome.runtime.sendMessage(message).catch((error) => ({ ok: false, error: error.message || String(error) }));
  }

  function syncPluginUpdatePolling() {
    if (state.opened) {
      if (!pluginUpdateTimer) {
        refreshPluginUpdateInfo().catch(() => {});
        pluginUpdateTimer = window.setInterval(() => {
          if (!state.opened) return;
          refreshPluginUpdateInfo().catch(() => {});
        }, PLUGIN_UPDATE_POLL_MS);
      }
      return;
    }
    if (pluginUpdateTimer) {
      window.clearInterval(pluginUpdateTimer);
      pluginUpdateTimer = 0;
    }
  }

  function sortResources(resources) {
    return [...resources].sort((a, b) => {
      const priority = (item) => item.kind === "video" ? 0 : item.kind === "playlist" ? 1 : item.kind === "image" ? 2 : 3;
      const diff = priority(a) - priority(b);
      return diff || (b.time || 0) - (a.time || 0);
    });
  }

  function decorateResourcesWithPageMeta(resources) {
    const hydrated = hydrateResourceMeta(resources);
    const cards = extractDouyinVideoCards();
    if (!cards.length) return hydrated;

    const metaByUrl = new Map(cards.filter((item) => item.resourceKey).map((item) => [item.resourceKey, item]));
    return hydrated.map((item) => {
      if (item.authorName && item.likeCountText && item.sourceTraceUrl && item.videoTitle) return item;
      const matched = metaByUrl.get(normalizeResourceKey(item.url)) || null;
      if (!matched) return item;
      return mergeResourceCardMeta(item, matched);
    });
  }

  function hydrateResourceMeta(resources) {
    const metaByUrl = new Map();
    for (const item of resources) {
      const key = normalizeResourceKey(item.url);
      if (!key || (!item.authorName && !item.likeCountText && !item.sourceTraceUrl && !item.videoTitle)) continue;
      const existing = metaByUrl.get(key) || {};
      metaByUrl.set(key, {
        authorName: existing.authorName || item.authorName || "",
        likeCountText: existing.likeCountText || item.likeCountText || "",
        videoTitle: existing.videoTitle || item.videoTitle || "",
        awemeId: existing.awemeId || item.awemeId || "",
        sourceTraceUrl: existing.sourceTraceUrl || item.sourceTraceUrl || ""
      });
    }
    return resources.map((item) => {
      if (item.authorName && item.likeCountText && item.sourceTraceUrl && item.videoTitle) return item;
      const matched = metaByUrl.get(normalizeResourceKey(item.url)) || null;
      return matched ? mergeResourceCardMeta(item, matched) : item;
    });
  }

  function mergeResourceCardMeta(item, card) {
    return {
      ...item,
      authorName: item.authorName || card.authorName || "",
      likeCountText: item.likeCountText || card.likeCountText || "",
      videoTitle: item.videoTitle || card.videoTitle || "",
      awemeId: item.awemeId || card.awemeId || "",
      sourceTraceUrl: item.sourceTraceUrl || card.sourceTraceUrl || ""
    };
  }

  function extractDouyinVideoCards() {
    const searchCards = extractDouyinSearchCards();
    if (searchCards.length) return searchCards;
    return extractDouyinDetailCard();
  }

  function extractDouyinSearchCards() {
    const shells = [];
    const seen = new Set();
    for (const node of listDouyinCardShells()) {
      const shell = node.closest('[id^="waterfall_item_"]') || node;
      if (!(shell instanceof HTMLElement) || seen.has(shell)) continue;
      seen.add(shell);
      if (!isVisibleCardShell(shell)) continue;
      const meta = buildCardMeta(shell, shell.id || `card-${shells.length}`);
      if (meta.authorName || meta.likeCountText || meta.videoTitle) shells.push(meta);
    }
    return shells.sort((left, right) => left.top - right.top || left.left - right.left);
  }

  function listDouyinCardShells() {
    const shells = Array.from(document.querySelectorAll(DOUYIN_CARD_SELECTOR));
    for (const link of document.querySelectorAll(DOUYIN_PROFILE_LINK_SELECTOR)) {
      shells.push(link.closest("li") || link);
    }
    return shells.filter((shell, index) => shell && shells.indexOf(shell) === index);
  }

  function extractDouyinDetailCard() {
    const authorNode = document.querySelector(
      '[data-e2e="video-author-nickname"], [data-e2e="video-author-name"], [data-e2e="user-name"]'
    );
    const likeNode = document.querySelector('[data-e2e="like-count"], [data-e2e="video-like-count"]');
    const titleNode = document.querySelector(
      '[data-e2e="video-desc"], [data-e2e="feed-video-desc"], [data-e2e="video-title"], [data-e2e="feed-video-title"]'
    );
    const authorName = normalizeAuthorText(authorNode?.textContent || "");
    const likeCountText = normalizeLikeText(likeNode?.textContent || "");
    const videoTitle = normalizeVideoTitle(titleNode?.innerText || titleNode?.textContent || "");
    if (!authorName && !likeCountText && !videoTitle) return [];
    return [{
      id: "detail-current",
      top: 0,
      left: 0,
      authorName,
      likeCountText,
      videoTitle,
      awemeId: extractAwemeId(location.href),
      sourceTraceUrl: isSpecificDouyinTraceUrl(location.href) ? stripHash(location.href) : "",
      resourceKey: normalizeResourceKey(getCardMediaUrl(document.querySelector("video")))
    }];
  }

  function buildCardMeta(shell, id) {
    const rect = shell.getBoundingClientRect();
    const traceMeta = readSourceTraceMeta(shell);
    const lines = String(shell.innerText || "")
      .split(/\n+/)
      .map((line) => line.trim())
      .filter(Boolean)
      .slice(0, 20);
    return {
      id,
      top: rect.top,
      left: rect.left,
      authorName: findCardAuthor(lines),
      likeCountText: findCardLike(lines),
      videoTitle: findCardTitle(lines),
      awemeId: traceMeta.awemeId,
      sourceTraceUrl: traceMeta.url,
      resourceKey: normalizeResourceKey(getCardMediaUrl(shell.querySelector("video")))
    };
  }

  function isVisibleCardShell(shell) {
    const rect = shell.getBoundingClientRect();
    return rect.width > 120
      && rect.height > 120
      && rect.bottom > 0
      && rect.right > 0
      && rect.top < window.innerHeight
      && rect.left < window.innerWidth;
  }

  function getCardMediaUrl(video) {
    if (!(video instanceof HTMLVideoElement)) return "";
    const url = video.currentSrc || video.src || "";
    return /^(blob|data):/i.test(url) ? "" : url;
  }

  function readSourceTraceMeta(root) {
    const awemeId = findAwemeId(root);
    return {
      awemeId,
      url: resolveSourceTraceUrl(root, awemeId)
    };
  }

  function findAwemeId(root) {
    const roots = [];
    let node = root instanceof Element ? root : null;
    for (let depth = 0; node && depth < 8; depth += 1, node = node.parentElement) roots.push(node);
    for (const current of roots) {
      for (const value of [
        current.id,
        current.className,
        current.getAttribute?.("data-e2e-vid"),
        current.getAttribute?.("data-aweme-id"),
        current.getAttribute?.("data-video-id"),
        current.getAttribute?.("data-item-id"),
        current.getAttribute?.("data-id"),
        current.getAttribute?.("data-note-id"),
        current.getAttribute?.("data-post-id"),
        current.getAttribute?.("data-object-id")
      ]) {
        const matched = extractAwemeId(value);
        if (matched) return matched;
      }
      const link = findSourceTraceLink(current, "");
      const fromLink = extractAwemeId(link);
      if (fromLink) return fromLink;
    }
    return extractAwemeId(location.href);
  }

  function resolveSourceTraceUrl(root, awemeId) {
    const link = findSourceTraceLink(root, awemeId);
    if (link) return link;
    if (awemeId) return buildAwemePageUrl(awemeId);
    return isSpecificDouyinTraceUrl(location.href) ? stripHash(location.href) : "";
  }

  function findSourceTraceLink(root, awemeId) {
    if (!(root instanceof Element)) return "";
    const selector = [
      'a[href*="/video/"]',
      'a[href*="/note/"]',
      'a[href*="modal_id="]',
      'a[href*="/jingxuan/"]'
    ].join(",");
    const roots = [];
    let node = root;
    for (let depth = 0; node && depth < 8; depth += 1, node = node.parentElement) roots.push(node);
    for (const current of roots) {
      const links = Array.from(current.matches?.("a[href]") ? [current] : current.querySelectorAll(selector));
      const urls = links
        .map((link) => normalizeHttpUrl(link.getAttribute("href") || link.href || ""))
        .filter(isSpecificDouyinTraceUrl);
      if (awemeId) {
        const matched = urls.find((url) => url.includes(awemeId));
        if (matched) return stripHash(matched);
      }
      if (urls[0]) return stripHash(urls[0]);
    }
    return "";
  }

  function buildAwemePageUrl(awemeId) {
    return `https://www.douyin.com/video/${awemeId}`;
  }

  function getSourceTraceUrl(item) {
    const values = [
      item?.sourceTraceUrl,
      item?.pageUrl,
      ...(Array.isArray(item?.downloadCandidates) ? item.downloadCandidates.map((candidate) => candidate.sourceTraceUrl || candidate.pageUrl) : [])
    ];
    return values.map((value) => String(value || "").trim()).find(isSpecificDouyinTraceUrl) || "";
  }

  function buildSourceTraceBatchItem(item) {
    const sourceTraceUrl = getSourceTraceUrl(item);
    if (!sourceTraceUrl) return null;
    return {
      id: item.id || "",
      url: item.url || "",
      name: item.name || item.fileName || "",
      pageTitle: item.pageTitle || document.title || "",
      pageUrl: item.pageUrl || location.href,
      authorName: item.authorName || "",
      likeCountText: item.likeCountText || "",
      videoTitle: item.videoTitle || "",
      awemeId: item.awemeId || extractAwemeId(sourceTraceUrl),
      cardId: item.cardId || "",
      sourceTraceUrl
    };
  }

  function isSpecificDouyinTraceUrl(url) {
    try {
      const parsed = new URL(url, location.href);
      if (!/(^|\.)douyin\.com$/i.test(parsed.hostname)) return false;
      return /\/(video|note|jingxuan)\b/i.test(parsed.pathname) || parsed.searchParams.has("modal_id");
    } catch {
      return false;
    }
  }

  function extractAwemeId(value) {
    const match = String(value || "").match(/\d{8,}/);
    return match ? match[0] : "";
  }

  function normalizeHttpUrl(value) {
    try {
      return new URL(String(value || ""), location.href).href;
    } catch {
      return "";
    }
  }

  function stripHash(url) {
    try {
      const parsed = new URL(url, location.href);
      parsed.hash = "";
      return parsed.href;
    } catch {
      return String(url || "").split("#")[0];
    }
  }

  function findCardAuthor(lines) {
    const authorLine = lines.find((line) => /^@/.test(line)) || lines.find((line) => /@\S+/.test(line));
    return normalizeAuthorText(authorLine || "");
  }

  function normalizeAuthorText(value) {
    return String(value || "")
      .split(/[\u00b7\u2022]/)[0]
      .replace(/^@+/, "")
      .trim()
      .slice(0, 24);
  }

  function findCardLike(lines) {
    const likeLine = lines.find((line) => isLikeCountText(line));
    return normalizeLikeText(likeLine || "");
  }

  function findCardTitle(lines) {
    const titleLine = lines.find((line) => {
      const text = String(line || "").trim();
      if (text.length < 2) return false;
      if (/^\d{1,2}:\d{2}$/.test(text)) return false;
      if (isLikeCountText(text) || /^@/.test(text)) return false;
      if (/^[\u00b7\u2022]?\s*(?:\d{4}[-/.年])?\d{1,2}(?:月|[-/.])\d{1,2}日?$/.test(text)) return false;
      return !/^(?:广告|置顶|搜索|关注|喜欢|分享)$/.test(text);
    });
    return normalizeVideoTitle(titleLine || "");
  }

  function normalizeVideoTitle(value) {
    return String(value || "")
      .replace(/\s+/g, " ")
      .replace(/\s*(?:展开|收起)\s*$/u, "")
      .trim()
      .slice(0, 500);
  }

  function normalizeLikeText(value) {
    const text = String(value || "").trim().replace(/\s+/g, "");
    if (!text || !isLikeCountText(text)) return "";
    return text.endsWith("\u8d5e") ? text : `${text}\u8d5e`;
  }

  function isLikeCountText(value) {
    const text = String(value || "").trim();
    if (!text) return false;
    if (/^\d{1,2}:\d{2}$/.test(text)) return false;
    return /^(?:\d+(?:\.\d+)?(?:\u4e07|\u4ebf)?|\d{1,3}(?:,\d{3})+)(?:\u8d5e)?$/.test(text);
  }

  function normalizeResourceKey(url) {
    if (!url || isPageLikeUrl(url)) return "";
    return resourceKey(url);
  }

  function resourceKey(url) {
    try {
      const parsed = new URL(url);
      return `${parsed.origin}${parsed.pathname}`;
    } catch {
      return String(url || "").split("?")[0];
    }
  }

  function getVisibleResources() {
    if (state.view === "image") return getVisibleImageResources();
    return resolveDisplayResources(getVideoResources());
  }

  function findResource(id) {
    return getVisibleResources().find((item) => item.id === id) || null;
  }

  function getVideoResources() {
    return state.resources.filter(isTrustedVideoResource);
  }

  function getImageResources() {
    return state.resources.filter(isTrustedImageResource);
  }

  function getVisibleImageResources() {
    return getDimensionFilteredImageResources().filter(matchesImageFormatFilter);
  }

  function getDimensionFilteredImageResources() {
    return resolveDisplayImageResources(getImageResources()).filter(matchesImageDimensionFilters);
  }

  function resourceType(item) {
    if (item.kind === "playlist") return "m3u8";
    return item.extension || item.kind || "media";
  }

  function isDirectVideoUrl(url) {
    return /^https?:\/\//i.test(url || "") && !isPageLikeUrl(url) && !/\.(m3u8|mpd)(?:[?#]|$)/i.test(url || "");
  }

  function isPlayableVideoResource(item) {
    return item?.kind === "video" && isDirectVideoUrl(item.url) && isLikelyMediaResource(item);
  }

  function isVisibleResource(item) {
    return isTrustedVideoResource(item);
  }

  function isTrustedDownloadResource(item) {
    if (!item?.url || !/^https?:\/\//i.test(item.url) || isPageLikeUrl(item.url, item)) return false;
    if (item.kind === "image") return isImageResource(item);
    if (["video", "playlist", "audio"].includes(item.kind)) return isLikelyMediaResource(item);
    return MEDIA_EXTENSIONS.has(getExtension(item.url) || String(item.extension || "").toLowerCase());
  }

  function isTrustedVideoResource(item) {
    if (!isTrustedDownloadResource(item)) return false;
    return item.kind === "video" || item.kind === "playlist";
  }

  function isTrustedImageResource(item) {
    return Boolean(item)
      && item.kind === "image"
      && isTrustedDownloadResource(item)
      && isImageResource(item);
  }

  function resolveDisplayResources(resources) {
    const groups = new Map();
    for (const item of resources) {
      const key = buildDisplayResourceGroupKey(item);
      if (!key) continue;
      const group = groups.get(key) || [];
      group.push(item);
      groups.set(key, group);
    }
    return [...groups.values()]
      .map(buildDisplayResourceEntry)
      .filter(Boolean)
      .sort((left, right) => (right.time || 0) - (left.time || 0));
  }

  function resolveDisplayImageResources(resources) {
    return uniqueItems(resources.filter(isTrustedImageResource))
      .sort((left, right) => {
        const timeDiff = (right.time || 0) - (left.time || 0);
        if (timeDiff) return timeDiff;
        const areaDiff = imageArea(right) - imageArea(left);
        if (areaDiff) return areaDiff;
        return (Number(right.size) || 0) - (Number(left.size) || 0);
      });
  }

  function buildDisplayResourceGroupKey(item) {
    if (item?.cardId) return `card:${item.cardId}`;
    const key = normalizeResourceKey(item?.url);
    if (key) return `url:${key}`;
    return item?.fingerprint || item?.id || "";
  }

  function buildDisplayResourceEntry(group) {
    const preferred = group.filter(isPreferredVideoCandidate);
    if (!preferred.length) return null;
    const sorted = [...group].sort((left, right) => {
      const diff = scoreDisplayResource(right) - scoreDisplayResource(left);
      return diff || (right.time || 0) - (left.time || 0);
    });
    const best = sorted.find(isDomBackedPreferredVideoCandidate)
      || sorted.find(isPreferredDirectVideoCandidate)
      || sorted.find(isPreferredVideoCandidate)
      || sorted[0];
    if (!best) return null;
    const candidates = buildDisplayDownloadCandidates(sorted, best);
    return {
      ...best,
      authorName: best.authorName || sorted.find((item) => item.authorName)?.authorName || "",
      likeCountText: best.likeCountText || sorted.find((item) => item.likeCountText)?.likeCountText || "",
      videoTitle: best.videoTitle || sorted.find((item) => item.videoTitle)?.videoTitle || "",
      awemeId: best.awemeId || sorted.find((item) => item.awemeId)?.awemeId || "",
      sourceTraceUrl: best.sourceTraceUrl || sorted.find((item) => item.sourceTraceUrl)?.sourceTraceUrl || "",
      pageTitle: best.pageTitle || sorted.find((item) => item.pageTitle)?.pageTitle || "",
      pageUrl: best.pageUrl || sorted.find((item) => item.pageUrl)?.pageUrl || "",
      requestHeaders: best.requestHeaders || sorted.find((item) => item.requestHeaders)?.requestHeaders || null,
      size: best.size || Math.max(0, ...sorted.map((item) => Number(item.size) || 0)),
      time: Math.max(...sorted.map((item) => Number(item.time) || 0)),
      downloadCandidates: candidates.length ? candidates : [createDisplayDownloadCandidate(best, best)]
    };
  }

  function createDisplayDownloadCandidate(item, fallback) {
    return {
      ...item,
      cardId: item.cardId || fallback.cardId || "",
      authorName: item.authorName || fallback.authorName || "",
      likeCountText: item.likeCountText || fallback.likeCountText || "",
      videoTitle: item.videoTitle || fallback.videoTitle || "",
      awemeId: item.awemeId || fallback.awemeId || "",
      sourceTraceUrl: item.sourceTraceUrl || fallback.sourceTraceUrl || "",
      pageTitle: item.pageTitle || fallback.pageTitle || "",
      pageUrl: item.pageUrl || fallback.pageUrl || "",
      requestHeaders: item.requestHeaders || fallback.requestHeaders || null
    };
  }

  function listDownloadCandidates(item) {
    const candidates = Array.isArray(item?.downloadCandidates) && item.downloadCandidates.length
      ? item.downloadCandidates
      : [item];
    return uniqueItems(candidates.filter((candidate) => isSafeVideoDownloadCandidate(candidate, item)));
  }

  function buildDisplayDownloadCandidates(sorted, best) {
    const compatible = sorted.filter((item) => isCandidateCompatibleWithBest(item, best));
    const domDirect = compatible.filter(isDomBackedPreferredVideoCandidate);
    if (domDirect.length) {
      return uniqueItems(domDirect.map((item) => createDisplayDownloadCandidate(item, best))).slice(0, 3);
    }
    const directFull = compatible.filter(isPreferredDirectVideoCandidate);
    if (directFull.length) {
      return uniqueItems(directFull.map((item) => createDisplayDownloadCandidate(item, best))).slice(0, 3);
    }
    const playlists = compatible.filter((item) => item.kind === "playlist");
    if (playlists.length) {
      return uniqueItems(playlists.map((item) => createDisplayDownloadCandidate(item, best))).slice(0, 2);
    }
    const safeFallbacks = compatible.filter((item) => isPreferredVideoCandidate(item) && !isSegmentedVideoCandidate(item));
    return uniqueItems((safeFallbacks.length ? safeFallbacks : [best]).map((item) => createDisplayDownloadCandidate(item, best))).slice(0, 3);
  }

  function uniqueItems(items) {
    const result = [];
    const seen = new Set();
    for (const item of items) {
      const key = item?.fingerprint || normalizeResourceKey(item?.url) || item?.id || "";
      if (!key || seen.has(key)) continue;
      seen.add(key);
      result.push(item);
    }
    return result;
  }

  function imageArea(item) {
    return (Number(item?.width) || 0) * (Number(item?.height) || 0);
  }

  function syncImageFilters() {
    const images = resolveDisplayImageResources(getImageResources());
    const widthBounds = buildImageBounds(images, "width");
    const heightBounds = buildImageBounds(images, "height");
    state.imageFilters.widthBounds = widthBounds;
    state.imageFilters.heightBounds = heightBounds;
    state.imageFilters.minWidth = normalizeImageFilterValue(state.imageFilters.minWidth, widthBounds);
    state.imageFilters.minHeight = normalizeImageFilterValue(state.imageFilters.minHeight, heightBounds);
    if (!["all", "png", "jpeg", "other"].includes(state.imageFilters.format)) {
      state.imageFilters.format = "all";
    }
  }

  function buildImageBounds(resources, key) {
    const values = resources
      .map((item) => Number(item?.[key]) || 0)
      .filter((value) => value > 0);
    if (!values.length) return { min: 0, max: 0 };
    return {
      min: 0,
      max: Math.max(...values)
    };
  }

  function normalizeImageFilterValue(current, bounds) {
    const max = Number(bounds?.max) || 0;
    if (!max) return 0;
    const value = Number(current) || 0;
    return clampNumber(value, 0, max);
  }

  function matchesImageDimensionFilters(item) {
    const width = Number(item?.width) || 0;
    const height = Number(item?.height) || 0;
    const minWidth = Number(state.imageFilters.minWidth) || 0;
    const minHeight = Number(state.imageFilters.minHeight) || 0;
    if (!width && !height) return !minWidth && !minHeight;
    if (!width) return !minWidth && height >= minHeight;
    if (!height) return width >= minWidth && !minHeight;
    return width >= minWidth && height >= minHeight;
  }

  function matchesImageFormatFilter(item) {
    const filter = state.imageFilters.format || "all";
    return filter === "all" || getImageFormatKey(item) === filter;
  }

  function getImageFormatKey(item) {
    const extension = String(item?.extension || getExtension(item?.url || "") || "").toLowerCase();
    const mimeType = String(item?.mimeType || "").split(";")[0].trim().toLowerCase();
    if (extension === "png" || mimeType === "image/png") return "png";
    if (["jpg", "jpeg", "jfif"].includes(extension) || mimeType === "image/jpeg") return "jpeg";
    return "other";
  }

  function buildImageFormatStats(resources) {
    return resources.reduce((stats, item) => {
      stats[getImageFormatKey(item)] += 1;
      return stats;
    }, { png: 0, jpeg: 0, other: 0 });
  }

  function formatImageMeta(item) {
    const width = Number(item?.width) || 0;
    const height = Number(item?.height) || 0;
    const dimensionText = width && height ? `${width}*${height}` : width ? `${width}*--` : height ? `--*${height}` : "--*--";
    const extension = String(item?.extension || getExtension(item?.url || "") || "image").toUpperCase();
    return `${dimensionText} ${extension}`;
  }

  function isPreferredVideoCandidate(item) {
    if (!item?.url || item.kind !== "video" || isPageLikeUrl(item.url, item)) return false;
    const extension = getExtension(item.url) || String(item.extension || "").toLowerCase();
    const mimeType = String(item.mimeType || "").split(";")[0].trim().toLowerCase();
    return isDirectVideoUrl(item.url)
      || mimeType.startsWith("video/")
      || ["mp4", "m4v", "mov", "webm"].includes(extension);
  }

  function isPreferredDirectVideoCandidate(item) {
    return isPreferredVideoCandidate(item)
      && !isAudioOnlyResource(item)
      && !isSegmentedVideoCandidate(item);
  }

  function isDomBackedPreferredVideoCandidate(item) {
    return isPreferredDirectVideoCandidate(item) && isDomVideoSource(item);
  }

  function isDomVideoSource(item) {
    return /^dom-video/.test(String(item?.source || ""));
  }

  function isAudioOnlyResource(item) {
    const extension = getExtension(item?.url || "") || String(item?.extension || "").toLowerCase();
    const mimeType = String(item?.mimeType || "").split(";")[0].trim().toLowerCase();
    return item?.kind === "audio" || mimeType.startsWith("audio/") || AUDIO_EXTENSIONS.has(extension);
  }

  function isSegmentedVideoCandidate(item) {
    const extension = getExtension(item?.url || "") || String(item?.extension || "").toLowerCase();
    return item?.kind === "playlist" || ["m3u8", "mpd", "m4s", "ts"].includes(extension);
  }

  function isCandidateCompatibleWithBest(item, best) {
    if (!item?.url || !best?.url) return false;
    if (!isTrustedDownloadResource(item) || isAudioOnlyResource(item)) return false;
    const itemKey = normalizeResourceKey(item.url);
    const bestKey = normalizeResourceKey(best.url);
    if (itemKey && bestKey && itemKey === bestKey) return true;
    if (best.cardId && item.cardId && best.cardId === item.cardId) {
      if (best.authorName && item.authorName && !sameAuthor(best.authorName, item.authorName)) return false;
      if (best.likeCountText && item.likeCountText && !sameLike(best.likeCountText, item.likeCountText)) return false;
      return true;
    }
    return sameAuthor(best.authorName, item.authorName) && sameLike(best.likeCountText, item.likeCountText);
  }

  function isSafeVideoDownloadCandidate(item, context = null) {
    if (!isTrustedDownloadResource(item) || !isPreferredVideoCandidate(item) || isAudioOnlyResource(item)) return false;
    if (context && !isCandidateCompatibleWithBest(item, context)) return false;
    if (isSegmentedVideoCandidate(item)) {
      return item.kind === "playlist";
    }
    return true;
  }

  function scoreDisplayResource(item) {
    const extension = getExtension(item.url) || String(item.extension || "").toLowerCase();
    const mimeType = String(item.mimeType || "").split(";")[0].trim().toLowerCase();
    let score = 0;
    if (item.kind === "video") score += 220;
    if (isDirectVideoUrl(item.url)) score += 180;
    if (mimeType.startsWith("video/")) score += 140;
    if (["mp4", "m4v", "mov", "webm"].includes(extension)) score += 120;
    if (item.source === "dom-video") score += 180;
    if (/^dom-video/.test(item.source || "")) score += 120;
    if (item.cardId) score += 50;
    if (item.authorName) score += 24;
    if (item.likeCountText) score += 12;
    if (isAudioOnlyResource(item)) score -= 800;
    if (["m3u8", "mpd"].includes(extension) || item.kind === "playlist") score -= 180;
    if (["m4s", "ts"].includes(extension)) score -= 120;
    score += Math.min(40, Math.round((Number(item.size) || 0) / 1024 / 1024 * 6));
    return score;
  }

  function sameAuthor(left, right) {
    return Boolean(normalizeAuthorText(left) && normalizeAuthorText(left) === normalizeAuthorText(right));
  }

  function sameLike(left, right) {
    return Boolean(normalizeLikeText(left) && normalizeLikeText(left) === normalizeLikeText(right));
  }

  function isLikelyMediaResource(item) {
    const extension = getExtension(item.url) || String(item.extension || "").toLowerCase();
    const mimeType = String(item.mimeType || "").split(";")[0].trim().toLowerCase();
    if (mimeType.startsWith("video/") || mimeType.startsWith("audio/") || mimeType.includes("mpegurl") || mimeType.includes("dash+xml")) return true;
    if (extension && MEDIA_EXTENSIONS.has(extension)) return true;
    return TRUSTED_MEDIA_HOSTS.test(getHost(item.url));
  }

  function isImageResource(item) {
    const extension = getExtension(item.url) || String(item.extension || "").toLowerCase();
    const mimeType = String(item.mimeType || "").split(";")[0].trim().toLowerCase();
    return mimeType.startsWith("image/") || ["jpg", "jpeg", "png", "gif", "webp", "avif", "bmp", "svg", "apng"].includes(extension);
  }

  function isPageLikeUrl(url, item = {}) {
    const extension = getExtension(url) || String(item.extension || "").toLowerCase();
    const mimeType = String(item.mimeType || "").split(";")[0].trim().toLowerCase();
    if (PAGE_EXTENSIONS.has(extension) || mimeType === "text/html" || mimeType === "application/xhtml+xml") return true;
    try {
      const parsed = new URL(url);
      return /(^|\.)douyin\.com$/i.test(parsed.hostname)
        && /^\/(video|search|user|discover|note)(\/|$)/i.test(parsed.pathname);
    } catch {
      return false;
    }
  }

  function getExtension(url) {
    try {
      const pathname = new URL(url).pathname.toLowerCase();
      const match = pathname.match(/\.([a-z0-9]{2,8})$/i);
      return match ? match[1] : "";
    } catch {
      const match = String(url || "").toLowerCase().match(/\.([a-z0-9]{2,8})(?:[?#]|$)/);
      return match ? match[1] : "";
    }
  }

  function getHost(url) {
    try {
      return new URL(url).hostname;
    } catch {
      return "";
    }
  }

  function formatSize(value) {
    const size = Number(value || 0);
    if (!size) return "";
    if (size < 1024) return `${size} B`;
    if (size < 1024 * 1024) return `${(size / 1024).toFixed(size < 10 * 1024 ? 1 : 0)} KB`;
    return `${(size / 1024 / 1024).toFixed(size < 10 * 1024 * 1024 ? 1 : 0)} MB`;
  }

  function normalizeDimensionValue(value) {
    const number = Number(value) || 0;
    return number > 0 ? Math.round(number) : 0;
  }

  function clampNumber(value, min, max) {
    return Math.min(Math.max(Number(value) || 0, min), max);
  }

  function formatBrowserAddress(url) {
    try {
      const parsed = new URL(url);
      const path = parsed.pathname === "/" ? "" : parsed.pathname;
      const search = parsed.search ? parsed.search.slice(0, 22) : "";
      const suffix = parsed.search.length > 22 ? "..." : "";
      return `${parsed.host}${path}${search}${suffix}`.slice(0, 56);
    } catch {
      return String(url || "").slice(0, 56);
    }
  }

  function updateFeatureCardTilt(event) {
    const card = event.currentTarget;
    const rect = card.getBoundingClientRect();
    if (!rect.width || !rect.height) return;
    const ratioX = clamp((event.clientX - rect.left) / rect.width, 0, 1);
    const ratioY = clamp((event.clientY - rect.top) / rect.height, 0, 1);
    const rotateX = (0.5 - ratioY) * 14;
    const rotateY = (ratioX - 0.5) * 16;
    card.style.setProperty("--feature-rotate-x", `${rotateX.toFixed(2)}deg`);
    card.style.setProperty("--feature-rotate-y", `${rotateY.toFixed(2)}deg`);
    card.style.setProperty("--feature-glare-x", `${(ratioX * 100).toFixed(1)}%`);
    card.style.setProperty("--feature-glare-y", `${(ratioY * 100).toFixed(1)}%`);
  }

  function resetFeatureCardTilt(event) {
    const card = event.currentTarget;
    card.style.removeProperty("--feature-rotate-x");
    card.style.removeProperty("--feature-rotate-y");
    card.style.removeProperty("--feature-glare-x");
    card.style.removeProperty("--feature-glare-y");
  }

  function createDefaultDock() {
    const x = Math.max(EDGE_GAP, window.innerWidth - LAUNCHER_SIZE - EDGE_GAP);
    return {
      x,
      y: Math.max(100, Math.round(window.innerHeight * 0.4)),
      side: "right",
      hidden: false
    };
  }

  function normalizeDock(raw) {
    const base = raw && typeof raw === "object" ? raw : createDefaultDock();
    const maxX = Math.max(EDGE_GAP, window.innerWidth - LAUNCHER_SIZE - EDGE_GAP);
    const maxY = Math.max(EDGE_GAP, window.innerHeight - LAUNCHER_SIZE - EDGE_GAP);
    return {
      x: clamp(Number(base.x) || EDGE_GAP, EDGE_GAP, maxX),
      y: clamp(Number(base.y) || createDefaultDock().y, EDGE_GAP, maxY),
      side: base.side === "right" ? "right" : "left",
      hidden: Boolean(base.hidden)
    };
  }

  function snapDock(dock) {
    const maxX = Math.max(EDGE_GAP, window.innerWidth - LAUNCHER_SIZE - EDGE_GAP);
    const next = normalizeDock(dock);
    if (next.x <= EDGE_SNAP) {
      next.x = EDGE_GAP;
      next.side = "left";
      next.hidden = true;
      return next;
    }
    if (next.x >= maxX - EDGE_SNAP) {
      next.x = maxX;
      next.side = "right";
      next.hidden = true;
      return next;
    }
    next.hidden = false;
    next.side = next.x + LAUNCHER_SIZE / 2 >= window.innerWidth / 2 ? "right" : "left";
    return next;
  }

  function persistDock() {
    chrome.storage.local.set({
      [STORAGE_KEYS.dock]: {
        x: Math.round(state.dock.x),
        y: Math.round(state.dock.y),
        side: state.dock.side,
        hidden: state.dock.hidden
      }
    }).catch(() => {});
  }

  function getLauncherRect() {
    return {
      left: state.dock.x,
      top: state.dock.y
    };
  }

  function getDeskRect() {
    const wideView = state.view === "shopMove";
    const width = Math.min(wideView ? SHOP_MOVE_DESK_WIDTH : DESK_WIDTH, Math.max(280, window.innerWidth - 24));
    const maxHeight = Math.min(wideView ? SHOP_MOVE_DESK_MAX_HEIGHT : DESK_MAX_HEIGHT, Math.max(360, window.innerHeight - 24));
    let left = state.dock.side === "right"
      ? state.dock.x - width - PANEL_GAP
      : state.dock.x + LAUNCHER_SIZE + PANEL_GAP;
    let top = state.dock.y - 12;

    left = clamp(left, 12, Math.max(12, window.innerWidth - width - 12));
    top = clamp(top, 12, Math.max(12, window.innerHeight - maxHeight - 12));

    return { left, top, width, maxHeight };
  }

  function clamp(value, min, max) {
    return Math.min(Math.max(value, min), max);
  }

  function delay(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

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

  function onReady(callback) {
    if (document.body) callback();
    else document.addEventListener("DOMContentLoaded", callback, { once: true });
  }

  function styles() {
    return `
      :host{all:initial}
      *{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Microsoft YaHei",sans-serif}
      .launcher{
        position:fixed;
        z-index:1170;
        width:54px;
        height:54px;
        padding:0;
        border:1px solid rgba(255,255,255,.28);
        border-radius:18px;
        background:rgba(17,24,39,.42);
        box-shadow:0 10px 28px rgba(0,0,0,.22);
        backdrop-filter:blur(14px) saturate(1.35);
        -webkit-backdrop-filter:blur(14px) saturate(1.35);
        cursor:pointer;
        touch-action:none;
        user-select:none;
        transition:transform .18s ease,box-shadow .18s ease,border-color .18s ease,border-radius .18s ease;
      }
      .launcher.peek-left{transform:translateX(-22px);border-radius:0 18px 18px 0}
      .launcher.peek-right{transform:translateX(22px);border-radius:18px 0 0 18px}
      .launcher:hover,.launcher.is-open,.launcher.is-dragging{transform:translateX(0)}
      .launcher:hover{box-shadow:0 14px 34px rgba(0,0,0,.34)}
      .launcher.has-update{
        border-color:rgba(255,184,61,.96);
        box-shadow:
          0 0 0 3px rgba(255,184,61,.2),
          0 0 24px rgba(255,166,35,.58),
          0 10px 28px rgba(0,0,0,.24);
      }
      .launcher.has-update:hover,
      .launcher.has-update.is-open,
      .launcher.has-update.is-dragging{
        box-shadow:
          0 0 0 3px rgba(255,184,61,.26),
          0 0 30px rgba(255,166,35,.68),
          0 14px 34px rgba(0,0,0,.3);
      }
      .launcherImage{display:block;width:100%;height:100%;padding:9px;object-fit:contain;pointer-events:none;user-select:none}
      .desk{
        position:fixed;
        z-index:1170;
        display:flex;
        flex-direction:column;
        overflow:hidden;
        border-radius:14px;
        background:#d8d9dd;
        color:#0b1020;
        box-shadow:0 20px 48px rgba(0,0,0,.24);
        border:1px solid rgba(255,255,255,.55);
      }
      .desk.auto-height{height:auto}
      button{cursor:pointer}
      .browserShell{
        display:flex;
        flex:1;
        flex-direction:column;
        min-height:0;
        height:100%;
        background:#d8d9dd;
      }
      .desk.auto-height .browserShell{height:auto}
      .browserTabs{
        display:flex;
        align-items:flex-end;
        justify-content:space-between;
        gap:10px;
        padding:12px 12px 0;
        background:#2f2f32;
        cursor:grab;
        touch-action:none;
      }
      .browserTabs:active{cursor:grabbing}
      .browserTabsLeft{
        display:flex;
        align-items:flex-end;
        gap:6px;
        min-width:0;
        flex:1;
      }
      .browserUpdateSlot{
        display:flex;
        align-items:flex-end;
        padding:0 2px 8px;
      }
      .browserTab{
        min-width:0;
        height:36px;
        border-radius:10px 10px 0 0;
        border:0;
        background:#4a4b4f;
        color:#fff;
      }
      .browserHomeTab{
        max-width:160px;
        padding:8px 12px 0;
        display:grid;
        grid-template-columns:18px minmax(0,1fr);
        align-items:start;
        column-gap:8px;
        text-align:left;
      }
      .browserHomeTab.active{background:#5a5b60}
      .browserHomeMeta{
        display:flex;
        flex-direction:column;
        min-width:0;
      }
      .browserVersionTag{
        display:block;
        color:rgba(255,255,255,.62);
        font-size:10px;
        line-height:1.15;
        letter-spacing:.04em;
      }
      .browserFeatureTab{
        max-width:132px;
        padding:0 8px 0 12px;
        display:flex;
        align-items:center;
        gap:8px;
        background:#5a5b60;
      }
      .browserTabIcon{
        width:18px;
        height:18px;
        object-fit:contain;
        margin-top:1px;
      }
      .browserTab span{
        display:block;
        color:#fff;
        font-size:12px;
        line-height:1.2;
        white-space:nowrap;
        overflow:hidden;
        text-overflow:ellipsis;
      }
      .browserFeatureTab span{flex:1}
      .browserTabClose{
        width:20px;
        height:20px;
        flex:0 0 20px;
        border:0;
        border-radius:999px;
        background:transparent;
        color:#e5e7eb;
        font-size:11px;
        line-height:1;
        transition:background .18s ease;
      }
      .browserTabClose:hover{background:rgba(255,255,255,.12)}
      .browserWindowButtons{
        display:flex;
        align-items:center;
        gap:8px;
        padding:0 2px 8px;
      }
      .browserUpdateBell{
        position:relative;
        width:28px;
        height:28px;
        border:1px solid rgba(255,255,255,.12);
        border-radius:8px;
        background:rgba(255,255,255,.06);
        color:rgba(255,255,255,.74);
        display:flex;
        align-items:center;
        justify-content:center;
        box-shadow:inset 0 1px 0 rgba(255,255,255,.05);
        transition:transform .18s ease, background .18s ease, color .18s ease, border-color .18s ease, box-shadow .18s ease;
        cursor:default;
      }
      .browserUpdateBell svg{
        width:16px;
        height:16px;
        display:block;
        fill:currentColor;
      }
      .browserUpdateBell.has-update{
        color:#ffd85f;
        background:rgba(255,216,95,.14);
        border-color:rgba(255,216,95,.28);
        box-shadow:
          0 0 16px rgba(255,216,95,.16),
          inset 0 1px 0 rgba(255,255,255,.1);
        cursor:pointer;
        animation:mmwBellWiggle 1.9s ease-in-out infinite;
        transform-origin:50% 12%;
      }
      .browserUpdateBell.has-update:hover{
        background:rgba(255,216,95,.2);
        box-shadow:
          0 0 20px rgba(255,216,95,.22),
          inset 0 1px 0 rgba(255,255,255,.12);
      }
      .browserUpdateBell::after{
        content:attr(data-tooltip);
        position:absolute;
        top:calc(100% + 8px);
        right:-4px;
        padding:6px 10px;
        border-radius:8px;
        background:rgba(17,24,39,.96);
        color:#f8fafc;
        font-size:11px;
        line-height:1.3;
        white-space:nowrap;
        box-shadow:0 12px 30px rgba(15,23,42,.3);
        opacity:0;
        transform:translateY(-3px);
        pointer-events:none;
        transition:opacity .18s ease, transform .18s ease;
      }
      .browserUpdateBell:hover::after{
        opacity:1;
        transform:translateY(0);
      }
      .browserWindowDot{
        width:10px;
        height:10px;
        border-radius:999px;
        background:rgba(255,255,255,.24);
        display:block;
      }
      .browserWindowClose{
        width:28px;
        height:28px;
        border:0;
        border-radius:8px;
        background:transparent;
        color:#fff;
        font-size:16px;
        line-height:1;
        transition:background .18s ease;
      }
      .browserWindowClose:hover{background:#d14343}
      .browserToolbar{
        display:flex;
        align-items:center;
        gap:8px;
        padding:10px 12px;
        background:#4a4b4f;
      }
      .browserNavButton{
        width:30px;
        height:30px;
        border:0;
        border-radius:999px;
        background:transparent;
        color:#f3f4f6;
        font-size:18px;
        line-height:1;
        transition:background .18s ease,opacity .18s ease;
      }
      .browserNavButton:hover{background:rgba(255,255,255,.12)}
      .browserNavButton:disabled{
        opacity:.36;
        cursor:default;
      }
      .browserNavButton:disabled:hover{background:transparent}
      @keyframes mmwBellWiggle{
        0%, 100%{transform:rotate(0deg)}
        10%{transform:rotate(0deg)}
        18%{transform:rotate(10deg)}
        30%{transform:rotate(-9deg)}
        42%{transform:rotate(7deg)}
        54%{transform:rotate(-5deg)}
        66%{transform:rotate(3deg)}
        76%{transform:rotate(0deg)}
      }
      .browserAddress{
        flex:1;
        min-width:0;
        height:30px;
        padding:0 12px;
        border:0;
        border-radius:999px;
        background:#3a3b3f;
        color:#f9fafb;
        display:flex;
        align-items:center;
        gap:8px;
        font-size:12px;
        text-align:left;
      }
      .browserAddress span:last-child{
        min-width:0;
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
      }
      .browserAddressBadge{
        width:8px;
        height:8px;
        flex:0 0 8px;
        border-radius:999px;
        background:#8bd17c;
      }
      .browserStatusBar{
        display:flex;
        gap:8px;
        padding:8px 12px 10px;
        background:#4a4b4f;
        border-top:1px solid rgba(255,255,255,.04);
        flex-wrap:wrap;
      }
      .browserPageChip{
        flex:0 0 auto;
        min-width:86px;
        max-width:112px;
        height:30px;
        padding:0 10px;
        border:1px solid rgba(255,255,255,.08);
        border-radius:8px;
        background:rgba(255,255,255,.06);
        color:#f3f4f6;
        display:flex;
        align-items:center;
        justify-content:center;
        text-align:center;
      }
      .browserPageChip.active{
        background:rgba(255,255,255,.12);
        border-color:rgba(255,255,255,.16);
      }
      .browserPageChip:disabled{
        cursor:not-allowed;
        opacity:.45;
      }
      .browserPageChip b{
        display:block;
        font-size:11px;
        line-height:1.2;
        white-space:nowrap;
        overflow:hidden;
        text-overflow:ellipsis;
      }
      .browserViewport{
        flex:1;
        min-height:0;
        background:#e6e6e7;
        padding:12px 12px 16px;
      }
      .desk.auto-height .browserViewport{flex:0 0 auto}
      .browserViewportInner{
        display:flex;
        flex-direction:column;
        flex:1;
        height:100%;
        min-height:0;
        max-height:calc(100vh - 160px);
        overflow:auto;
        padding:14px 14px 18px;
        border-radius:12px;
        background:rgba(255,255,255,.72);
        box-shadow:inset 0 1px 0 rgba(255,255,255,.7);
      }
      .desk.auto-height .browserViewportInner{height:auto}
      .authViewport{
        display:flex;
        align-items:center;
        justify-content:center;
        padding:18px 14px;
        background:
          radial-gradient(circle at top right, rgba(255,255,255,.92) 0%, rgba(255,255,255,.74) 24%, rgba(232,232,232,.92) 100%);
      }
      .homeViewport{
        display:block;
        height:auto;
        max-height:none;
        min-height:0;
        padding:0 0 10px;
        background:transparent;
        box-shadow:none;
        overflow:auto;
      }
      .shopMoveViewport{
        padding:0;
        overflow:hidden;
        background:#eef3fb;
      }
      .shopMovePage{
        display:flex;
        flex:1;
        min-height:0;
        height:100%;
      }
      .shopMoveFrame{
        display:block;
        width:100%;
        height:100%;
        min-height:560px;
        border:0;
        background:#eef3fb;
      }
      .loginButton{
        display:flex;
        align-items:center;
        justify-content:center;
        gap:10px;
        min-width:120px;
        height:38px;
        margin:0 0 10px 2px;
        padding:0 10px;
        border:2px solid #111827;
        border-radius:999px;
        background:#fdd835;
        box-shadow:0 8px 16px rgba(0,0,0,.14);
        color:#fffdf3;
        font-size:16px;
        font-weight:700;
        letter-spacing:1px;
        line-height:1;
        text-shadow:2px 2px 0 rgba(125,90,9,.35);
        position:relative;
        overflow:hidden;
        isolation:isolate;
        transition:all .5s ease;
      }
      .loginButton:hover{box-shadow:0 10px 18px rgba(0,0,0,.18)}
      .loginButton:active{transform:scale(.9);transition:all .1s ease}
      .loginButton:focus-visible{outline:2px solid rgba(17,24,39,.4);outline-offset:2px}
      .loginButtonIcon{
        width:36px;
        height:36px;
        flex:0 0 36px;
        filter:drop-shadow(0 2px 0 rgba(0,0,0,.16));
        transition:all .5s ease;
        z-index:2;
      }
      .loginButtonMain{
        display:block;
        position:relative;
        z-index:2;
        white-space:nowrap;
        transition:all .5s ease;
        transition-delay:.3s;
      }
      .loginButtonNow{
        position:absolute;
        left:10px;
        transform:translateX(-120%);
        transition:all .5s ease;
        z-index:2;
        white-space:nowrap;
      }
      .loginButton:hover .loginButtonIcon{transform:scale(2.8) translate(46%)}
      .loginButton:hover .loginButtonNow{transform:translateX(8px);transition-delay:.3s}
      .loginButton:hover .loginButtonMain{transform:translateX(200%);transition-delay:.3s}
      .authLogoutButton{margin:0}
      .authCard{
        display:grid;
        grid-template-columns:40px minmax(0,1fr) auto;
        gap:10px;
        align-items:center;
        margin:0 0 14px;
      }
      .avatar{
        width:40px;
        height:40px;
        border-radius:50%;
        display:flex;
        align-items:center;
        justify-content:center;
        background:#9bd1ff;
        color:#111827;
        font-size:18px;
        font-weight:800;
      }
      .authMeta{min-width:0}
      .authNameRow{
        display:flex;
        align-items:center;
        gap:8px;
        min-width:0;
      }
      .authMeta b{
        min-width:0;
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
        font-size:19px;
        line-height:1.25;
      }
      .pointsBadge{
        flex:0 0 auto;
        max-width:96px;
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
        border:1px solid rgba(34,197,94,.42);
        border-radius:999px;
        background:rgba(220,252,231,.92);
        color:#166534;
        padding:2px 7px;
        font-size:10px;
        font-weight:800;
        line-height:1.25;
      }
      .authMeta small{
        display:block;
        margin-top:3px;
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
        color:#374151;
        font-size:11px;
      }
      .authSmallButton{
        height:30px;
        padding:0 9px;
        border:0;
        border-radius:8px;
        background:rgba(255,255,255,.35);
        color:#111827;
        font-size:12px;
      }
      .authPage{
        width:100%;
        display:flex;
        flex-direction:column;
        align-items:center;
        justify-content:center;
        gap:18px;
        color:#323232;
      }
      .authSwitch{
        display:flex;
        align-items:center;
        justify-content:center;
        gap:16px;
        width:100%;
      }
      .authSwitchLabel{
        border:0;
        background:transparent;
        color:#323232;
        font-size:16px;
        font-weight:700;
        text-decoration:none;
        opacity:.62;
        transition:opacity .2s ease,text-decoration-color .2s ease;
      }
      .authSwitchLabel.active{
        opacity:1;
        text-decoration:underline;
        text-underline-offset:4px;
      }
      .authSwitchToggle{
        width:54px;
        height:26px;
        padding:0;
        border:2px solid #323232;
        border-radius:7px;
        background:#f5f5f5;
        box-shadow:4px 4px 0 #323232;
        position:relative;
        transition:background .25s ease;
      }
      .authSwitchThumb{
        position:absolute;
        left:-2px;
        top:2px;
        width:22px;
        height:22px;
        border:2px solid #323232;
        border-radius:6px;
        background:#fff;
        box-shadow:0 3px 0 #323232;
        transition:transform .25s ease;
      }
      .authFlipCard{
        width:100%;
        max-width:300px;
      }
      .authFlipInner{
        width:100%;
        min-height:360px;
      }
      .authPanel{
        min-height:360px;
        padding:26px 20px 22px;
        display:flex;
        flex-direction:column;
        justify-content:center;
        gap:18px;
        border-radius:6px;
        border:2px solid #323232;
        background:#d9d9d9;
        box-shadow:4px 4px 0 #323232;
      }
      .authPanelTitle{
        margin:0;
        text-align:center;
        color:#323232;
        font-size:24px;
        font-weight:900;
      }
      .authForm{
        display:flex;
        flex-direction:column;
        align-items:center;
        gap:16px;
      }
      .authForm input{
        width:100%;
        height:42px;
        padding:0 12px;
        border:2px solid #323232;
        border-radius:6px;
        background:#fff;
        box-shadow:4px 4px 0 #323232;
        color:#323232;
        font-size:14px;
        font-weight:600;
        outline:none;
      }
      .authForm input::placeholder{color:#777}
      .authForm input:focus{border-color:#2d8cf0}
      .authSubmit{
        min-width:128px;
        height:42px;
        margin-top:4px;
        padding:0 18px;
        border:2px solid #323232;
        border-radius:6px;
        background:#fff;
        box-shadow:4px 4px 0 #323232;
        color:#323232;
        font-size:17px;
        font-weight:700;
        transition:transform .12s ease,box-shadow .12s ease;
      }
      .authSubmit:active{
        transform:translate(3px,3px);
        box-shadow:0 0 0 #323232;
      }
      .authSubmit:disabled{
        opacity:.7;
        cursor:wait;
      }
      .authPageMessage{
        width:100%;
        max-width:300px;
        min-height:20px;
        padding:0 4px;
        color:#7f1d1d;
        font-size:12px;
        line-height:1.5;
        text-align:center;
        word-break:break-word;
      }
      .authPageLoggedIn{
        width:100%;
        max-width:300px;
        padding:20px;
        border:2px solid #323232;
        border-radius:6px;
        background:#d9d9d9;
        box-shadow:4px 4px 0 #323232;
      }
      .authPageSummary{
        display:grid;
        grid-template-columns:44px minmax(0,1fr);
        gap:12px;
        align-items:center;
      }
      .authPageSummaryMeta b{
        display:block;
        font-size:18px;
        line-height:1.2;
      }
      .authPageSummaryMeta small{
        display:block;
        margin-top:4px;
        color:#4b5563;
        font-size:12px;
        line-height:1.5;
      }
      .authPageActions{
        display:flex;
        justify-content:center;
        gap:10px;
        margin-top:18px;
      }
      .authPageActions button{
        min-width:94px;
        height:36px;
        padding:0 12px;
        border:2px solid #323232;
        border-radius:6px;
        background:#fff;
        box-shadow:4px 4px 0 #323232;
        color:#323232;
        font-size:13px;
        font-weight:700;
      }
      .homeScene{
        position:relative;
        min-height:206px;
        padding:14px 16px 18px;
        border-radius:18px;
        overflow:hidden;
        background:
          linear-gradient(180deg, rgba(29,27,39,.98) 0%, rgba(16,18,28,.98) 100%);
        border:1px solid rgba(129,140,248,.14);
        box-shadow:
          0 22px 38px rgba(15,23,42,.16),
          inset 0 1px 0 rgba(255,255,255,.08),
          inset 0 0 56px rgba(76,29,149,.18);
        isolation:isolate;
      }
      .homeScene::before{
        content:"";
        position:absolute;
        inset:0;
        background:
          radial-gradient(circle at 18% 6%, rgba(203,172,255,.24) 0%, rgba(155,64,252,.14) 24%, transparent 48%),
          radial-gradient(circle at 82% 82%, rgba(56,189,248,.12) 0%, transparent 24%);
        pointer-events:none;
        z-index:0;
      }
      .homeScene::after{
        content:"";
        position:absolute;
        inset:0;
        background:linear-gradient(180deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,0) 26%, rgba(0,0,0,.16) 100%);
        pointer-events:none;
        z-index:0;
      }
      .homeSceneBackdrop{
        position:absolute;
        inset:0;
        pointer-events:none;
        z-index:0;
      }
      .homeSceneContent{
        position:relative;
        z-index:1;
      }
      .homeSceneMoon{
        position:absolute;
        top:-34px;
        left:-18px;
        width:132px;
        height:132px;
        border-radius:50%;
        background:#faf8ff;
        box-shadow:
          0 0 90px rgba(193,119,241,.7),
          0 0 130px rgba(135,42,211,.38),
          inset 0 0 24px rgba(155,64,252,.18);
      }
      .homeSceneGlow{
        position:absolute;
        top:14px;
        left:58px;
        width:166px;
        height:92px;
        border-radius:999px;
        background:radial-gradient(circle, rgba(155,64,252,.28) 0%, rgba(155,64,252,.08) 38%, transparent 72%);
        filter:blur(14px);
      }
      .homeSceneStars{
        position:absolute;
        top:0;
        left:0;
        width:2px;
        height:2px;
        border-radius:50%;
        background:rgba(255,255,255,.92);
        animation:mmwTwinkle 3.2s linear infinite alternate;
      }
      .homeSceneStarsA{
        box-shadow:
          36px 46px rgba(255,255,255,.9),
          82px 22px rgba(255,255,255,.7),
          146px 34px rgba(255,255,255,.82),
          214px 58px rgba(255,255,255,.78),
          286px 28px rgba(255,255,255,.92),
          332px 64px rgba(255,255,255,.8),
          104px 120px rgba(255,255,255,.72),
          48px 152px rgba(255,255,255,.88),
          258px 142px rgba(255,255,255,.7),
          340px 132px rgba(255,255,255,.8),
          374px 96px rgba(255,255,255,.64);
      }
      .homeSceneStarsB{
        animation-delay:.7s;
        box-shadow:
          24px 96px rgba(255,255,255,.72),
          118px 74px rgba(255,255,255,.86),
          176px 102px rgba(255,255,255,.68),
          236px 18px rgba(255,255,255,.88),
          302px 90px rgba(255,255,255,.74),
          354px 34px rgba(255,255,255,.82),
          388px 124px rgba(255,255,255,.62),
          320px 170px rgba(255,255,255,.86),
          208px 170px rgba(255,255,255,.64),
          88px 178px rgba(255,255,255,.8);
      }
      .homeSceneStarsC{
        animation-delay:1.3s;
        box-shadow:
          62px 68px rgba(255,255,255,.8),
          156px 152px rgba(255,255,255,.86),
          224px 134px rgba(255,255,255,.74),
          276px 118px rgba(255,255,255,.8),
          346px 164px rgba(255,255,255,.92),
          396px 68px rgba(255,255,255,.76),
          18px 138px rgba(255,255,255,.72),
          132px 40px rgba(255,255,255,.62),
          250px 72px rgba(255,255,255,.82),
          372px 18px rgba(255,255,255,.7);
      }
      .homeSceneTrail{
        position:absolute;
        top:40px;
        right:58px;
        width:86px;
        height:1px;
        background:linear-gradient(90deg, transparent, rgba(255,255,255,.92), transparent);
        transform:rotate(-38deg);
        opacity:.7;
        filter:drop-shadow(0 0 6px rgba(255,255,255,.5));
        animation:mmwShootingStar 6.2s ease-in-out infinite;
      }
      .homeSceneAstronaut{
        position:absolute;
        right:-6px;
        bottom:-8px;
        width:122px;
        opacity:.92;
        filter:
          drop-shadow(0 14px 22px rgba(24,24,39,.5))
          drop-shadow(0 0 24px rgba(91,232,255,.18));
        animation:mmwFloatAstronaut 10s ease-in-out infinite;
        user-select:none;
        -webkit-user-drag:none;
      }
      .homeScene .authCard{
        position:relative;
        z-index:2;
        margin:0 0 16px;
      }
      .homeScene .authMeta{
        padding:8px 14px 9px;
        border-radius:14px;
        background:linear-gradient(180deg, rgba(255,255,255,.2) 0%, rgba(255,255,255,.1) 100%);
        border:1px solid rgba(255,255,255,.18);
        box-shadow:
          0 12px 28px rgba(15,23,42,.18),
          inset 0 1px 0 rgba(255,255,255,.22);
        backdrop-filter:blur(12px) saturate(140%);
        -webkit-backdrop-filter:blur(12px) saturate(140%);
      }
      .homeScene .avatar{
        box-shadow:
          0 10px 24px rgba(59,130,246,.18),
          inset 0 1px 0 rgba(255,255,255,.24);
      }
      .homeScene .authMeta b{
        color:#f8fafc;
        text-shadow:0 2px 12px rgba(15,23,42,.5);
      }
      .homeScene .pointsBadge{
        border-color:rgba(134,239,172,.58);
        background:rgba(220,252,231,.92);
        box-shadow:0 6px 14px rgba(22,101,52,.18);
      }
      .homeScene .authMeta small{
        color:rgba(241,245,249,.92);
        text-shadow:0 1px 10px rgba(15,23,42,.42);
      }
      .homeGrid{
        display:grid;
        grid-template-columns:1fr 1fr;
        gap:14px;
        padding:2px 0 4px;
        perspective:1200px;
      }
      .homePageStage{
        min-height:304px;
      }
      .homePagePanel[hidden]{display:none}
      .homePagePanel.is-active{
        display:block;
      }
      .homeGridQianchuan{
        align-content:start;
      }
      .homePager{
        display:grid;
        grid-template-columns:1fr 1fr;
        width:190px;
        height:36px;
        margin:12px auto 0;
        padding:3px;
        border:1px solid rgba(255,255,255,.18);
        border-radius:999px;
        background:rgba(12,17,29,.7);
        box-shadow:inset 0 1px 0 rgba(255,255,255,.08),0 8px 20px rgba(2,6,23,.2);
        backdrop-filter:blur(10px);
        -webkit-backdrop-filter:blur(10px);
      }
      .homePagerButton{
        min-width:0;
        border:0;
        border-radius:999px;
        background:transparent;
        color:rgba(226,232,240,.68);
        font-size:12px;
        font-weight:700;
        transition:background .18s ease,color .18s ease,box-shadow .18s ease;
      }
      .homePagerButton:hover{color:#fff}
      .homePagerButton.is-active{
        background:linear-gradient(135deg,#eaf4ff 0%,#d9e8ff 100%);
        color:#175fb8;
        box-shadow:0 4px 12px rgba(15,23,42,.24),inset 0 1px 0 rgba(255,255,255,.9);
      }
      .homePagerButton:focus-visible{
        outline:2px solid #7dd3fc;
        outline-offset:2px;
      }
      .feature{
        position:relative;
        height:92px;
        padding:0;
        border:0;
        border-radius:16px;
        background:transparent;
        color:#111827;
        overflow:visible;
        transform-style:preserve-3d;
      }
      .featureCardInner{
        position:absolute;
        inset:0;
        border-radius:16px;
        overflow:hidden;
        background:
          radial-gradient(circle at 18% 16%, rgba(255,255,255,.12) 0%, transparent 24%),
          linear-gradient(180deg, rgba(32,36,57,.98) 0%, rgba(18,22,34,.98) 100%);
        border:1px solid rgba(111,126,255,.18);
        box-shadow:
          0 14px 26px rgba(15,23,42,.16),
          inset 0 1px 0 rgba(255,255,255,.06),
          inset 0 0 24px rgba(0,0,0,.28);
        transform:
          rotateX(var(--feature-rotate-x, 0deg))
          rotateY(var(--feature-rotate-y, 0deg))
          translateZ(0);
        transition:transform .18s ease, box-shadow .24s ease, filter .24s ease, border-color .24s ease;
      }
      .featureCard:hover .featureCardInner{
        box-shadow:
          0 18px 30px rgba(15,23,42,.22),
          0 0 22px rgba(92,103,255,.18),
          inset 0 1px 0 rgba(255,255,255,.08),
          inset 0 0 28px rgba(0,0,0,.28);
        filter:brightness(1.05);
      }
      .featureCard.locked .featureCardInner{filter:saturate(.78) brightness(.94)}
      .featureCardGlow,
      .featureCardGrid,
      .featureCardLines,
      .featureCardParticles,
      .featureCardCorners,
      .featureCardContent{
        position:absolute;
        inset:0;
        pointer-events:none;
      }
      .featureCardGlow{
        background:
          radial-gradient(circle at var(--feature-glare-x, 50%) var(--feature-glare-y, 50%), rgba(137,236,255,.24), transparent 34%),
          radial-gradient(circle at 80% 18%, rgba(124,92,255,.18), transparent 30%);
        opacity:.46;
        transition:opacity .2s ease;
      }
      .featureCard:hover .featureCardGlow{opacity:.9}
      .featureCardGrid{
        background:
          linear-gradient(rgba(92,103,255,.11) 1px, transparent 1px),
          linear-gradient(90deg, rgba(92,103,255,.1) 1px, transparent 1px);
        background-size:100% 26px, 42px 100%;
        mask-image:linear-gradient(180deg, rgba(255,255,255,.75), transparent 85%);
        opacity:.18;
      }
      @keyframes mmwTwinkle{
        0%{opacity:.25}
        50%{opacity:.95}
        100%{opacity:.4}
      }
      @keyframes mmwShootingStar{
        0%, 100%{transform:translateX(0) rotate(-38deg);opacity:0}
        12%{opacity:.65}
        32%{transform:translateX(-126px) rotate(-38deg);opacity:.78}
        46%{transform:translateX(-170px) rotate(-38deg);opacity:0}
      }
      @keyframes mmwFloatAstronaut{
        0%{transform:translate3d(0,0,0) rotate(-4deg)}
        25%{transform:translate3d(-8px,-10px,0) rotate(-9deg)}
        50%{transform:translate3d(-14px,4px,0) rotate(-2deg)}
        75%{transform:translate3d(6px,-12px,0) rotate(7deg)}
        100%{transform:translate3d(0,0,0) rotate(-4deg)}
      }
      .featureCardLines span{
        position:absolute;
        left:12px;
        right:12px;
        height:1px;
        background:linear-gradient(90deg, transparent, rgba(107,123,255,.38), transparent);
        opacity:.44;
      }
      .featureCardLines span:nth-child(1){top:18px}
      .featureCardLines span:nth-child(2){top:45px}
      .featureCardLines span:nth-child(3){bottom:16px}
      .featureCardParticles span{
        position:absolute;
        width:4px;
        height:4px;
        border-radius:999px;
        background:rgba(137,236,255,.9);
        box-shadow:0 0 10px rgba(137,236,255,.55);
        opacity:0;
        transform:translate3d(0,0,0);
      }
      .featureCardParticles span:nth-child(1){top:18px;left:20px}
      .featureCardParticles span:nth-child(2){top:26px;right:28px}
      .featureCardParticles span:nth-child(3){top:52px;left:34px}
      .featureCardParticles span:nth-child(4){bottom:18px;right:26px}
      .featureCardParticles span:nth-child(5){bottom:22px;left:54px}
      .featureCard:hover .featureCardParticles span{
        opacity:1;
        animation:featureParticleFloat 1.8s ease-in-out infinite;
      }
      .featureCardParticles span:nth-child(2){animation-delay:.18s}
      .featureCardParticles span:nth-child(3){animation-delay:.34s}
      .featureCardParticles span:nth-child(4){animation-delay:.12s}
      .featureCardParticles span:nth-child(5){animation-delay:.42s}
      .featureCardCorners span{
        position:absolute;
        width:12px;
        height:12px;
        border-color:rgba(103,117,255,.58);
        border-style:solid;
        opacity:.74;
        transition:transform .22s ease, opacity .22s ease;
      }
      .featureCardCorners span:nth-child(1){top:10px;left:10px;border-width:2px 0 0 2px}
      .featureCardCorners span:nth-child(2){top:10px;right:10px;border-width:2px 2px 0 0}
      .featureCardCorners span:nth-child(3){bottom:10px;left:10px;border-width:0 0 2px 2px}
      .featureCardCorners span:nth-child(4){bottom:10px;right:10px;border-width:0 2px 2px 0}
      .featureCard:hover .featureCardCorners span{opacity:1}
      .featureCardContent{
        z-index:1;
        display:flex;
        flex-direction:column;
        align-items:center;
        justify-content:center;
        gap:8px;
        padding:14px;
        text-align:center;
      }
      .featureCardContent strong{
        display:block;
        color:#f8fafc;
        font-size:15px;
        line-height:1.25;
        text-shadow:0 0 12px rgba(255,255,255,.1);
        transition:transform .22s ease, color .22s ease;
      }
      .featureCardContent em{
        display:block;
        font-style:normal;
        color:#b7c0d4;
        font-size:12px;
        line-height:1.2;
        transition:transform .22s ease, color .22s ease;
      }
      .featureCard:hover .featureCardContent strong{
        transform:translateY(-4px);
        color:#ffffff;
      }
      .featureCard:hover .featureCardContent em{
        transform:translateY(2px);
        color:#dbe3f6;
      }
      .feature-cyan .featureCardGlow{
        background:
          radial-gradient(circle at var(--feature-glare-x, 50%) var(--feature-glare-y, 50%), rgba(0,255,213,.28), transparent 36%),
          radial-gradient(circle at 80% 18%, rgba(75,155,255,.2), transparent 28%);
      }
      .feature-violet .featureCardGlow{
        background:
          radial-gradient(circle at var(--feature-glare-x, 50%) var(--feature-glare-y, 50%), rgba(168,104,255,.26), transparent 36%),
          radial-gradient(circle at 18% 16%, rgba(104,172,255,.18), transparent 28%);
      }
      .feature-violet .featureCardCorners span,
      .feature-violet .featureCardLines span{border-color:rgba(165,112,255,.62);background:linear-gradient(90deg, transparent, rgba(165,112,255,.4), transparent)}
      .feature-violet .featureCardParticles span{
        background:rgba(198,154,255,.9);
        box-shadow:0 0 10px rgba(198,154,255,.52);
      }
      .feature-emerald .featureCardGlow{
        background:
          radial-gradient(circle at var(--feature-glare-x, 50%) var(--feature-glare-y, 50%), rgba(65,255,177,.26), transparent 36%),
          radial-gradient(circle at 82% 18%, rgba(71,208,164,.18), transparent 28%);
      }
      .feature-emerald .featureCardCorners span,
      .feature-emerald .featureCardLines span{border-color:rgba(97,224,179,.62);background:linear-gradient(90deg, transparent, rgba(97,224,179,.42), transparent)}
      .feature-emerald .featureCardParticles span{
        background:rgba(190,255,231,.92);
        box-shadow:0 0 10px rgba(96,219,176,.45);
      }
      .feature-amber .featureCardGlow{
        background:
          radial-gradient(circle at var(--feature-glare-x, 50%) var(--feature-glare-y, 50%), rgba(255,196,87,.26), transparent 36%),
          radial-gradient(circle at 82% 18%, rgba(255,134,74,.2), transparent 28%);
      }
      .feature-amber .featureCardCorners span,
      .feature-amber .featureCardLines span{border-color:rgba(255,193,92,.62);background:linear-gradient(90deg, transparent, rgba(255,193,92,.4), transparent)}
      .feature-amber .featureCardParticles span{
        background:rgba(255,218,152,.92);
        box-shadow:0 0 10px rgba(255,194,96,.5);
      }
      .feature-blue .featureCardGlow{
        background:
          radial-gradient(circle at var(--feature-glare-x, 50%) var(--feature-glare-y, 50%), rgba(83,167,255,.28), transparent 36%),
          radial-gradient(circle at 82% 18%, rgba(59,130,246,.22), transparent 28%);
      }
      .feature-blue .featureCardCorners span,
      .feature-blue .featureCardLines span{border-color:rgba(96,165,250,.64);background:linear-gradient(90deg, transparent, rgba(96,165,250,.44), transparent)}
      .feature-blue .featureCardParticles span{
        background:rgba(191,219,254,.94);
        box-shadow:0 0 10px rgba(96,165,250,.48);
      }
      .videoPage{
        display:flex;
        flex:1;
        flex-direction:column;
        min-height:0;
      }
      .subHead{display:flex;align-items:center;gap:8px;margin-bottom:10px}
      .subHead b{font-size:16px}
      .back{
        width:28px;
        height:28px;
        border:0;
        border-radius:7px;
        background:rgba(255,255,255,.35);
        font-size:22px;
        line-height:1;
      }
      .tools{display:flex;gap:8px;margin:8px 0;flex-wrap:wrap}
      .tools button{
        height:32px;
        padding:0 10px;
        border:1px solid rgba(0,0,0,.22);
        border-radius:7px;
        background:#f5f5f5;
        color:#111827;
        font-size:12px;
      }
      .tools button:disabled{opacity:.5;cursor:not-allowed}
      .tools .sourceBatchButton{
        border-color:rgba(64,158,255,.45);
        background:#eaf4ff;
        color:#1d4ed8;
        font-weight:600;
      }
      .tools .sourceBatchButton:disabled{
        border-color:rgba(0,0,0,.18);
        background:#f5f5f5;
        color:#6b7280;
        font-weight:400;
      }
      .tools .titleDownloadButton{
        border-color:rgba(22,163,74,.38);
        background:#effbf3;
        color:#15803d;
        font-weight:600;
      }
      .tools .titleDownloadButton:disabled{
        border-color:rgba(0,0,0,.18);
        background:#f5f5f5;
        color:#6b7280;
        font-weight:400;
      }
      .selectAll{
        display:inline-flex;
        align-items:center;
        gap:6px;
        height:32px;
        padding:0 10px;
        border:1px solid rgba(0,0,0,.18);
        border-radius:7px;
        background:rgba(255,255,255,.45);
        color:#111827;
        font-size:12px;
      }
      .selectAll input{
        width:14px;
        height:14px;
        margin:0;
      }
      .selectAll.disabled{
        opacity:.5;
        cursor:not-allowed;
      }
      .filterBar{display:flex;gap:6px;margin:2px 0 8px}
      .filter{
        height:28px;
        padding:0 10px;
        border:1px solid rgba(0,0,0,.16);
        border-radius:7px;
        background:rgba(255,255,255,.35);
        color:#111827;
        font-size:12px;
      }
      .filter.active{background:#111827;color:#fff;border-color:#111827}
      .count{margin:4px 0 8px;color:#1f2937;font-size:12px}
      .resourceList{
        display:grid;
        flex:1;
        min-height:0;
        align-content:start;
        gap:8px;
        overflow:auto;
        padding:0 2px 18px 0;
      }
      .resource{
        display:grid;
        grid-template-columns:18px 58px minmax(0,1fr) auto;
        gap:8px;
        align-items:start;
        padding:8px;
        border:1px solid rgba(0,0,0,.18);
        border-radius:7px;
        background:rgba(255,255,255,.45);
      }
      .resource.expanded{background:rgba(255,255,255,.56)}
      .resource input{margin-top:21px}
      .previewButton{
        width:58px;
        height:72px;
        padding:0;
        border:0;
        border-radius:6px;
        background:transparent;
        overflow:hidden;
      }
      .resourcePreview{
        width:58px;
        height:72px;
        border-radius:6px;
        background:#e5e7eb;
        object-fit:cover;
        overflow:hidden;
      }
      .resourcePreview.icon{
        display:flex;
        align-items:center;
        justify-content:center;
        color:#111827;
        font-weight:700;
        font-size:11px;
        text-transform:uppercase;
      }
      .resourceMeta{min-width:0}
      .resourceMeta[data-preview-id]{cursor:pointer}
      .resource b{
        display:block;
        max-width:210px;
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
        font-size:12px;
        color:#111827;
      }
      .resource small{
        display:block;
        max-width:210px;
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
        margin-top:3px;
        font-size:10px;
        color:#4b5563;
      }
      .resourceTags{display:flex;gap:5px;flex-wrap:wrap;margin-top:6px}
      .resourceInfo{display:flex;gap:6px;flex-wrap:wrap;margin-top:6px}
      .resourceInfo span{
        max-width:96px;
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
        font-size:10px;
        color:#1f2937;
        background:rgba(255,255,255,.78);
        border:1px solid rgba(0,0,0,.12);
        border-radius:6px;
        padding:2px 6px;
      }
      .resourceInfo .sourceLinkChip,
      .resourceInfo .titleCapturedChip{
        max-width:none;
        border-color:#86efac;
        background:#dcfce7;
        color:#166534;
        font-weight:700;
      }
      .resource em{
        font-style:normal;
        font-size:10px;
        color:#111827;
        background:#e5e7eb;
        border-radius:999px;
        padding:3px 6px;
      }
      .resourceActions{display:grid;gap:6px}
      .resourceActions button{
        min-width:44px;
        height:28px;
        padding:0 8px;
        border:1px solid rgba(0,0,0,.16);
        border-radius:7px;
        background:#f5f5f5;
        color:#111827;
        font-size:11px;
      }
      .resourcePlayer{
        grid-column:2 / -1;
        min-height:218px;
        border-radius:7px;
        background:rgba(17,24,39,.16);
        display:flex;
        align-items:center;
        justify-content:center;
        overflow:hidden;
      }
      .resourcePlayer video,.resourcePlayer img{
        width:100%;
        max-height:320px;
        object-fit:contain;
        background:#111827;
      }
      .resourcePlayer.icon{
        min-height:118px;
        color:#111827;
        font-weight:700;
        text-transform:uppercase;
      }
      .imagePage{
        display:flex;
        flex:1;
        flex-direction:column;
        min-height:0;
      }
      .imageFiltersPanel{
        display:grid;
        gap:10px;
        padding:10px 12px 12px;
        border:1px solid rgba(0,0,0,.12);
        border-radius:10px;
        background:rgba(255,255,255,.62);
        box-shadow:inset 0 1px 0 rgba(255,255,255,.72);
      }
      .imageRangeRow{
        display:grid;
        grid-template-columns:auto auto minmax(0,1fr) auto;
        align-items:center;
        gap:8px;
      }
      .imageRangeLabel,
      .imageFilterLabel,
      .imageActionMeta{
        color:#374151;
        font-size:12px;
        line-height:1.2;
      }
      .imageRangeValue,
      .imageRangeMax{
        min-width:42px;
        color:#111827;
        font-size:12px;
        line-height:1.2;
      }
      .imageRangeMax{text-align:right}
      .imageRangeInput{
        width:100%;
        accent-color:#6aa98a;
      }
      .imageRangeInput:disabled{opacity:.45}
      .imageFormatRow,
      .imageActionRow{
        display:flex;
        align-items:center;
        gap:8px;
        flex-wrap:wrap;
      }
      .imageActionRow{justify-content:flex-end}
      .imageActionMeta{
        margin-right:auto;
        font-weight:600;
      }
      .imageFormatChip{
        min-width:58px;
        height:28px;
        padding:0 10px;
        border:1px solid rgba(0,0,0,.12);
        border-radius:8px;
        background:#f7faf8;
        color:#3f4c46;
        font-size:12px;
      }
      .imageFormatChip.active{
        background:#6aa98a;
        color:#fff;
        border-color:#6aa98a;
        box-shadow:0 8px 18px rgba(106,169,138,.22);
      }
      .imageGrid{
        display:grid;
        grid-template-columns:repeat(2, minmax(0, 1fr));
        gap:10px;
        flex:1;
        min-height:0;
        align-content:start;
        overflow:auto;
        padding:12px 2px 12px 0;
      }
      .imageGrid .empty{grid-column:1 / -1}
      .imageCard{
        position:relative;
        display:flex;
        flex-direction:column;
        min-height:214px;
        border:1px solid rgba(0,0,0,.12);
        border-radius:10px;
        background:#f8fbf7;
        overflow:hidden;
        box-shadow:0 8px 20px rgba(15,23,42,.06);
      }
      .imageCardCheckbox{
        position:absolute;
        top:8px;
        right:8px;
        z-index:2;
        width:22px;
        height:22px;
        border-radius:999px;
        background:rgba(255,255,255,.92);
        box-shadow:0 4px 12px rgba(15,23,42,.12);
        cursor:pointer;
      }
      .imageCardCheckbox input{
        position:absolute;
        inset:0;
        opacity:0;
        margin:0;
        cursor:pointer;
      }
      .imageCardCheckbox span{
        position:absolute;
        inset:0;
        border:1px solid rgba(0,0,0,.14);
        border-radius:999px;
      }
      .imageCardCheckbox input:checked + span{
        background:#6aa98a;
        border-color:#6aa98a;
      }
      .imageCardCheckbox input:checked + span::after{
        content:"";
        position:absolute;
        left:7px;
        top:4px;
        width:5px;
        height:9px;
        border:solid #fff;
        border-width:0 2px 2px 0;
        transform:rotate(45deg);
      }
      .imageCardPreview{
        display:flex;
        align-items:center;
        justify-content:center;
        width:100%;
        min-height:154px;
        padding:12px;
        border:0;
        border-bottom:1px solid rgba(0,0,0,.08);
        background:#fff;
      }
      .imageCardPreview.is-error{background:linear-gradient(180deg, #ffffff 0%, #f3f4f6 100%)}
      .imageCardPreview.is-error::before{
        content:"图片预览失败";
        color:#9ca3af;
        font-size:12px;
      }
      .imageCardThumb{
        display:block;
        max-width:100%;
        max-height:130px;
        width:auto;
        height:auto;
        border-radius:8px;
        background:#fff;
        object-fit:contain;
      }
      .imageCardFooter{
        display:flex;
        align-items:center;
        justify-content:space-between;
        gap:8px;
        padding:10px;
        margin-top:auto;
      }
      .imageCardMeta{
        min-width:0;
        color:#3f4c46;
        font-size:12px;
        line-height:1.35;
      }
      .imageCardMeta span{
        display:block;
        overflow:hidden;
        text-overflow:ellipsis;
        white-space:nowrap;
      }
      .imageCardActions{
        display:flex;
        align-items:center;
        gap:6px;
        flex-shrink:0;
      }
      .iconActionButton{
        width:28px;
        height:28px;
        padding:0;
        border:1px solid rgba(0,0,0,.12);
        border-radius:8px;
        background:#fff;
        color:#5b7081;
        display:flex;
        align-items:center;
        justify-content:center;
      }
      .iconActionButton svg{
        width:15px;
        height:15px;
        display:block;
        fill:currentColor;
      }
      .iconActionButton:hover{
        color:#324450;
        border-color:rgba(0,0,0,.18);
      }
      .imageFooterBar{
        display:flex;
        justify-content:flex-start;
        padding-top:10px;
      }
      .imageFooterButton{
        height:34px;
        padding:0 14px;
        border:1px solid rgba(0,0,0,.14);
        border-radius:8px;
        background:#f7faf8;
        color:#324450;
        font-size:12px;
      }
      .imageFooterButton:disabled{
        opacity:.5;
        cursor:not-allowed;
      }
      .toggle{
        display:grid;
        grid-template-columns:1fr auto;
        gap:10px;
        align-items:center;
        margin-bottom:8px;
        padding:10px;
        border:1px solid rgba(0,0,0,.18);
        border-radius:7px;
        background:rgba(255,255,255,.42);
      }
      .toggle b{display:block;font-size:13px}
      .toggle small{display:block;margin-top:3px;font-size:11px;color:#374151}
      .toggle input{width:20px;height:20px}
      @keyframes featureParticleFloat{
        0%{transform:translate3d(0,0,0) scale(.7);opacity:0}
        35%{opacity:1}
        100%{transform:translate3d(8px,-10px,0) scale(1.18);opacity:0}
      }
      .empty{padding:18px;text-align:center;color:#374151;font-size:12px}
      .legalFloat{
        position:fixed;
        z-index:1170;
        left:50%;
        top:64px;
        transform:translateX(-50%);
        width:min(340px,calc(100vw - 28px));
        max-height:min(560px,calc(100vh - 28px));
        display:flex;
        flex-direction:column;
        overflow:hidden;
        border:1px solid rgba(15,23,42,.14);
        border-radius:14px;
        background:#ffffff;
        color:#172033;
        box-shadow:0 22px 54px rgba(15,23,42,.28);
      }
      .legalFloatHeader{
        display:grid;
        grid-template-columns:42px minmax(0,1fr) 30px;
        align-items:center;
        gap:10px;
        padding:14px 14px 10px;
        border-bottom:1px solid rgba(15,23,42,.08);
        background:#f8fafc;
      }
      .legalFloatIcon{
        width:42px;
        height:42px;
        border-radius:14px;
        display:flex;
        align-items:center;
        justify-content:center;
        background:#ffffff;
        box-shadow:0 10px 24px rgba(15,23,42,.14);
      }
      .legalFloatIcon img{
        width:32px;
        height:32px;
        object-fit:contain;
        display:block;
      }
      .legalFloatTitle{min-width:0}
      .legalFloatTitle small{
        display:block;
        margin-bottom:2px;
        color:#64748b;
        font-size:11px;
        line-height:1.2;
      }
      .legalFloatTitle strong{
        display:block;
        color:#111827;
        font-size:17px;
        line-height:1.2;
        font-weight:800;
        white-space:nowrap;
        overflow:hidden;
        text-overflow:ellipsis;
      }
      .legalFloatClose{
        width:30px;
        height:30px;
        border:0;
        border-radius:8px;
        background:transparent;
        color:#64748b;
        font-size:15px;
        line-height:1;
      }
      .legalFloatClose:hover{background:#e5e7eb;color:#111827}
      .legalFloatIntro{
        margin:0;
        padding:12px 16px 8px;
        color:#475569;
        font-size:12px;
        line-height:1.55;
      }
      .legalFloatBody{
        margin:0 14px;
        padding:12px 12px 10px;
        max-height:264px;
        overflow:auto;
        border-radius:8px;
        background:#f4f6fa;
        color:#243044;
        font-size:12px;
        line-height:1.65;
        outline:none;
      }
      .legalFloatBody:focus{box-shadow:0 0 0 2px rgba(37,99,235,.16)}
      .legalFloatBody h2{
        margin:12px 0 7px;
        color:#111827;
        font-size:14px;
        line-height:1.35;
      }
      .legalFloatBody h2:first-child{margin-top:0}
      .legalFloatBody p{margin:0 0 8px}
      .legalFloatBody ul{
        margin:0 0 8px;
        padding-left:18px;
      }
      .legalFloatBody li{margin:0 0 6px}
      .legalFloatError{
        margin:0;
        color:#b91c1c;
        font-weight:700;
      }
      .legalFloatCheck{
        display:grid;
        grid-template-columns:18px minmax(0,1fr);
        gap:8px;
        align-items:flex-start;
        padding:12px 16px 8px;
        color:#334155;
        font-size:12px;
        line-height:1.5;
      }
      .legalFloatCheck input{
        width:16px;
        height:16px;
        margin:2px 0 0;
        accent-color:#2563eb;
      }
      .legalFloatActions{
        display:flex;
        justify-content:space-between;
        gap:10px;
        padding:8px 16px 16px;
      }
      .legalFloatActions button{
        min-width:86px;
        height:34px;
        padding:0 12px;
        border-radius:8px;
        font-size:12px;
        font-weight:800;
      }
      .legalFloatSecondary{
        border:0;
        background:transparent;
        color:#475569;
      }
      .legalFloatSecondary:hover{background:#f1f5f9;color:#111827}
      .legalFloatPrimary{
        border:0;
        background:#2563eb;
        color:#ffffff;
        box-shadow:0 8px 18px rgba(37,99,235,.24);
      }
      .legalFloatPrimary:disabled{
        opacity:.42;
        cursor:not-allowed;
        box-shadow:none;
      }
      .sourceConfirmMask{
        position:fixed;
        inset:0;
        z-index:1170;
        display:flex;
        align-items:center;
        justify-content:center;
        padding:24px;
        background:rgba(15,23,42,.42);
        opacity:0;
        box-sizing:border-box;
        transition:opacity .16s ease;
      }
      .sourceConfirmMask.is-show{opacity:1}
      .sourceConfirmDialog{
        width:min(340px,calc(100vw - 48px));
        padding:18px 18px 16px;
        border-radius:8px;
        background:#fff;
        color:#1f2937;
        box-shadow:0 14px 36px rgba(15,23,42,.24);
        box-sizing:border-box;
        font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
      }
      .sourceConfirmMessage{
        color:#202124;
        font-size:15px;
        font-weight:600;
        line-height:1.55;
      }
      .sourceConfirmCheck{
        display:flex;
        align-items:center;
        gap:8px;
        width:max-content;
        max-width:100%;
        margin-top:14px;
        color:#606266;
        font-size:13px;
        line-height:1.4;
        cursor:pointer;
        user-select:none;
      }
      .sourceConfirmCheck input{
        width:15px;
        height:15px;
        margin:0;
        accent-color:#409EFF;
        cursor:pointer;
      }
      .sourceConfirmActions{
        display:flex;
        justify-content:flex-end;
        gap:10px;
        margin-top:18px;
      }
      .sourceConfirmActions button{
        min-width:76px;
        height:34px;
        border:1px solid #dcdfe6;
        border-radius:6px;
        background:#fff;
        color:#606266;
        font-size:14px;
        cursor:pointer;
        transition:background .16s,border-color .16s,color .16s;
      }
      .sourceConfirmActions button:hover{
        border-color:#409EFF;
        color:#409EFF;
      }
      .sourceConfirmActions button:disabled{
        cursor:wait;
        opacity:.7;
      }
      .sourceConfirmActions .sourceConfirmOk{
        border-color:#409EFF;
        background:#409EFF;
        color:#fff;
      }
      .sourceConfirmActions .sourceConfirmOk:hover{
        border-color:#66b1ff;
        background:#66b1ff;
        color:#fff;
      }
      .toast{
        position:fixed;
        z-index:1170;
        padding:9px 12px;
        border-radius:8px;
        color:#fff;
        font-size:12px;
        line-height:1.45;
        text-align:center;
        box-sizing:border-box;
        box-shadow:0 8px 22px rgba(0,0,0,.28);
        pointer-events:none;
      }
      .toast.info{
        background:#111827;
      }
      .toast.success{
        background:#16a34a;
      }
      .toast.error{
        background:#e53935;
      }
      @media (prefers-reduced-motion: reduce){
        .homeSceneStars,
        .homeSceneTrail,
        .homeSceneAstronaut,
        .homePagePanel.is-active,
        .featureCard:hover .featureCardParticles span{animation:none}
        .homePagerButton,
        .featureCardInner,
        .featureCardGlow,
        .featureCardCorners span,
        .featureCardContent strong,
        .featureCardContent em{transition:none}
        .featureCard:hover .featureCardContent strong,
        .featureCard:hover .featureCardContent em{transform:none}
      }
    `;
  }
})();
